initial commit phase .
This commit is contained in:
parent
50e2f6d92d
commit
a5f1d08a5e
6 changed files with 4517 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
/target
|
||||
*db*
|
||||
__pycache__/
|
||||
.o
|
||||
.so
|
||||
.exe
|
||||
.pyc
|
||||
.ts
|
||||
*_no.git_*
|
||||
venv/
|
||||
*venv*
|
3902
Cargo.lock
generated
Normal file
3902
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "evmSH"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.70"
|
||||
ethers = "2.0.3"
|
||||
revm = "3.1.1"
|
||||
tokio = "1.27.0"
|
471
bpnpcode.js
Executable file
471
bpnpcode.js
Executable file
|
@ -0,0 +1,471 @@
|
|||
#!/usr/bin/env node
|
||||
/* note: it can crash a net while running on kernel-space */
|
||||
/*jshint es5:false, asi:true, quotmark:false, eqeqeq:false, forin: false */
|
||||
|
||||
/*
|
||||
* (c) 2011-14 Tim Becker, see file LICENSE for details
|
||||
*/
|
||||
|
||||
/*
|
||||
* Provides functionality for bencoding and decoding as use in
|
||||
* bittorrent and described in: http://www.bittorrent.org/beps/bep_0003.html
|
||||
*
|
||||
* Encoding is as follows:
|
||||
*
|
||||
* var benc = require('bncode'),
|
||||
* exmp = {}
|
||||
*
|
||||
* exmp.bla = "blup"
|
||||
* exmp.foo = "bar"
|
||||
* exmp.one = 1
|
||||
* exmp.woah = {}
|
||||
* exmp.woah.arr = []
|
||||
* exmp.woah.arr.push(1)
|
||||
* exmp.woah.arr.push(2)
|
||||
* exmp.woah.arr.push(3)
|
||||
* exmp.str = new Buffer("Buffers work too")
|
||||
*
|
||||
* var bencBuffer = benc.encode(exmp) i
|
||||
*
|
||||
* // d3:bla4:blup3:foo3:bar3:onei1e4:woahd3:arr \
|
||||
* // li1ei2ei3eee3:str16:Buffers work tooe
|
||||
*
|
||||
*
|
||||
* Decoding will work in progressively, e.g. if you're receiving partial
|
||||
* bencoded strings on the network:
|
||||
*
|
||||
* var benc = require("bncode"),
|
||||
* buf = null
|
||||
*
|
||||
* decoder = new bncode.decoder()
|
||||
* while (buf = receiveData()) {
|
||||
* decoder.decode(buf)
|
||||
* }
|
||||
*
|
||||
* log(decoder.result())
|
||||
*
|
||||
*
|
||||
* Or "all in one"
|
||||
*
|
||||
* var benc = require("bncode"),
|
||||
* buf = getBuffer(),
|
||||
* dec = benc.decode(buf)
|
||||
*
|
||||
* log(dec.bla)
|
||||
*
|
||||
*
|
||||
* There are some subtleties concerning bencoded strings. These are
|
||||
* decoded as Buffer objects because they are just strings of raw bytes
|
||||
* and as such would wreak havoc with multi byte strings in javascript.
|
||||
*
|
||||
* The exception to this is strings that appear as keys in bencoded
|
||||
* dicts. These are decoded as Javascript Strings, as they should always
|
||||
* be strings of (ascii) characters and if they weren't decoded as JS
|
||||
* Strings, dict's would map to Javascript objects with properties.
|
||||
*
|
||||
*/
|
||||
|
||||
exports.encode = Bencode
|
||||
exports.decoder = Bdecode
|
||||
exports.decode = decode
|
||||
exports.Stream = Stream
|
||||
|
||||
var inherits = require('util').inherits
|
||||
var Transform = require('stream').Transform
|
||||
|
||||
var I = 'i'.charCodeAt(0)
|
||||
var L = 'l'.charCodeAt(0)
|
||||
var E = 'e'.charCodeAt(0)
|
||||
var D = 'd'.charCodeAt(0)
|
||||
var COLON = ':'.charCodeAt(0)
|
||||
var DASH = '-'.charCodeAt(0)
|
||||
|
||||
var STATE_INITIAL = 0
|
||||
var STATE_STATE_STRING_LEN = STATE_INITIAL + 1
|
||||
var STATE_STRING = STATE_STATE_STRING_LEN + 1
|
||||
var STATE_COLON = STATE_STRING + 1
|
||||
var STATE_STATE_INTEGER = STATE_COLON + 1
|
||||
var STATE_INTEGER = STATE_STATE_INTEGER + 1
|
||||
|
||||
/*
|
||||
* This is the internal state machine for taking apart bencoded strings,
|
||||
* it's not exposed in the eports. It's constructed with four callbacks
|
||||
* that get fired when:
|
||||
*
|
||||
* cb: a value (string or number) is encountered
|
||||
* cb_list: a begin list element is encountered
|
||||
* cb_dict: a beginning of dictionary is encountered.
|
||||
* cd_end: an end element, wheter dict or list is encountered
|
||||
*
|
||||
* Once constructed, the machine may be fed with buffers containing
|
||||
* partial bencoded string. Call `consistent` to check whether the
|
||||
* current state is consistent, e.g. not smack-dap in the middle of
|
||||
* a string or a number and if the dict, list and end calls balance
|
||||
*
|
||||
*
|
||||
* The functionality being so rudimentary requires some more state and
|
||||
* logic in the code executing the machine, for this see Context, below.
|
||||
*
|
||||
*/
|
||||
|
||||
function BdecodeSMachine (cb, cb_list, cb_dict, cb_end) {
|
||||
var depth = 0
|
||||
var state = STATE_INITIAL
|
||||
|
||||
this.consistent = function () {
|
||||
return state === STATE_INITIAL && depth === 0
|
||||
}
|
||||
|
||||
var strLen = 0
|
||||
var str = ''
|
||||
var _int = 0
|
||||
var neg = false
|
||||
|
||||
this.parse = function (buffer, encoding) {
|
||||
if (typeof buffer === 'string') {
|
||||
buffer = new Buffer(buffer, encoding || 'utf8')
|
||||
}
|
||||
|
||||
for (var pos = 0; pos !== buffer.length; ++pos) {
|
||||
switch (state) {
|
||||
case STATE_INITIAL:
|
||||
switch (buffer[pos]) {
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x32:
|
||||
case 0x33:
|
||||
case 0x34:
|
||||
case 0x35:
|
||||
case 0x36:
|
||||
case 0x37:
|
||||
case 0x38:
|
||||
case 0x39:
|
||||
state = STATE_STATE_STRING_LEN
|
||||
strLen = 0
|
||||
strLen += buffer[pos] - 0x30
|
||||
break
|
||||
case I:
|
||||
state = STATE_STATE_INTEGER
|
||||
_int = 0
|
||||
neg = false
|
||||
break
|
||||
case L:
|
||||
state = STATE_INITIAL
|
||||
depth += 1
|
||||
cb_list()
|
||||
break
|
||||
case D:
|
||||
state = STATE_INITIAL
|
||||
depth += 1
|
||||
cb_dict()
|
||||
break
|
||||
case E:
|
||||
state = STATE_INITIAL
|
||||
depth -= 1
|
||||
if (depth < 0) {
|
||||
throw new Error('end with no beginning: ' + pos)
|
||||
} else {
|
||||
cb_end()
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
case STATE_STATE_STRING_LEN:
|
||||
if (integer(buffer[pos])) {
|
||||
strLen *= 10
|
||||
strLen += buffer[pos] - 0x30
|
||||
} else {
|
||||
str = new Buffer(strLen)
|
||||
pos -=1
|
||||
state = STATE_COLON
|
||||
}
|
||||
break
|
||||
case STATE_COLON:
|
||||
if (buffer[pos] !== COLON) {
|
||||
throw new Error('not a colon at: ' + pos.toString(16))
|
||||
}
|
||||
state = STATE_STRING
|
||||
// in case this is a zero length string, there's
|
||||
// no bytes to be collected.
|
||||
if (0 === strLen) {
|
||||
cb(new Buffer(0))
|
||||
state = STATE_INITIAL
|
||||
}
|
||||
break
|
||||
case STATE_STRING:
|
||||
if (0 === strLen) {
|
||||
cb(str)
|
||||
state = STATE_INITIAL
|
||||
} else {
|
||||
//str += String.fromCharCode(buffer[pos]) // not unicode safe..
|
||||
str[str.length-strLen] = buffer[pos]
|
||||
strLen -= 1
|
||||
if (0 === strLen) {
|
||||
cb(str)
|
||||
state = STATE_INITIAL
|
||||
}
|
||||
}
|
||||
break
|
||||
case STATE_STATE_INTEGER:
|
||||
state = STATE_INTEGER
|
||||
if (buffer[pos] === DASH) {
|
||||
neg = true // handle neg and zero within value.
|
||||
break
|
||||
} // else fall through
|
||||
case STATE_INTEGER:
|
||||
if (integer(buffer[pos])) {
|
||||
_int *= 10
|
||||
_int += buffer[pos] - 0x30
|
||||
} else if (buffer[pos] === E) {
|
||||
var ret = neg ? 0 - _int : _int
|
||||
cb(ret)
|
||||
state = STATE_INITIAL
|
||||
} else {
|
||||
throw new Error('not part of int at:'+pos.toString(16))
|
||||
}
|
||||
break
|
||||
} // switch state
|
||||
} // for buffer
|
||||
} // function parse
|
||||
|
||||
function integer (value) {
|
||||
// check that value is a number and that
|
||||
// its value is ascii integer.
|
||||
if (typeof value !== 'number') {
|
||||
return false
|
||||
}
|
||||
return between(value, 0x30, 0x39)
|
||||
}
|
||||
function between (val, min, max) {
|
||||
return (min <= val && val <= max)
|
||||
}
|
||||
|
||||
} // end BdecodeSMachine
|
||||
|
||||
/*
|
||||
* The exported decode functionality.
|
||||
*/
|
||||
function Bdecode () {
|
||||
// markers
|
||||
var DICTIONARY_START = {}
|
||||
var LIST_START = {}
|
||||
|
||||
var Context = function () {
|
||||
var self = this
|
||||
var stack = []
|
||||
|
||||
this.cb = function (o) {
|
||||
stack.push(o)
|
||||
}
|
||||
this.cb_list = function () {
|
||||
self.cb(LIST_START)
|
||||
}
|
||||
this.cb_dict = function () {
|
||||
self.cb(DICTIONARY_START)
|
||||
}
|
||||
|
||||
this.cb_end = function () {
|
||||
|
||||
// unwind the stack until either a DICTIONARY_START or LIST_START is
|
||||
// found, create arr or hash, stick unwound stack on, push arr or hash
|
||||
// back onto stack
|
||||
|
||||
var obj = null
|
||||
var tmp_stack = []
|
||||
|
||||
while ((obj = stack.pop()) !== undefined) {
|
||||
if (LIST_START === obj) {
|
||||
var obj2 = null
|
||||
var list = []
|
||||
while((obj2 = tmp_stack.pop()) !== undefined) {
|
||||
list.push(obj2)
|
||||
}
|
||||
self.cb(list)
|
||||
break
|
||||
} else if (DICTIONARY_START === obj) {
|
||||
var key = null
|
||||
var val = null
|
||||
var dic = {}
|
||||
while ((key = tmp_stack.pop()) !== undefined && (val = tmp_stack.pop()) !== undefined) {
|
||||
dic[key.toString()] = val
|
||||
}
|
||||
|
||||
if (key !== undefined && dic[key] === undefined) {
|
||||
throw new Error('uneven number of keys and values A')
|
||||
}
|
||||
self.cb(dic)
|
||||
break
|
||||
} else {
|
||||
tmp_stack.push(obj)
|
||||
}
|
||||
}
|
||||
if (tmp_stack.length > 0) {
|
||||
// could this case even occur?
|
||||
throw new Error('uneven number of keys and values B')
|
||||
}
|
||||
}
|
||||
this.result = function () {
|
||||
return stack
|
||||
}
|
||||
}
|
||||
|
||||
var self = this
|
||||
var ctx = new Context()
|
||||
var smachine = new BdecodeSMachine(ctx.cb, ctx.cb_list, ctx.cb_dict, ctx.cb_end)
|
||||
|
||||
this.result = function () {
|
||||
if (!smachine.consistent()) {
|
||||
throw new Error('not in consistent state. More bytes coming?')
|
||||
}
|
||||
return ctx.result()
|
||||
}
|
||||
|
||||
this.decode = function (buf, encoding) {
|
||||
smachine.parse(buf, encoding)
|
||||
}
|
||||
}
|
||||
|
||||
function Bencode (obj) {
|
||||
var self = this
|
||||
var to_encode = obj
|
||||
var buffer = null
|
||||
|
||||
switch (typeof obj) {
|
||||
case 'string':
|
||||
return encodeString(obj)
|
||||
case 'number':
|
||||
return encodeNumber(obj)
|
||||
case 'object':
|
||||
if (obj instanceof Array) {
|
||||
return encodeList(obj)
|
||||
} else if (Buffer.isBuffer(obj)) {
|
||||
return encodeBuffer(obj)
|
||||
} else {
|
||||
// assume it's a hash
|
||||
return encodeDict(obj)
|
||||
}
|
||||
}
|
||||
|
||||
function encodeString (obj) {
|
||||
var blen = Buffer.byteLength(obj)
|
||||
var len = blen.toString(10)
|
||||
var buf = new Buffer(len.length + 1 + blen)
|
||||
|
||||
buf.write(len, 0, 'ascii')
|
||||
buf.write(':', len.length, 'ascii')
|
||||
buf.write(obj, len.length + 1, 'utf8')
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
function encodeNumber (num) {
|
||||
var n = num.toString(10)
|
||||
var buf = new Buffer(n.length + 2)
|
||||
|
||||
buf.write('i', 0)
|
||||
buf.write(n, 1)
|
||||
buf.write('e', n.length + 1)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
function encodeDict (obj) {
|
||||
var func = function (obj, pos) {
|
||||
var keys = Object.keys(obj).sort()
|
||||
keys.forEach(function (key) {
|
||||
var val = new Bencode(obj[key])
|
||||
key = new Bencode(key)
|
||||
|
||||
ensure(key.length + val.length, pos)
|
||||
key.copy(buffer, pos, 0)
|
||||
pos += key.length
|
||||
val.copy(buffer, pos, 0)
|
||||
pos += val.length
|
||||
})
|
||||
return pos
|
||||
}
|
||||
return assemble(obj, 'd', func)
|
||||
}
|
||||
|
||||
function encodeList (obj) {
|
||||
var func = function(obj, pos) {
|
||||
obj.forEach(function (o) {
|
||||
var elem = new Bencode(o)
|
||||
|
||||
ensure(elem.length, pos)
|
||||
elem.copy(buffer, pos, 0)
|
||||
pos += elem.length
|
||||
})
|
||||
return pos
|
||||
}
|
||||
return assemble(obj, 'l', func)
|
||||
}
|
||||
|
||||
function encodeBuffer (obj) {
|
||||
var len = obj.length.toString(10)
|
||||
var buf = new Buffer(len.length + 1 + obj.length)
|
||||
|
||||
buf.write(len, 0, 'ascii')
|
||||
buf.write(':', len.length, 'ascii')
|
||||
obj.copy(buf, len.length + 1, 0)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
function assemble (obj, prefix, func) {
|
||||
var pos = 0
|
||||
|
||||
ensure(1024, 0)
|
||||
buffer.write(prefix, pos++)
|
||||
|
||||
pos = func(obj, pos)
|
||||
ensure(1, pos)
|
||||
|
||||
buffer.write('e', pos++)
|
||||
return buffer.slice(0, pos)
|
||||
}
|
||||
|
||||
function ensure (num, pos) {
|
||||
if (!buffer) {
|
||||
buffer = new Buffer(num)
|
||||
} else {
|
||||
if (buffer.length > num + pos + 1) {
|
||||
return
|
||||
} else {
|
||||
var buf2 = new Buffer(buffer.length + num)
|
||||
buffer.copy(buf2, 0, 0)
|
||||
buffer = buf2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function decode (buffer, encoding) {
|
||||
var decoder = new Bdecode()
|
||||
|
||||
decoder.decode(buffer, encoding)
|
||||
return decoder.result()[0]
|
||||
}
|
||||
|
||||
function Stream (options) {
|
||||
options = options || {}
|
||||
options.objectMode = true
|
||||
Transform.call(this, options)
|
||||
this._decoder = new Bdecode()
|
||||
}
|
||||
|
||||
inherits(Stream, Transform)
|
||||
|
||||
Stream.prototype._transform = function (chunk, encoding, callback) {
|
||||
try {
|
||||
this._decoder.decode(chunk, encoding)
|
||||
callback(null)
|
||||
} catch(err) {
|
||||
callback(err)
|
||||
}
|
||||
}
|
||||
|
||||
Stream.prototype._flush = function (callback) {
|
||||
this.push(this._decoder.result()[0])
|
||||
callback(null)
|
||||
}
|
9
src/main.rs
Normal file
9
src/main.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use waddaFork::tait;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// create ethers client and wrap it in Arc<M>
|
||||
// let client = Arc::new(nick.local.CLIENT);
|
||||
await tait().?;
|
||||
println!("Hello, world!");
|
||||
}
|
112
src/waddaFork.rs
Normal file
112
src/waddaFork.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use std::{str::FromStr, sync::Arc};
|
||||
use anyhow::{Ok, Result};
|
||||
use bytes::Bytes;
|
||||
use ethers::{
|
||||
abi::parse_abi,
|
||||
prelude::BaseContract,
|
||||
providers::{Http, Provider},
|
||||
};
|
||||
use revm::{
|
||||
db::{CacheDB, EmptyDB, EthersDB},
|
||||
primitives::{ExecutionResult, Output, TransactTo, B160, U256 as rU256},
|
||||
Database, EVM,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn tait() -> Result<()> {
|
||||
// create ethers client and wrap it in Arc<M>
|
||||
let client = Provider::<Http>::try_from(
|
||||
"https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
|
||||
)?;
|
||||
let client = Arc::new(client);
|
||||
|
||||
// ----------------------------------------------------------- //
|
||||
// Storage slots of UniV2Pair contract //
|
||||
// =========================================================== //
|
||||
// storage[5] = factory: address //
|
||||
// storage[6] = token0: address //
|
||||
// storage[7] = token1: address //
|
||||
// storage[8] = (res0, res1, ts): (uint112, uint112, uint32) //
|
||||
// storage[9] = price0CumulativeLast: uint256 //
|
||||
// storage[10] = price1CumulativeLast: uint256 //
|
||||
// storage[11] = kLast: uint256 //
|
||||
// =========================================================== //
|
||||
|
||||
// choose slot of storage that you would like to transact with
|
||||
let slot = rU256::from(8);
|
||||
|
||||
// ETH/USDT pair on Uniswap V2
|
||||
let pool_address = B160::from_str("0x252ea274ff957494274057ed94844ffd7184e21f")?;
|
||||
|
||||
// generate abi for the calldata from the human readable interface
|
||||
let abi = BaseContract::from(
|
||||
parse_abi(&[
|
||||
"function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)",
|
||||
])?
|
||||
);
|
||||
|
||||
// encode abi into Bytes
|
||||
let encoded = abi.encode("getReserves", ())?;
|
||||
|
||||
// initialize new EthersDB
|
||||
let mut ethersdb = EthersDB::new(Arc::clone(&client), None).unwrap();
|
||||
|
||||
// query basic properties of an account incl bytecode
|
||||
let acc_info = ethersdb.basic(pool_address).unwrap().unwrap();
|
||||
|
||||
// query value of storage slot at account address
|
||||
let value = ethersdb.storage(pool_address, slot).unwrap();
|
||||
|
||||
// initialise empty in-memory-db
|
||||
let mut cache_db = CacheDB::new(EmptyDB::default());
|
||||
|
||||
// insert basic account info which was generated via Web3DB with the corresponding address
|
||||
cache_db.insert_account_info(pool_address, acc_info);
|
||||
|
||||
// insert our pre-loaded storage slot to the corresponding contract key (address) in the DB
|
||||
cache_db
|
||||
.insert_account_storage(pool_address, slot, value)
|
||||
.unwrap();
|
||||
|
||||
// initialise an empty (default) EVM
|
||||
let mut evm = EVM::new();
|
||||
|
||||
// insert pre-built database from above
|
||||
evm.database(cache_db);
|
||||
|
||||
// fill in missing bits of env struc
|
||||
// change that to whatever caller you want to be
|
||||
evm.env.tx.caller = B160::from_str("0x0000000000000000000000000000000000000000")?;
|
||||
// account you want to transact with
|
||||
evm.env.tx.transact_to = TransactTo::Call(pool_address);
|
||||
// calldata formed via abigen
|
||||
evm.env.tx.data = Bytes::from(hex::decode(hex::encode(&encoded))?);
|
||||
// transaction value in wei
|
||||
evm.env.tx.value = rU256::try_from(0)?;
|
||||
|
||||
// execute transaction without writing to the DB
|
||||
let ref_tx = evm.transact_ref().unwrap();
|
||||
// select ExecutionResult struct
|
||||
let result = ref_tx.result;
|
||||
|
||||
// unpack output call enum into raw bytes
|
||||
let value = match result {
|
||||
ExecutionResult::Success { output, .. } => match output {
|
||||
Output::Call(value) => Some(value),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// decode bytes to reserves + ts via ethers-rs's abi decode
|
||||
let (reserve0, reserve1, ts): (u128, u128, u32) =
|
||||
abi.decode_output("getReserves", value.unwrap())?;
|
||||
|
||||
// Print emulated getReserves() call output
|
||||
println!("Reserve0: {:#?}", reserve0);
|
||||
println!("Reserve1: {:#?}", reserve1);
|
||||
println!("Timestamp: {:#?}", ts);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in a new issue