diff --git a/Cargo.lock b/Cargo.lock index 5db527ba2f..32520521e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2089,9 +2089,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "powerfmt" diff --git a/Cargo.toml b/Cargo.toml index 4f7b81ca89..426d6e403f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,13 @@ [workspace] +members = [ + "bins/revme", + "bins/revm-test", + "crates/revm", + "crates/primitives", + "crates/interpreter", + "crates/precompile", +] resolver = "2" -members = ["bins/*", "crates/*"] default-members = ["crates/revm"] [workspace.metadata.docs.rs] diff --git a/bins/revm-test/src/bin/analysis.rs b/bins/revm-test/src/bin/analysis.rs index a7a81af9ce..b0f2b6404e 100644 --- a/bins/revm-test/src/bin/analysis.rs +++ b/bins/revm-test/src/bin/analysis.rs @@ -3,33 +3,29 @@ use std::time::Instant; use revm::{ db::BenchmarkDB, interpreter::analysis::to_analysed, - primitives::{Bytecode, Bytes, TransactTo}, + primitives::{address, bytes, Bytecode, Bytes, TransactTo}, + Evm, }; extern crate alloc; fn main() { let contract_data : Bytes = hex::decode( "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029" ).unwrap().into(); - // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - evm.env.tx.data = Bytes::from(hex::decode("8035F0CE").unwrap()); - let bytecode_raw = Bytecode::new_raw(contract_data.clone()); let bytecode_checked = Bytecode::new_raw(contract_data.clone()).to_checked(); let bytecode_analysed = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(bytecode_raw)); + // BenchmarkDB is dummy state that implements Database trait. + let mut evm = Evm::builder() + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); + tx.data = bytes!("8035F0CE"); + }) + .with_db(BenchmarkDB::new_bytecode(bytecode_raw)) + .build(); // just to spead up processor. for _ in 0..10000 { @@ -42,7 +38,10 @@ fn main() { } println!("Raw elapsed time: {:?}", timer.elapsed()); - evm.database(BenchmarkDB::new_bytecode(bytecode_checked)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(bytecode_checked)) + .build(); let timer = Instant::now(); for _ in 0..30000 { @@ -50,7 +49,10 @@ fn main() { } println!("Checked elapsed time: {:?}", timer.elapsed()); - evm.database(BenchmarkDB::new_bytecode(bytecode_analysed)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(bytecode_analysed)) + .build(); let timer = Instant::now(); for _ in 0..30000 { diff --git a/bins/revm-test/src/bin/snailtracer.rs b/bins/revm-test/src/bin/snailtracer.rs index 19ed7df595..94f3a6c9db 100644 --- a/bins/revm-test/src/bin/snailtracer.rs +++ b/bins/revm-test/src/bin/snailtracer.rs @@ -1,55 +1,36 @@ +use std::time::Duration; + use revm::{ db::BenchmarkDB, interpreter::analysis::to_analysed, - primitives::{Bytecode, Bytes, TransactTo}, + primitives::{address, bytes, Bytecode, Bytes, TransactTo}, + Evm, }; -extern crate alloc; pub fn simple_example() { - let contract_data : Bytes = hex::decode("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033").unwrap().into(); + let bytecode = to_analysed(Bytecode::new_raw(CONTRACT_DATA.clone())); // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - let bytecode = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.data = bytes!("30627b7c"); + }) + .build(); // Microbenchmark - //let bench_options = microbench::Options::default().time(Duration::from_secs(2)); + let bench_options = microbench::Options::default().time(Duration::from_secs(2)); - //let env = evm.env.clone(); - //microbench::bench( - // &bench_options, - // "Snailtracer Host+Interpreter benchmark", - // || { - let _ = evm.transact().unwrap(); - // }, - //); - /* - // revm interpreter - let contract = Contract { - input: evm.env.tx.data, - bytecode: BytecodeLocked::try_from(bytecode).unwrap(), - ..Default::default() - }; - - let mut host = DummyHost::new(env); - microbench::bench(&bench_options, "Snailtracer Interpreter benchmark", || { - let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); - interpreter.run::<_, BerlinSpec>(&mut host); - host.clear() - }); - */ + microbench::bench( + &bench_options, + "Snailtracer Host+Interpreter benchmark", + || { + let _ = evm.transact(); + }, + ); } fn main() { @@ -57,3 +38,5 @@ fn main() { simple_example(); //println!("end!"); } + +static CONTRACT_DATA : Bytes = bytes!("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"); diff --git a/bins/revm-test/src/bin/transfer.rs b/bins/revm-test/src/bin/transfer.rs index ff74d442c4..2af94b040d 100644 --- a/bins/revm-test/src/bin/transfer.rs +++ b/bins/revm-test/src/bin/transfer.rs @@ -1,39 +1,33 @@ use revm::{ db::BenchmarkDB, primitives::{Bytecode, TransactTo, U256}, + Evm, }; -use std::time::{Duration, Instant}; +use std::time::Duration; extern crate alloc; fn main() { // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x0000000000000000000000000000000000000001" - .parse() - .unwrap(); - evm.env.tx.value = U256::from(10); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - - evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = "0x0000000000000000000000000000000000000001" + .parse() + .unwrap(); + tx.value = U256::from(10); + tx.transact_to = TransactTo::Call( + "0x0000000000000000000000000000000000000000" + .parse() + .unwrap(), + ); + }) + .build(); // Microbenchmark - let bench_options = microbench::Options::default().time(Duration::from_secs(1)); + let bench_options = microbench::Options::default().time(Duration::from_secs(3)); microbench::bench(&bench_options, "Simple value transfer", || { let _ = evm.transact().unwrap(); }); - - let time = Instant::now(); - for _ in 0..10000 { - let _ = evm.transact().unwrap(); - } - let elapsed = time.elapsed(); - println!("10k runs in {:?}", elapsed.as_nanos() / 10_000); } diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 3a490174c4..97c424dfb8 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -1,17 +1,21 @@ use super::{ merkle_trie::{log_rlp_hash, state_merkle_trie_root}, - models::{SpecName, TestSuite}, + models::{SpecName, Test, TestSuite}, }; use indicatif::ProgressBar; use revm::{ + db::EmptyDB, + inspector_handle_register, inspectors::TracerEip3155, interpreter::CreateScheme, primitives::{ - address, b256, calc_excess_blob_gas, keccak256, Bytecode, Env, HashMap, SpecId, TransactTo, - B256, U256, + address, b256, calc_excess_blob_gas, keccak256, Bytecode, EVMResultGeneric, Env, + ExecutionResult, HashMap, SpecId, TransactTo, B256, U256, }, + Evm, State, }; use std::{ + convert::Infallible, io::stdout, path::{Path, PathBuf}, sync::atomic::Ordering, @@ -96,6 +100,65 @@ fn skip_test(path: &Path) -> bool { ) || path_str.contains("stEOF") } +fn check_evm_execution( + test: &Test, + test_name: &str, + exec_result: &EVMResultGeneric, + evm: &Evm<'_, EXT, &mut State>, +) -> Result<(), TestError> { + // if we expect exception revm should return error from execution. + // So we do not check logs and state root. + // + // Note that some tests that have exception and run tests from before state clear + // would touch the caller account and make it appear in state root calculation. + // This is not something that we would expect as invalid tx should not touch state. + // but as this is a cleanup of invalid tx it is not properly defined and in the end + // it does not matter. + // Test where this happens: `tests/GeneralStateTests/stTransactionTest/NoSrcAccountCreate.json` + // and you can check that we have only two "hash" values for before and after state clear. + match (&test.expect_exception, exec_result) { + // do nothing + (None, Ok(_)) => (), + // return okay, exception is expected. + (Some(_), Err(_)) => return Ok(()), + _ => { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::UnexpectedException { + expected_exception: test.expect_exception.clone(), + got_exception: exec_result.clone().err().map(|e| e.to_string()), + }, + }); + } + } + + let logs_root = log_rlp_hash(&exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); + + if logs_root != test.logs { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::LogsRootMismatch { + got: logs_root, + expected: test.logs, + }, + }); + } + + let state_root = state_merkle_trie_root(evm.context.evm.db.cache.trie_account()); + + if state_root != test.hash { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::StateRootMismatch { + got: state_root, + expected: test.hash, + }, + }); + } + + Ok(()) +} + pub fn execute_test_suite( path: &Path, elapsed: &Arc>, @@ -205,7 +268,7 @@ pub fn execute_test_suite( continue; } - env.cfg.spec_id = spec_name.to_spec_id(); + let spec_id = spec_name.to_spec_id(); for (index, test) in tests.into_iter().enumerate() { env.tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); @@ -244,86 +307,48 @@ pub fn execute_test_suite( let mut cache = cache_state.clone(); cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, + spec_id, revm::primitives::SpecId::SPURIOUS_DRAGON, )); let mut state = revm::db::State::builder() .with_cached_prestate(cache) .with_bundle_update() .build(); - let mut evm = revm::new(); - evm.database(&mut state); - evm.env = env.clone(); + let mut evm = Evm::builder() + .with_db(&mut state) + .modify_env(|e| *e = env.clone()) + .spec_id(spec_id) + .build(); // do the deed let timer = Instant::now(); - let exec_result = if trace { - evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) + let (e, exec_result) = if trace { + let mut evm = evm + .modify() + .reset_handler_with_external_context(TracerEip3155::new( + Box::new(stdout()), + false, + true, + )) + .append_handler_register(inspector_handle_register) + .build(); + let res = evm.transact_commit(); + + let Err(e) = check_evm_execution(&test, &name, &res, &evm) else { + continue; + }; + // reset external context + (e, res) } else { - evm.transact_commit() - }; - *elapsed.lock().unwrap() += timer.elapsed(); + let res = evm.transact_commit(); - // validate results - // this is in a closure so we can have a common printing routine for errors - let check = || { - // if we expect exception revm should return error from execution. - // So we do not check logs and state root. - // - // Note that some tests that have exception and run tests from before state clear - // would touch the caller account and make it appear in state root calculation. - // This is not something that we would expect as invalid tx should not touch state. - // but as this is a cleanup of invalid tx it is not properly defined and in the end - // it does not matter. - // Test where this happens: `tests/GeneralStateTests/stTransactionTest/NoSrcAccountCreate.json` - // and you can check that we have only two "hash" values for before and after state clear. - match (&test.expect_exception, &exec_result) { - // do nothing - (None, Ok(_)) => (), - // return okay, exception is expected. - (Some(_), Err(_)) => return Ok(()), - _ => { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::UnexpectedException { - expected_exception: test.expect_exception.clone(), - got_exception: exec_result.clone().err().map(|e| e.to_string()), - }, - }); - } - } - - let logs_root = - log_rlp_hash(&exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); - - if logs_root != test.logs { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::LogsRootMismatch { - got: logs_root, - expected: test.logs, - }, - }); - } - - let db = evm.db.as_ref().unwrap(); - let state_root = state_merkle_trie_root(db.cache.trie_account()); - - if state_root != test.hash { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::StateRootMismatch { - got: state_root, - expected: test.hash, - }, - }); - } - - Ok(()) + // dump state and traces if test failed + let Err(e) = check_evm_execution(&test, &name, &res, &evm) else { + continue; + }; + (e, res) }; - - // dump state and traces if test failed - let Err(e) = check() else { continue }; + *elapsed.lock().unwrap() += timer.elapsed(); // print only once static FAILED: AtomicBool = AtomicBool::new(false); @@ -334,22 +359,29 @@ pub fn execute_test_suite( // re build to run with tracing let mut cache = cache_state.clone(); cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, + spec_id, revm::primitives::SpecId::SPURIOUS_DRAGON, )); - let mut state = revm::db::StateBuilder::default() + let state = revm::db::State::builder() .with_cached_prestate(cache) + .with_bundle_update() .build(); - evm.database(&mut state); let path = path.display(); println!("\nTraces:"); - let _ = evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); + let mut evm = Evm::builder() + .spec_id(spec_id) + .with_db(state) + .with_external_context(TracerEip3155::new(Box::new(stdout()), false, false)) + .append_handler_register(inspector_handle_register) + .build(); + let _ = evm.transact_commit(); println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); - println!("\nState after: {:#?}", evm.db().unwrap().cache); + println!("\nState after: {:#?}", evm.context.evm.db.cache); + println!("\nSpecification: {spec_id:?}"); println!("\nEnvironment: {env:#?}"); println!("\nTest name: {name:?} (index: {index}, path: {path}) failed:\n{e}"); diff --git a/book.toml b/book.toml index ffe9273021..8395e94b05 100644 --- a/book.toml +++ b/book.toml @@ -1,5 +1,5 @@ [book] -authors = ["Colin Roberts, Waylon Jepsen"] +authors = ["Colin Roberts, Waylon Jepsen", "Dragan Rakita"] language = "en" multilingual = false src = "documentation/src" diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 1042262a97..e700c5501b 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -5,6 +5,7 @@ mod constants; pub use calc::*; pub use constants::*; +use revm_primitives::{Spec, SpecId::LONDON}; /// Represents the state of gas during execution. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -80,6 +81,16 @@ impl Gas { self.refunded += refund; } + /// Set a refund value for final refund. + /// + /// Max refund value is limited to Nth part (depending of fork) of gas spend. + /// + /// Related to EIP-3529: Reduction in refunds + pub fn set_final_refund(&mut self) { + let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; + self.refunded = (self.refunded() as u64).min(self.spend() / max_refund_quotient) as i64; + } + /// Set a refund value pub fn set_refund(&mut self, refund: i64) { self.refunded = refund; diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index e5b627b7e7..1200bf9709 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -333,7 +333,7 @@ pub fn memory_gas(a: usize) -> u64 { /// Initial gas that is deducted for transaction to be included. /// Initial gas contains initial stipend gas, gas for access list and input data. -pub fn initial_tx_gas( +pub fn validate_initial_tx_gas( input: &[u8], is_create: bool, access_list: &[(Address, Vec)], diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index 29795e3189..1368634bf2 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,3 +1,5 @@ +use revm_primitives::{TransactTo, TxEnv}; + pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, U256}; @@ -35,7 +37,47 @@ pub struct CreateInputs { pub gas_limit: u64, } +impl CallInputs { + pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { + let TransactTo::Call(address) = tx_env.transact_to else { + return None; + }; + + Some(CallInputs { + contract: address, + transfer: Transfer { + source: tx_env.caller, + target: address, + value: tx_env.value, + }, + input: tx_env.data.clone(), + gas_limit, + context: CallContext { + caller: tx_env.caller, + address, + code_address: address, + apparent_value: tx_env.value, + scheme: CallScheme::Call, + }, + is_static: false, + }) + } +} + impl CreateInputs { + pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { + let TransactTo::Create(scheme) = tx_env.transact_to else { + return None; + }; + + Some(CreateInputs { + caller: tx_env.caller, + scheme, + value: tx_env.value, + init_code: tx_env.data.clone(), + gas_limit, + }) + } /// Returns the address that this create call will create. pub fn created_address(&self, nonce: u64) -> Address { match self.scheme { diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index cc129bfcb0..f6265d122b 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -7,7 +7,6 @@ use crate::{ Host, Interpreter, }; use alloc::boxed::Box; -use alloc::sync::Arc; use core::fmt; /// EVM opcode function signature. @@ -17,36 +16,21 @@ pub type Instruction = fn(&mut Interpreter, &mut H); /// 256 EVM opcodes. pub type InstructionTable = [Instruction; 256]; -/// Arc over plain instruction table -pub type InstructionTableArc = Arc>; - /// EVM opcode function signature. pub type BoxedInstruction<'a, H> = Box; /// A table of instructions. pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256]; -/// Arc over instruction table -pub type BoxedInstructionTableArc<'a, H> = Arc>; - /// Instruction set that contains plain instruction table that contains simple `fn` function pointer. /// and Boxed `Fn` variant that contains `Box` function pointer that can be used with closured. /// /// Note that `Plain` variant gives us 10-20% faster Interpreter execution. /// /// Boxed variant can be used to wrap plain function pointer with closure. -pub enum InstructionTables<'a, H> { - Plain(InstructionTableArc), - Boxed(BoxedInstructionTableArc<'a, H>), -} - -impl<'a, H> Clone for InstructionTables<'a, H> { - fn clone(&self) -> Self { - match self { - Self::Plain(table) => Self::Plain(table.clone()), - Self::Boxed(table) => Self::Boxed(table.clone()), - } - } +pub enum InstructionTables<'a, H: Host> { + Plain(InstructionTable), + Boxed(BoxedInstructionTable<'a, H>), } macro_rules! opcodes { @@ -95,7 +79,7 @@ pub fn make_boxed_instruction_table<'a, H, SPEC, FN>( outer: FN, ) -> BoxedInstructionTable<'a, H> where - H: Host + 'a, + H: Host, SPEC: Spec + 'static, FN: Fn(Instruction) -> BoxedInstruction<'a, H>, { diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 743e161e3b..1caed8a239 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -95,8 +95,7 @@ impl Interpreter { /// When sub create call returns we can insert output of that call into this interpreter. pub fn insert_create_output(&mut self, result: InterpreterResult, address: Option
) { - let interpreter = self; - interpreter.return_data_buffer = match result.result { + self.return_data_buffer = match result.result { // Save data to return data buffer if the create reverted return_revert!() => result.output, // Otherwise clear it @@ -105,19 +104,19 @@ impl Interpreter { match result.result { return_ok!() => { - push_b256!(interpreter, address.unwrap_or_default().into_word()); - interpreter.gas.erase_cost(result.gas.remaining()); - interpreter.gas.record_refund(result.gas.refunded()); + push_b256!(self, address.unwrap_or_default().into_word()); + self.gas.erase_cost(result.gas.remaining()); + self.gas.record_refund(result.gas.refunded()); } return_revert!() => { - push!(interpreter, U256::ZERO); - interpreter.gas.erase_cost(result.gas.remaining()); + push!(self, U256::ZERO); + self.gas.erase_cost(result.gas.remaining()); } InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; + self.instruction_result = InstructionResult::FatalExternalError; } _ => { - push!(interpreter, U256::ZERO); + push!(self, U256::ZERO); } } } @@ -135,28 +134,27 @@ impl Interpreter { let out_offset = memory_return_offset.start; let out_len = memory_return_offset.len(); - let interpreter = self; - interpreter.return_data_buffer = result.output; - let target_len = min(out_len, interpreter.return_data_buffer.len()); + self.return_data_buffer = result.output; + let target_len = min(out_len, self.return_data_buffer.len()); match result.result { return_ok!() => { // return unspend gas. - interpreter.gas.erase_cost(result.gas.remaining()); - interpreter.gas.record_refund(result.gas.refunded()); - shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::from(1)); + self.gas.erase_cost(result.gas.remaining()); + self.gas.record_refund(result.gas.refunded()); + shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); + push!(self, U256::from(1)); } return_revert!() => { - interpreter.gas.erase_cost(result.gas.remaining()); - shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::ZERO); + self.gas.erase_cost(result.gas.remaining()); + shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); + push!(self, U256::ZERO); } InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; + self.instruction_result = InstructionResult::FatalExternalError; } _ => { - push!(interpreter, U256::ZERO); + push!(self, U256::ZERO); } } } diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index ea20a8185b..aa99a81776 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -20,7 +20,7 @@ mod modexp; mod secp256k1; pub mod utilities; -use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; use core::{fmt, hash::Hash}; use once_cell::race::OnceBox; #[doc(hidden)] @@ -57,21 +57,22 @@ impl PrecompileOutput { } } } -#[derive(Clone, Debug)] +#[derive(Clone, Default, Debug)] pub struct Precompiles { - pub inner: Vec, + /// Precompiles. + pub inner: HashMap, } impl Precompiles { /// Returns the precompiles for the given spec. - pub fn new(spec: SpecId) -> &'static Self { + pub fn new(spec: PrecompileSpecId) -> &'static Self { match spec { - SpecId::HOMESTEAD => Self::homestead(), - SpecId::BYZANTIUM => Self::byzantium(), - SpecId::ISTANBUL => Self::istanbul(), - SpecId::BERLIN => Self::berlin(), - SpecId::CANCUN => Self::cancun(), - SpecId::LATEST => Self::latest(), + PrecompileSpecId::HOMESTEAD => Self::homestead(), + PrecompileSpecId::BYZANTIUM => Self::byzantium(), + PrecompileSpecId::ISTANBUL => Self::istanbul(), + PrecompileSpecId::BERLIN => Self::berlin(), + PrecompileSpecId::CANCUN => Self::cancun(), + PrecompileSpecId::LATEST => Self::latest(), } } @@ -79,14 +80,14 @@ impl Precompiles { pub fn homestead() -> &'static Self { static INSTANCE: OnceBox = OnceBox::new(); INSTANCE.get_or_init(|| { - let mut inner = vec![ + let mut precompiles = Precompiles::default(); + precompiles.extend([ secp256k1::ECRECOVER, hash::SHA256, hash::RIPEMD160, identity::FUN, - ]; - inner.sort_unstable_by_key(|i| i.0); - Box::new(Self { inner }) + ]); + Box::new(precompiles) }) } @@ -169,13 +170,13 @@ impl Precompiles { /// Returns an iterator over the precompiles addresses. #[inline] pub fn addresses(&self) -> impl Iterator + '_ { - self.inner.iter().map(|i| &i.0) + self.inner.keys() } /// Consumes the type and returns all precompile addresses. #[inline] pub fn into_addresses(self) -> impl Iterator { - self.inner.into_iter().map(|precompile| precompile.0) + self.inner.into_keys() } /// Is the given address a precompile. @@ -188,10 +189,7 @@ impl Precompiles { #[inline] pub fn get(&self, address: &Address) -> Option { //return None; - self.inner - .binary_search_by_key(address, |i| i.0) - .ok() - .map(|i| self.inner[i].1.clone()) + self.inner.get(address).cloned() } /// Is the precompiles list empty. @@ -208,22 +206,7 @@ impl Precompiles { /// /// Other precompiles with overwrite existing precompiles. pub fn extend(&mut self, other: impl IntoIterator) { - self.inner = self - .inner - .iter() - .cloned() - .chain(other) - .map(|i| (i.0, i.1.clone())) - .collect::>() - .into_iter() - .map(|(k, v)| PrecompileWithAddress(k, v)) - .collect::>(); - } -} - -impl Default for Precompiles { - fn default() -> Self { - Self::new(SpecId::LATEST).clone() //berlin + self.inner.extend(other.into_iter().map(Into::into)); } } @@ -258,7 +241,7 @@ impl From for (Address, Precompile) { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum SpecId { +pub enum PrecompileSpecId { HOMESTEAD, BYZANTIUM, ISTANBUL, @@ -267,7 +250,7 @@ pub enum SpecId { LATEST, } -impl SpecId { +impl PrecompileSpecId { /// Returns the appropriate precompile Spec for the primitive [SpecId](revm_primitives::SpecId) pub const fn from_spec_id(spec_id: revm_primitives::SpecId) -> Self { use revm_primitives::SpecId::*; diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index de816f2cea..054175cc97 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -19,6 +19,12 @@ pub struct Env { } impl Env { + /// Resets environment to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } + /// Calculates the effective gas price of the transaction. #[inline] pub fn effective_gas_price(&self) -> U256 { @@ -174,7 +180,7 @@ impl Env { /// Validate transaction against state. #[inline] - pub fn validate_tx_against_state( + pub fn validate_tx_against_state( &self, account: &mut Account, ) -> Result<(), InvalidTransaction> { @@ -211,7 +217,7 @@ impl Env { .and_then(|gas_cost| gas_cost.checked_add(self.tx.value)) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - if SpecId::enabled(self.cfg.spec_id, SpecId::CANCUN) { + if SPEC::enabled(SpecId::CANCUN) { let data_fee = self.calc_data_fee().expect("already checked"); balance_check = balance_check .checked_add(U256::from(data_fee)) @@ -241,8 +247,9 @@ impl Env { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[non_exhaustive] pub struct CfgEnv { + /// Chain ID of the EVM, it will be compared to the transaction's Chain ID. + /// Chain ID is introduced EIP-155 pub chain_id: u64, - pub spec_id: SpecId, /// KZG Settings for point evaluation precompile. By default, this is loaded from the ethereum mainnet trusted setup. #[cfg(feature = "c-kzg")] #[cfg_attr(feature = "serde", serde(skip))] @@ -375,7 +382,6 @@ impl Default for CfgEnv { fn default() -> Self { Self { chain_id: 1, - spec_id: SpecId::LATEST, perf_analyse_created_bytecodes: AnalysisKind::default(), limit_contract_code_size: None, #[cfg(feature = "c-kzg")] @@ -470,6 +476,12 @@ impl BlockEnv { .as_ref() .map(|a| a.excess_blob_gas) } + + /// Clears environment and resets fields to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } } impl Default for BlockEnv { @@ -555,6 +567,12 @@ impl TxEnv { pub fn get_total_blob_gas(&self) -> u64 { GAS_PER_BLOB * self.blob_hashes.len() as u64 } + + /// Clears environment and resets fields to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } } impl Default for TxEnv { @@ -740,13 +758,15 @@ mod tests { #[test] fn test_validate_tx_against_state_deposit_tx() { // Set the optimism flag and source hash. + + use crate::LatestSpec; let mut env = Env::default(); env.cfg.optimism = true; env.tx.optimism.source_hash = Some(B256::ZERO); // Nonce and balance checks should be skipped for deposit transactions. assert!(env - .validate_tx_against_state(&mut Account::default()) + .validate_tx_against_state::(&mut Account::default()) .is_ok()); } diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 97f2b5f94e..e7434be7d7 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -69,6 +69,10 @@ impl SpecId { Self::n(spec_id) } + pub fn is_enabled_in(&self, other: Self) -> bool { + Self::enabled(*self, other) + } + #[inline] pub const fn enabled(our: SpecId, other: SpecId) -> bool { our as u8 >= other as u8 @@ -154,11 +158,125 @@ spec!(REGOLITH, RegolithSpec); #[cfg(feature = "optimism")] spec!(CANYON, CanyonSpec); -#[cfg(feature = "optimism")] +#[macro_export] +macro_rules! spec_to_generic { + ($spec_id:expr, $e:expr) => {{ + // We are transitioning from var to generic spec. + match $spec_id { + $crate::SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { + use $crate::FrontierSpec as SPEC; + $e + } + $crate::SpecId::HOMESTEAD | SpecId::DAO_FORK => { + use $crate::HomesteadSpec as SPEC; + $e + } + $crate::SpecId::TANGERINE => { + use $crate::TangerineSpec as SPEC; + $e + } + $crate::SpecId::SPURIOUS_DRAGON => { + use $crate::SpuriousDragonSpec as SPEC; + $e + } + $crate::SpecId::BYZANTIUM => { + use $crate::ByzantiumSpec as SPEC; + $e + } + $crate::SpecId::PETERSBURG | $crate::SpecId::CONSTANTINOPLE => { + use $crate::PetersburgSpec as SPEC; + $e + } + $crate::SpecId::ISTANBUL | $crate::SpecId::MUIR_GLACIER => { + use $crate::IstanbulSpec as SPEC; + $e + } + $crate::SpecId::BERLIN => { + use $crate::BerlinSpec as SPEC; + $e + } + $crate::SpecId::LONDON + | $crate::SpecId::ARROW_GLACIER + | $crate::SpecId::GRAY_GLACIER => { + use $crate::LondonSpec as SPEC; + $e + } + $crate::SpecId::MERGE => { + use $crate::MergeSpec as SPEC; + $e + } + $crate::SpecId::SHANGHAI => { + use $crate::ShanghaiSpec as SPEC; + $e + } + $crate::SpecId::CANCUN => { + use $crate::CancunSpec as SPEC; + $e + } + $crate::SpecId::LATEST => { + use $crate::LatestSpec as SPEC; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::BEDROCK => { + use $crate::BedrockSpec as SPEC; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::REGOLITH => { + use $crate::RegolithSpec as SPEC; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::CANYON => { + use $crate::CanyonSpec as SPEC; + $e + } + } + }}; +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn spec_to_generic() { + use SpecId::*; + + spec_to_generic!(FRONTIER, assert_eq!(SPEC::SPEC_ID, FRONTIER)); + spec_to_generic!(FRONTIER_THAWING, assert_eq!(SPEC::SPEC_ID, FRONTIER)); + spec_to_generic!(HOMESTEAD, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); + spec_to_generic!(DAO_FORK, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); + spec_to_generic!(TANGERINE, assert_eq!(SPEC::SPEC_ID, TANGERINE)); + spec_to_generic!(SPURIOUS_DRAGON, assert_eq!(SPEC::SPEC_ID, SPURIOUS_DRAGON)); + spec_to_generic!(BYZANTIUM, assert_eq!(SPEC::SPEC_ID, BYZANTIUM)); + spec_to_generic!(CONSTANTINOPLE, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); + spec_to_generic!(PETERSBURG, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); + spec_to_generic!(ISTANBUL, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); + spec_to_generic!(MUIR_GLACIER, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); + spec_to_generic!(BERLIN, assert_eq!(SPEC::SPEC_ID, BERLIN)); + spec_to_generic!(LONDON, assert_eq!(SPEC::SPEC_ID, LONDON)); + spec_to_generic!(ARROW_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); + spec_to_generic!(GRAY_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); + spec_to_generic!(MERGE, assert_eq!(SPEC::SPEC_ID, MERGE)); + #[cfg(feature = "optimism")] + spec_to_generic!(BEDROCK, assert_eq!(SPEC::SPEC_ID, BEDROCK)); + #[cfg(feature = "optimism")] + spec_to_generic!(REGOLITH, assert_eq!(SPEC::SPEC_ID, REGOLITH)); + spec_to_generic!(SHANGHAI, assert_eq!(SPEC::SPEC_ID, SHANGHAI)); + #[cfg(feature = "optimism")] + spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON)); + spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); + spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); + } +} + +#[cfg(feature = "optimism")] +#[cfg(test)] +mod optimism_tests { + use super::*; + #[test] fn test_bedrock_post_merge_hardforks() { assert!(BedrockSpec::enabled(SpecId::MERGE)); diff --git a/crates/revm/CHANGELOG.md b/crates/revm/CHANGELOG.md index 60208fea7c..92c8d59e2f 100644 --- a/crates/revm/CHANGELOG.md +++ b/crates/revm/CHANGELOG.md @@ -1,3 +1,10 @@ +# v3.6.0 + +Big renaming long overdue: +* EVMImpl to Evm, +* EVM to EvmFactory +* EVMData to EvmContext + # v3.5.0 date 02.10.2023 diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 4eaed1bc28..8fd90f60d2 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -14,10 +14,11 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +# revm revm-interpreter = { path = "../interpreter", version = "1.3.0", default-features = false } revm-precompile = { path = "../precompile", version = "2.2.0", default-features = false } -#misc +# misc auto_impl = { version = "1.1", default-features = false } # Optional @@ -73,10 +74,6 @@ optional_beneficiary_reward = ["revm-interpreter/optional_beneficiary_reward"] secp256k1 = ["revm-precompile/secp256k1"] c-kzg = ["revm-precompile/c-kzg"] -# deprecated features -web3db = [] -with-serde = [] - [[example]] name = "fork_ref_transact" path = "../../examples/fork_ref_transact.rs" diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 988c2e9b4f..73253cbae9 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -7,19 +7,20 @@ use revm::{ primitives::{ address, bytes, hex, BerlinSpec, Bytecode, BytecodeState, Bytes, TransactTo, U256, }, + Evm, }; use revm_interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use std::time::Duration; -type Evm = revm::EVM; - fn analysis(c: &mut Criterion) { - let mut evm = revm::new(); - - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - // evm.env.tx.data = bytes!("30627b7c"); - evm.env.tx.data = bytes!("8035F0CE"); + let evm = Evm::builder() + .modify_tx_env(|tx| { + tx.caller = address!("0000000000000000000000000000000000000002"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + // evm.env.tx.data = bytes!("30627b7c"); + tx.data = bytes!("8035F0CE"); + }) + .build(); let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); @@ -30,27 +31,38 @@ fn analysis(c: &mut Criterion) { .sample_size(10); let raw = Bytecode::new_raw(contract_data.clone()); - evm.database(BenchmarkDB::new_bytecode(raw)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(raw)) + .build(); bench_transact(&mut g, &mut evm); let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); - evm.database(BenchmarkDB::new_bytecode(checked)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(checked)) + .build(); bench_transact(&mut g, &mut evm); let analysed = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(analysed)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(analysed)) + .build(); bench_transact(&mut g, &mut evm); g.finish(); } fn snailtracer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))); - - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.data = bytes!("30627b7c"); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))) + .modify_tx_env(|tx| { + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.data = bytes!("30627b7c"); + }) + .build(); let mut g = c.benchmark_group("snailtracer"); g.noise_threshold(0.03) @@ -63,12 +75,14 @@ fn snailtracer(c: &mut Criterion) { } fn transfer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); - - evm.env.tx.caller = address!("0000000000000000000000000000000000000001"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.value = U256::from(10); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) + .modify_tx_env(|tx| { + tx.caller = address!("0000000000000000000000000000000000000001"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.value = U256::from(10); + }) + .build(); let mut g = c.benchmark_group("transfer"); g.noise_threshold(0.03).warm_up_time(Duration::from_secs(1)); @@ -76,8 +90,8 @@ fn transfer(c: &mut Criterion) { g.finish(); } -fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { - let state = match evm.db.as_mut().unwrap().0.state { +fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'_, EXT, BenchmarkDB>) { + let state = match evm.context.evm.db.0.state { BytecodeState::Raw => "raw", BytecodeState::Checked { .. } => "checked", BytecodeState::Analysed { .. } => "analysed", @@ -86,15 +100,15 @@ fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); } -fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { +fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'static, (), BenchmarkDB>) { g.bench_function("eval", |b| { let contract = Contract { - input: evm.env.tx.data.clone(), - bytecode: BytecodeLocked::try_from(evm.db.as_ref().unwrap().0.clone()).unwrap(), + input: evm.context.evm.env.tx.data.clone(), + bytecode: BytecodeLocked::try_from(evm.context.evm.db.0.clone()).unwrap(), ..Default::default() }; let mut shared_memory = SharedMemory::new(); - let mut host = DummyHost::new(evm.env.clone()); + let mut host = DummyHost::new(*evm.context.evm.env.clone()); let instruction_table = make_instruction_table::(); b.iter(move || { // replace memory with empty memory to use it inside interpreter. diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs new file mode 100644 index 0000000000..64bfcbffd5 --- /dev/null +++ b/crates/revm/src/builder.rs @@ -0,0 +1,362 @@ +use crate::{ + db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, + handler::register, + primitives::{BlockEnv, CfgEnv, Env, LatestSpec, SpecId, TxEnv}, + Context, Evm, EvmContext, Handler, +}; +use core::marker::PhantomData; + +/// Evm Builder allows building or modifying EVM. +/// Note that some of the methods that changes underlying structures +/// will reset the registered handler to default mainnet. +pub struct EvmBuilder<'a, Stage: BuilderStage, EXT, DB: Database> { + evm: EvmContext, + external: EXT, + handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, + phantom: PhantomData, +} + +/// Trait that unlocks builder stages. +pub trait BuilderStage {} + +/// First stage of the builder allows setting generic variables. +/// Generic variables are database and external context. +pub struct SetGenericStage; +impl BuilderStage for SetGenericStage {} + +/// Second stage of the builder allows appending handler registers. +/// Requires the database and external context to be set. +pub struct HandlerStage; +impl BuilderStage for HandlerStage {} + +impl<'a> Default for EvmBuilder<'a, SetGenericStage, (), EmptyDB> { + fn default() -> Self { + Self { + evm: EvmContext::new(EmptyDB::default()), + external: (), + handler: Handler::mainnet::(), + phantom: PhantomData, + } + } +} + +impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { + /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. + pub fn with_empty_db(self) -> EvmBuilder<'a, SetGenericStage, EXT, EmptyDB> { + EvmBuilder { + evm: self.evm.with_db(EmptyDB::default()), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } + /// Sets the [`Database`] that will be used by [`Evm`]. + pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { + EvmBuilder { + evm: self.evm.with_db(db), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } + /// Sets the [`DatabaseRef`] that will be used by [`Evm`]. + pub fn with_ref_db( + self, + db: ODB, + ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef> { + EvmBuilder { + evm: self.evm.with_db(WrapDatabaseRef(db)), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } + + /// Sets the external context that will be used by [`Evm`]. + pub fn with_external_context( + self, + external: OEXT, + ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> { + EvmBuilder { + evm: self.evm, + external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } +} + +impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { + /// Creates new builder from Evm, Evm is consumed and all field are moved to Builder. + /// It will preserve set handler and context. + /// + /// Builder is in HandlerStage and both database and external are set. + pub fn new(evm: Evm<'a, EXT, DB>) -> Self { + Self { + evm: evm.context.evm, + external: evm.context.external, + handler: evm.handler, + phantom: PhantomData, + } + } + + /// Sets the [`EmptyDB`] and resets the [`Handler`] to default mainnet. + pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EXT, EmptyDB> { + EvmBuilder { + evm: self.evm.with_db(EmptyDB::default()), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } + + /// Sets the [`Database`] that will be used by [`Evm`] + /// and resets the [`Handler`] to default mainnet. + pub fn reset_handler_with_db( + self, + db: ODB, + ) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { + EvmBuilder { + evm: self.evm.with_db(db), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + + phantom: PhantomData, + } + } + + /// Resets [`Handler`] and sets the [`DatabaseRef`] that will be used by [`Evm`] + /// and resets the [`Handler`] to default mainnet. + pub fn reset_handler_with_ref_db( + self, + db: ODB, + ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef> { + EvmBuilder { + evm: self.evm.with_db(WrapDatabaseRef(db)), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + + phantom: PhantomData, + } + } + + /// Resets [`Handler`] and sets new `ExternalContext` type. + /// and resets the [`Handler`] to default mainnet. + pub fn reset_handler_with_external_context( + self, + external: OEXT, + ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> { + EvmBuilder { + evm: self.evm, + external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } +} + +impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> { + /// Builds the [`Evm`]. + pub fn build(self) -> Evm<'a, EXT, DB> { + Evm::new( + Context { + evm: self.evm, + external: self.external, + }, + self.handler, + ) + } + + /// Register Handler that modifies the behavior of EVM. + /// Check [`Handler`] for more information. + /// + /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. + pub fn append_handler_register( + mut self, + handle_register: register::HandleRegister<'a, EXT, DB>, + ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { + self.handler + .append_handle_register(register::HandleRegisters::Plain(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Register Handler that modifies the behavior of EVM. + /// Check [`Handler`] for more information. + /// + /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. + pub fn append_handler_register_box( + mut self, + handle_register: register::HandleRegisterBox<'a, EXT, DB>, + ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { + self.handler + .append_handle_register(register::HandleRegisters::Box(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Sets specification Id , that will mark the version of EVM. + /// It represent the hard fork of ethereum. + /// + /// # Note + /// + /// When changed it will reapply all handle registers, this can be + /// expensive operation depending on registers. + pub fn spec_id(mut self, spec_id: SpecId) -> Self { + self.handler = self.handler.change_spec_id(spec_id); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Allows modification of Evm Database. + pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { + f(&mut self.evm.db); + self + } + + /// Allows modification of external context. + pub fn modify_external_context(mut self, f: impl FnOnce(&mut EXT)) -> Self { + f(&mut self.external); + self + } + + /// Allows modification of Evm Environment. + pub fn modify_env(mut self, f: impl FnOnce(&mut Env)) -> Self { + f(&mut self.evm.env); + self + } + + /// Allows modification of Evm's Transaction Environment. + pub fn modify_tx_env(mut self, f: impl FnOnce(&mut TxEnv)) -> Self { + f(&mut self.evm.env.tx); + self + } + + /// Allows modification of Evm's Block Environment. + pub fn modify_block_env(mut self, f: impl FnOnce(&mut BlockEnv)) -> Self { + f(&mut self.evm.env.block); + self + } + + /// Allows modification of Evm's Config Environment. + pub fn modify_cfg_env(mut self, f: impl FnOnce(&mut CfgEnv)) -> Self { + f(&mut self.evm.env.cfg); + self + } + + /// Clears Environment of EVM. + pub fn with_clear_env(mut self) -> Self { + self.evm.env.clear(); + self + } + + /// Clears Transaction environment of EVM. + pub fn with_clear_tx_env(mut self) -> Self { + self.evm.env.tx.clear(); + self + } + /// Clears Block environment of EVM. + pub fn with_clear_block_env(mut self) -> Self { + self.evm.env.block.clear(); + self + } +} + +#[cfg(test)] +mod test { + use super::SpecId; + use crate::{ + db::EmptyDB, inspector::inspector_handle_register, inspectors::NoOpInspector, Context, Evm, + EvmContext, + }; + + #[test] + fn simple_build() { + // build without external with latest spec + Evm::builder().build(); + // build with empty db + Evm::builder().with_empty_db().build(); + // build with_db + Evm::builder().with_db(EmptyDB::default()).build(); + // build with empty external + Evm::builder().with_empty_db().build(); + // build with some external + Evm::builder() + .with_empty_db() + .with_external_context(()) + .build(); + // build with spec + Evm::builder() + .with_empty_db() + .spec_id(SpecId::HOMESTEAD) + .build(); + + // with with Env change in multiple places + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + Evm::builder().modify_tx_env(|tx| tx.gas_limit = 10).build(); + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + + // with inspector handle + Evm::builder() + .with_empty_db() + .with_external_context(NoOpInspector) + .append_handler_register(inspector_handle_register) + .build(); + + // create the builder + let evm = Evm::builder() + .with_db(EmptyDB::default()) + .with_external_context(NoOpInspector) + .append_handler_register(inspector_handle_register) + // this would not compile + // .with_db(..) + .build(); + + let Context { + external, + evm: EvmContext { db, .. }, + } = evm.into_context(); + let _ = (external, db); + } + + #[test] + fn build_modify_build() { + // build evm + let evm = Evm::builder() + .with_empty_db() + .spec_id(SpecId::HOMESTEAD) + .build(); + + // modify evm + let evm = evm.modify().spec_id(SpecId::FRONTIER).build(); + let _ = evm + .modify() + .modify_tx_env(|tx| tx.chain_id = Some(2)) + .build(); + } +} diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/context.rs similarity index 75% rename from crates/revm/src/evm_context.rs rename to crates/revm/src/context.rs index a643adfda8..52a3e97abe 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/context.rs @@ -1,5 +1,5 @@ use crate::{ - db::Database, + db::{Database, EmptyDB}, interpreter::{ analysis::to_analysed, gas, return_ok, CallInputs, Contract, CreateInputs, Gas, InstructionResult, Interpreter, InterpreterResult, MAX_CODE_SIZE, @@ -7,24 +7,52 @@ use crate::{ journaled_state::JournaledState, precompile::{Precompile, Precompiles}, primitives::{ - keccak256, Address, AnalysisKind, Bytecode, Bytes, CreateScheme, EVMError, Env, Spec, - SpecId::*, B256, U256, + keccak256, Address, AnalysisKind, Bytecode, Bytes, CreateScheme, EVMError, Env, HashSet, + Spec, SpecId, SpecId::*, B256, U256, }, - CallStackFrame, CALL_STACK_LIMIT, + CallStackFrame, FrameData, FrameOrResult, JournalCheckpoint, CALL_STACK_LIMIT, }; use alloc::boxed::Box; use core::ops::Range; -/// EVM Data contains all the data that EVM needs to execute. +/// Main Context structure that contains both EvmContext and External context. +pub struct Context { + /// Evm Context. + pub evm: EvmContext, + /// External contexts. + pub external: EXT, +} + +impl Context { + /// Creates empty context. This is useful for testing. + pub fn new_empty() -> Context<(), EmptyDB> { + Context { + evm: EvmContext::new(EmptyDB::new()), + external: (), + } + } +} + +impl Context<(), DB> { + /// Creates new context with database. + pub fn new_with_db(db: DB) -> Context<(), DB> { + Context { + evm: EvmContext::new_with_env(db, Box::default()), + external: (), + } + } +} + +/// EVM contexts contains data that EVM needs for execution. #[derive(Debug)] -pub struct EvmContext<'a, DB: Database> { +pub struct EvmContext { /// EVM Environment contains all the information about config, block and transaction that /// evm needs. - pub env: &'a mut Env, + pub env: Box, /// EVM State with journaling support. pub journaled_state: JournaledState, /// Database to load data from. - pub db: &'a mut DB, + pub db: DB, /// Error that happened during execution. pub error: Option, /// Precompiles that are available for evm. @@ -34,7 +62,50 @@ pub struct EvmContext<'a, DB: Database> { pub l1_block_info: Option, } -impl<'a, DB: Database> EvmContext<'a, DB> { +impl EvmContext { + pub fn with_db(self, db: ODB) -> EvmContext { + EvmContext { + env: self.env, + journaled_state: self.journaled_state, + db, + error: None, + precompiles: self.precompiles, + #[cfg(feature = "optimism")] + l1_block_info: self.l1_block_info, + } + } + pub fn new(db: DB) -> Self { + Self { + env: Box::default(), + journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } + + /// New context with database and environment. + pub fn new_with_env(db: DB, env: Box) -> Self { + Self { + env, + journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } + + /// Sets precompiles + pub fn set_precompiles(&mut self, precompiles: Precompiles) { + self.journaled_state.warm_preloaded_addresses = + precompiles.addresses().cloned().collect::>(); + self.precompiles = precompiles; + } + /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. @@ -42,7 +113,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn load_access_list(&mut self) -> Result<(), EVMError> { for (address, slots) in self.env.tx.access_list.iter() { self.journaled_state - .initial_account_load(*address, slots, self.db) + .initial_account_load(*address, slots, &mut self.db) .map_err(EVMError::Database)?; } Ok(()) @@ -50,7 +121,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { /// Return environment. pub fn env(&mut self) -> &mut Env { - self.env + &mut self.env } /// Fetch block hash from database. @@ -64,7 +135,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { /// Load account and return flags (is_cold, exists) pub fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { self.journaled_state - .load_account_exist(address, self.db) + .load_account_exist(address, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -82,7 +153,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { let (acc, is_cold) = self .journaled_state - .load_code(address, self.db) + .load_code(address, &mut self.db) .map_err(|e| self.error = Some(e)) .ok()?; Some((acc.info.code.clone().unwrap(), is_cold)) @@ -106,7 +177,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { // account is always warm. reference on that statement https://eips.ethereum.org/EIPS/eip-2929 see `Note 2:` self.journaled_state - .sload(address, index, self.db) + .sload(address, index, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -119,7 +190,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { value: U256, ) -> Option<(U256, U256, U256, bool)> { self.journaled_state - .sstore(address, index, value, self.db) + .sstore(address, index, value, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -135,15 +206,12 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } /// Make create frame. - pub fn make_create_frame( - &mut self, - inputs: &CreateInputs, - ) -> Result, InterpreterResult> { + pub fn make_create_frame(&mut self, spec_id: SpecId, inputs: &CreateInputs) -> FrameOrResult { // Prepare crate. let gas = Gas::new(inputs.gas_limit); let return_error = |e| { - Err(InterpreterResult { + FrameOrResult::Result(InterpreterResult { result: e, gas, output: Bytes::new(), @@ -186,7 +254,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { // Load account so it needs to be marked as warm for access list. if self .journaled_state - .load_account(created_address, self.db) + .load_account(created_address, &mut self.db) .map_err(|e| self.error = Some(e)) .is_err() { @@ -194,10 +262,11 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } // create account, transfer funds and make the journal checkpoint. - let checkpoint = match self.journaled_state.create_account_checkpoint::( + let checkpoint = match self.journaled_state.create_account_checkpoint( inputs.caller, created_address, inputs.value, + spec_id, ) { Ok(checkpoint) => checkpoint, Err(e) => { @@ -216,25 +285,23 @@ impl<'a, DB: Database> EvmContext<'a, DB> { inputs.value, )); - Ok(Box::new(CallStackFrame { - is_create: true, + FrameOrResult::new_frame(CallStackFrame { checkpoint, - created_address: Some(created_address), - subcall_return_memory_range: 0..0, + frame_data: FrameData::Create { created_address }, interpreter: Interpreter::new(contract, gas.limit(), false), - })) + }) } /// Make call frame pub fn make_call_frame( &mut self, inputs: &CallInputs, - return_memory_offset: Range, - ) -> Result, InterpreterResult> { + return_memory_range: Range, + ) -> FrameOrResult { let gas = Gas::new(inputs.gas_limit); let return_result = |instruction_result: InstructionResult| { - Err(InterpreterResult { + FrameOrResult::Result(InterpreterResult { result: instruction_result, gas, output: Bytes::new(), @@ -246,7 +313,10 @@ impl<'a, DB: Database> EvmContext<'a, DB> { return return_result(InstructionResult::CallTooDeep); } - let account = match self.journaled_state.load_code(inputs.contract, self.db) { + let account = match self + .journaled_state + .load_code(inputs.contract, &mut self.db) + { Ok((account, _)) => account, Err(e) => { self.error = Some(e); @@ -270,7 +340,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { &inputs.transfer.source, &inputs.transfer.target, inputs.transfer.value, - self.db, + &mut self.db, ) { self.journaled_state.checkpoint_revert(checkpoint); return return_result(e); @@ -283,7 +353,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } else { self.journaled_state.checkpoint_revert(checkpoint); } - Err(result) + FrameOrResult::Result(result) } else if !bytecode.is_empty() { let contract = Box::new(Contract::new_with_context( inputs.input.clone(), @@ -292,13 +362,13 @@ impl<'a, DB: Database> EvmContext<'a, DB> { &inputs.context, )); // Create interpreter and execute subcall and push new CallStackFrame. - Ok(Box::new(CallStackFrame { - is_create: false, + FrameOrResult::new_frame(CallStackFrame { checkpoint, - created_address: None, - subcall_return_memory_range: return_memory_offset, + frame_data: FrameData::Call { + return_memory_range, + }, interpreter: Interpreter::new(contract, gas.limit(), inputs.is_static), - })) + }) } else { self.journaled_state.checkpoint_commit(); return_result(InstructionResult::Stop) @@ -350,13 +420,13 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn call_return( &mut self, interpreter_result: InterpreterResult, - frame: Box, + journal_checkpoint: JournalCheckpoint, ) -> InterpreterResult { // revert changes or not. if matches!(interpreter_result.result, return_ok!()) { self.journaled_state.checkpoint_commit(); } else { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); } interpreter_result } @@ -366,13 +436,14 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn create_return( &mut self, mut interpreter_result: InterpreterResult, - frame: Box, - ) -> (InterpreterResult, Address) { - let address = frame.created_address.unwrap(); + address: Address, + journal_checkpoint: JournalCheckpoint, + ) -> InterpreterResult { + //let address = frame.created_address.unwrap(); // if return is not ok revert and return. if !matches!(interpreter_result.result, return_ok!()) { - self.journaled_state.checkpoint_revert(frame.checkpoint); - return (interpreter_result, address); + self.journaled_state.checkpoint_revert(journal_checkpoint); + return interpreter_result; } // Host error if present on execution // if ok, check contract creation limit and calculate gas deduction on output len. @@ -382,9 +453,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { && !interpreter_result.output.is_empty() && interpreter_result.output.first() == Some(&0xEF) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::CreateContractStartingWithEF; - return (interpreter_result, address); + return interpreter_result; } // EIP-170: Contract code size limit @@ -397,9 +468,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { .limit_contract_code_size .unwrap_or(MAX_CODE_SIZE) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::CreateContractSizeLimit; - return (interpreter_result, frame.created_address.unwrap()); + return interpreter_result; } let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; if !interpreter_result.gas.record_cost(gas_for_code) { @@ -408,9 +479,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { // final gas fee for adding the contract code to the state, the contract // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. if SPEC::enabled(HOMESTEAD) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::OutOfGas; - return (interpreter_result, address); + return interpreter_result; } else { interpreter_result.output = Bytes::new(); } @@ -433,10 +504,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { self.journaled_state.set_code(address, bytecode); interpreter_result.result = InstructionResult::Return; - (interpreter_result, address) + interpreter_result } } - /// Test utilities for the [`EvmContext`]. #[cfg(any(test, feature = "test-utils"))] pub(crate) mod test_utils { @@ -474,11 +544,11 @@ pub(crate) mod test_utils { /// Creates an evm context with a cache db backend. /// Additionally loads the mock caller account into the db, /// and sets the balance to the provided U256 value. - pub fn create_cache_db_evm_context_with_balance<'a>( - env: &'a mut Env, - db: &'a mut CacheDB, + pub fn create_cache_db_evm_context_with_balance( + env: Box, + mut db: CacheDB, balance: U256, - ) -> EvmContext<'a, CacheDB> { + ) -> EvmContext> { db.insert_account_info( test_utils::MOCK_CALLER, crate::primitives::AccountInfo { @@ -492,13 +562,13 @@ pub(crate) mod test_utils { } /// Creates a cached db evm context. - pub fn create_cache_db_evm_context<'a>( - env: &'a mut Env, - db: &'a mut CacheDB, - ) -> EvmContext<'a, CacheDB> { + pub fn create_cache_db_evm_context( + env: Box, + db: CacheDB, + ) -> EvmContext> { EvmContext { env, - journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: None, precompiles: Precompiles::default(), @@ -508,13 +578,10 @@ pub(crate) mod test_utils { } /// Returns a new `EvmContext` with an empty journaled state. - pub fn create_empty_evm_context<'a>( - env: &'a mut Env, - db: &'a mut EmptyDB, - ) -> EvmContext<'a, EmptyDB> { + pub fn create_empty_evm_context(env: Box, db: EmptyDB) -> EvmContext { EvmContext { env, - journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: None, precompiles: Precompiles::default(), @@ -536,14 +603,16 @@ mod tests { // call stack is too deep. #[test] fn test_make_call_frame_stack_too_deep() { - let mut env = Env::default(); - let mut db = EmptyDB::default(); - let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + let env = Env::default(); + let db = EmptyDB::default(); + let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db); evm_context.journaled_state.depth = CALL_STACK_LIMIT as usize + 1; let contract = address!("dead10000000000000000000000000000001dead"); let call_inputs = test_utils::create_mock_call_inputs(contract); let res = evm_context.make_call_frame(&call_inputs, 0..0); - let err = res.unwrap_err(); + let FrameOrResult::Result(err) = res else { + panic!("Expected FrameOrResult::Result"); + }; assert_eq!(err.result, InstructionResult::CallTooDeep); } @@ -552,14 +621,16 @@ mod tests { // checkpointed on the journaled state correctly. #[test] fn test_make_call_frame_transfer_revert() { - let mut env = Env::default(); - let mut db = EmptyDB::default(); - let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + let env = Env::default(); + let db = EmptyDB::default(); + let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db); let contract = address!("dead10000000000000000000000000000001dead"); let mut call_inputs = test_utils::create_mock_call_inputs(contract); call_inputs.transfer.value = U256::from(1); let res = evm_context.make_call_frame(&call_inputs, 0..0); - let err = res.unwrap_err(); + let FrameOrResult::Result(err) = res else { + panic!("Expected FrameOrResult::Result"); + }; assert_eq!(err.result, InstructionResult::OutOfFund); let checkpointed = vec![vec![JournalEntry::AccountLoaded { address: contract }]]; assert_eq!(evm_context.journaled_state.journal, checkpointed); @@ -568,19 +639,22 @@ mod tests { #[test] fn test_make_call_frame_missing_code_context() { - let mut env = Env::default(); - let mut cdb = CacheDB::new(EmptyDB::default()); + let env = Env::default(); + let cdb = CacheDB::new(EmptyDB::default()); let bal = U256::from(3_000_000_000_u128); - let mut evm_context = create_cache_db_evm_context_with_balance(&mut env, &mut cdb, bal); + let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal); let contract = address!("dead10000000000000000000000000000001dead"); let call_inputs = test_utils::create_mock_call_inputs(contract); let res = evm_context.make_call_frame(&call_inputs, 0..0); - assert_eq!(res.unwrap_err().result, InstructionResult::Stop); + let FrameOrResult::Result(res) = res else { + panic!("Expected FrameOrResult::Result"); + }; + assert_eq!(res.result, InstructionResult::Stop); } #[test] fn test_make_call_frame_succeeds() { - let mut env = Env::default(); + let env = Env::default(); let mut cdb = CacheDB::new(EmptyDB::default()); let bal = U256::from(3_000_000_000_u128); let by = Bytecode::new_raw(Bytes::from(vec![0x60, 0x00, 0x60, 0x00])); @@ -594,12 +668,18 @@ mod tests { code: Some(by), }, ); - let mut evm_context = create_cache_db_evm_context_with_balance(&mut env, &mut cdb, bal); + let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal); let call_inputs = test_utils::create_mock_call_inputs(contract); let res = evm_context.make_call_frame(&call_inputs, 0..0); - let frame = res.unwrap(); - assert!(!frame.is_create); - assert_eq!(frame.created_address, None); - assert_eq!(frame.subcall_return_memory_range, 0..0); + let FrameOrResult::Frame(frame) = res else { + panic!("Expected FrameOrResult::Frame"); + }; + assert!(!frame.is_create()); + assert_eq!( + frame.frame_data, + FrameData::Call { + return_memory_range: 0..0 + } + ); } } diff --git a/crates/revm/src/db.rs b/crates/revm/src/db.rs index 9d6494701e..f891358034 100644 --- a/crates/revm/src/db.rs +++ b/crates/revm/src/db.rs @@ -16,8 +16,3 @@ pub use states::{ OriginalValuesKnown, PlainAccount, RevertToSlot, State, StateBuilder, StateDBBox, StorageWithOriginalValues, TransitionAccount, TransitionState, }; - -#[cfg(all(not(feature = "ethersdb"), feature = "web3db"))] -compile_error!( - "`web3db` feature is deprecated, drop-in replacement can be found with feature `ethersdb`" -); diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index be73045315..521c754242 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,181 +1,362 @@ use crate::{ - db::{Database, DatabaseCommit, DatabaseRef}, - evm_impl::{new_evm, Transact}, - primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, - Inspector, + builder::{EvmBuilder, HandlerStage, SetGenericStage}, + db::{Database, DatabaseCommit, EmptyDB}, + handler::Handler, + interpreter::{ + opcode::InstructionTables, Host, Interpreter, InterpreterAction, InterpreterResult, + SelfDestructResult, SharedMemory, + }, + primitives::{ + specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, ExecutionResult, + Log, Output, ResultAndState, TransactTo, B256, U256, + }, + CallStackFrame, Context, FrameOrResult, }; +use alloc::{boxed::Box, vec::Vec}; +use core::fmt; -/// Struct that takes Database and enabled transact to update state directly to database. -/// additionally it allows user to set all environment parameters. -/// -/// Parameters that can be set are divided between Config, Block and Transaction(tx) -/// -/// For transacting on EVM you can call transact_commit that will automatically apply changes to db. -/// -/// You can do a lot with rust and traits. For Database abstractions that we need you can implement, -/// Database, DatabaseRef or Database+DatabaseCommit and they enable functionality depending on what kind of -/// handling of struct you want. -/// * Database trait has mutable self in its functions. It is usefully if on get calls you want to modify -/// your cache or update some statistics. They enable `transact` and `inspect` functions -/// * DatabaseRef takes reference on object, this is useful if you only have reference on state and don't -/// want to update anything on it. It enabled `transact_ref` and `inspect_ref` functions -/// * Database+DatabaseCommit allow directly committing changes of transaction. it enabled `transact_commit` -/// and `inspect_commit` -/// -/// /// # Example -/// -/// ``` -/// # use revm::EVM; // Assuming this struct is in 'your_crate_name' -/// # struct SomeDatabase; // Mocking a database type for the purpose of this example -/// # struct Env; // Assuming the type Env is defined somewhere -/// -/// let evm: EVM = EVM::new(); -/// assert!(evm.db.is_none()); -/// ``` -/// -#[derive(Clone, Debug)] -pub struct EVM { - pub env: Env, - pub db: Option, -} +/// EVM call stack limit. +pub const CALL_STACK_LIMIT: u64 = 1024; -pub fn new() -> EVM { - EVM::new() +/// EVM instance containing both internal EVM context and external context +/// and the handler that dictates the logic of EVM (or hardfork specification). +pub struct Evm<'a, EXT, DB: Database> { + /// Context of execution, containing both EVM and external context. + pub context: Context, + /// Handler of EVM that contains all the logic. Handler contains specification id + /// and it different depending on the specified fork. + pub handler: Handler<'a, Self, EXT, DB>, } -impl Default for EVM { - fn default() -> Self { - Self::new() +impl fmt::Debug for Evm<'_, EXT, DB> +where + EXT: fmt::Debug, + DB: Database + fmt::Debug, + DB::Error: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Evm") + .field("evm context", &self.context.evm) + .finish_non_exhaustive() } } -impl EVM { - /// Execute transaction and apply result to database +impl Evm<'_, EXT, DB> { + /// Commit the changes to the database. pub fn transact_commit(&mut self) -> Result> { let ResultAndState { result, state } = self.transact()?; - self.db.as_mut().unwrap().commit(state); + self.context.evm.db.commit(state); Ok(result) } +} - /// Inspect transaction and commit changes to database. - pub fn inspect_commit>( - &mut self, - inspector: INSP, - ) -> Result> { - let ResultAndState { result, state } = self.inspect(inspector)?; - self.db.as_mut().unwrap().commit(state); - Ok(result) +impl<'a> Evm<'a, (), EmptyDB> { + /// Returns evm builder with empty database and empty external context. + pub fn builder() -> EvmBuilder<'a, SetGenericStage, (), EmptyDB> { + EvmBuilder::default() + } +} + +impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { + /// Create new EVM. + pub fn new( + mut context: Context, + handler: Handler<'a, Self, EXT, DB>, + ) -> Evm<'a, EXT, DB> { + context.evm.journaled_state.set_spec_id(handler.spec_id); + Evm { context, handler } + } + + /// Allow for evm setting to be modified by feeding current evm + /// into the builder for modifications. + pub fn modify(self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { + EvmBuilder::new(self) } } -impl EVM { - /// Do checks that could make transaction fail before call/create +impl Evm<'_, EXT, DB> { + /// Returns specification (hardfork) that the EVM is instanced with. + /// + /// SpecId depends on the handler. + pub fn spec_id(&self) -> SpecId { + self.handler.spec_id + } + + /// Pre verify transaction by checking Environment, initial gas spend and if caller + /// has enough balance to pay for the gas. + #[inline] pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).preverify_transaction() - } else { - panic!("Database needs to be set"); - } + self.handler.validation().env(&self.context.evm.env)?; + self.handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; + Ok(()) } - /// Skip preverification steps and execute transaction without writing to DB, return change - /// state. + /// Transact pre-verified transaction + /// + /// This function will not validate the transaction. + #[inline] pub fn transact_preverified(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).transact_preverified() - } else { - panic!("Database needs to be set"); - } + let initial_gas_spend = self + .handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.post_execution().end(&mut self.context, output) } - /// Execute transaction without writing to DB, return change state. + /// Transact transaction + /// + /// This function will validate the transaction. + #[inline] pub fn transact(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).transact() - } else { - panic!("Database needs to be set"); - } + self.handler.validation().env(&self.context.evm.env)?; + let initial_gas_spend = self + .handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; + + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.post_execution().end(&mut self.context, output) } - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, Some(&mut inspector)).transact() - } else { - panic!("Database needs to be set"); + /// Modify spec id, this will create new EVM that matches this spec id. + pub fn modify_spec_id(self, spec_id: SpecId) -> Self { + if self.spec_id() == spec_id { + return self; } + self.modify().spec_id(spec_id).build() } -} -impl<'a, DB: DatabaseRef> EVM { - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) - .preverify_transaction() - } else { - panic!("Database needs to be set"); - } + /// Returns internal database and external struct. + #[inline] + pub fn into_context(self) -> Context { + self.context } - /// Skip preverification steps and execute transaction - /// without writing to DB, return change state. - pub fn transact_preverified_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) - .transact_preverified() - } else { - panic!("Database needs to be set"); - } + /// Start the main loop. + pub fn start_the_loop( + &mut self, + first_stack_frame: FrameOrResult, + ) -> (InterpreterResult, Output) { + // Created address will be something only if it is create. + let mut created_address = None; + + // start main loop if CallStackFrame is created correctly + let result = match first_stack_frame { + FrameOrResult::Frame(first_stack_frame) => { + created_address = first_stack_frame.created_address(); + // take instruction talbe + let table = self + .handler + .take_instruction_table() + .expect("Instruction table should be present"); + + // run main loop + let output = match &table { + InstructionTables::Plain(table) => self.run_the_loop(table, first_stack_frame), + InstructionTables::Boxed(table) => self.run_the_loop(table, first_stack_frame), + }; + + // return back instruction table + self.handler.set_instruction_table(table); + + output + } + FrameOrResult::Result(interpreter_result) => interpreter_result, + }; + + // output of execution + let main_output = match self.context.evm.env.tx.transact_to { + TransactTo::Call(_) => Output::Call(result.output.clone()), + TransactTo::Create(_) => Output::Create(result.output.clone(), created_address), + }; + + (result, main_output) } - /// Execute transaction without writing to DB, return change state. - pub fn transact_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None).transact() - } else { - panic!("Database needs to be set"); + /// Runs main call loop. + #[inline] + pub fn run_the_loop( + &mut self, + instruction_table: &[FN; 256], + first_frame: Box, + ) -> InterpreterResult + where + FN: Fn(&mut Interpreter, &mut Self), + { + let mut call_stack: Vec> = Vec::with_capacity(1025); + call_stack.push(first_frame); + + #[cfg(feature = "memory_limit")] + let mut shared_memory = + SharedMemory::new_with_memory_limit(self.context.evm.env.cfg.memory_limit); + #[cfg(not(feature = "memory_limit"))] + let mut shared_memory = SharedMemory::new(); + + shared_memory.new_context(); + + // peek last stack frame. + let mut stack_frame = call_stack.last_mut().unwrap(); + + loop { + // run interpreter + let action = stack_frame + .interpreter + .run(shared_memory, instruction_table, self); + // take shared memory back. + shared_memory = stack_frame.interpreter.take_memory(); + + let new_frame = match action { + InterpreterAction::SubCall { + inputs, + return_memory_offset, + } => self.handler.execution_loop().sub_call( + &mut self.context, + inputs, + stack_frame, + &mut shared_memory, + return_memory_offset, + ), + InterpreterAction::Create { inputs } => { + self.handler + .execution_loop() + .sub_create(&mut self.context, stack_frame, inputs) + } + InterpreterAction::Return { result } => { + // free memory context. + shared_memory.free_context(); + + let child = call_stack.pop().unwrap(); + let parent = call_stack.last_mut(); + + if let Some(result) = self.handler.execution_loop().frame_return( + &mut self.context, + child, + parent, + &mut shared_memory, + result, + ) { + return result; + } + stack_frame = call_stack.last_mut().unwrap(); + continue; + } + }; + if let Some(new_frame) = new_frame { + shared_memory.new_context(); + call_stack.push(new_frame); + } + stack_frame = call_stack.last_mut().unwrap(); } } - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect_ref>>( - &'a self, - mut inspector: I, - ) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - Some(&mut inspector), - ) - .transact() - } else { - panic!("Database needs to be set"); - } + /// Transact pre-verified transaction. + fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { + let hndl = &mut self.handler; + let ctx = &mut self.context; + + // load access list and beneficiary if needed. + hndl.pre_execution().load_accounts(ctx)?; + + // load precompiles + let precompiles = hndl.pre_execution().load_precompiles(); + ctx.evm.set_precompiles(precompiles); + + // deduce caller balance with its limit. + hndl.pre_execution().deduct_caller(ctx)?; + // gas limit used in calls. + let first_frame = hndl + .execution_loop() + .create_first_frame(ctx, ctx.evm.env.tx.gas_limit - initial_gas_spend); + + // Starts the main running loop. + let (result, main_output) = self.start_the_loop(first_frame); + + let hndl = &mut self.handler; + let ctx = &mut self.context; + + // handle output of call/create calls. + let gas = hndl + .execution_loop() + .first_frame_return(&ctx.evm.env, result.result, result.gas); + // Reimburse the caller + hndl.post_execution().reimburse_caller(ctx, &gas)?; + // Reward beneficiary + hndl.post_execution().reward_beneficiary(ctx, &gas)?; + // Returns output of transaction. + hndl.post_execution() + .output(ctx, result.result, main_output, &gas) } } -impl EVM { - /// Creates a new [EVM] instance with the default environment, - pub fn new() -> Self { - Self::with_env(Default::default()) +impl Host for Evm<'_, EXT, DB> { + fn env(&mut self) -> &mut Env { + self.context.evm.env() + } + + fn block_hash(&mut self, number: U256) -> Option { + self.context.evm.block_hash(number) + } + + fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { + self.context.evm.load_account(address) + } + + fn balance(&mut self, address: Address) -> Option<(U256, bool)> { + self.context.evm.balance(address) + } + + fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { + self.context.evm.code(address) + } + + fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { + self.context.evm.code_hash(address) + } + + fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { + self.context.evm.sload(address, index) + } + + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option<(U256, U256, U256, bool)> { + self.context.evm.sstore(address, index, value) } - /// Creates a new [EVM] instance with the given environment. - pub fn with_env(env: Env) -> Self { - Self { env, db: None } + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.context.evm.tload(address, index) } - pub fn database(&mut self, db: DB) { - self.db = Some(db); + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.context.evm.tstore(address, index, value) } - pub fn db(&mut self) -> Option<&mut DB> { - self.db.as_mut() + fn log(&mut self, address: Address, topics: Vec, data: Bytes) { + self.context.evm.journaled_state.log(Log { + address, + topics, + data, + }); } - pub fn take_db(&mut self) -> DB { - core::mem::take(&mut self.db).unwrap() + fn selfdestruct(&mut self, address: Address, target: Address) -> Option { + self.context + .evm + .journaled_state + .selfdestruct(address, target, &mut self.context.evm.db) + .map_err(|e| self.context.evm.error = Some(e)) + .ok() } } diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs deleted file mode 100644 index 0b7a4a2c66..0000000000 --- a/crates/revm/src/evm_impl.rs +++ /dev/null @@ -1,832 +0,0 @@ -use crate::{ - db::Database, - handler::Handler, - inspector_instruction, - interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, - }, - journaled_state::JournaledState, - precompile::Precompiles, - primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, - Output, Spec, SpecId::*, TransactTo, B256, U256, - }, - CallStackFrame, EvmContext, Inspector, -}; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; - -#[cfg(feature = "optimism")] -use crate::optimism; - -/// EVM call stack limit. -pub const CALL_STACK_LIMIT: u64 = 1024; - -pub struct EVMImpl<'a, SPEC: Spec, DB: Database> { - pub context: EvmContext<'a, DB>, - pub inspector: Option<&'a mut dyn Inspector>, - pub instruction_table: InstructionTables<'a, Self>, - pub handler: Handler, - _phantomdata: PhantomData, -} - -impl fmt::Debug for EVMImpl<'_, SPEC, DB> -where - SPEC: Spec, - DB: Database + fmt::Debug, - DB::Error: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EVMImpl") - .field("data", &self.context) - .finish_non_exhaustive() - } -} - -#[cfg(feature = "optimism")] -impl<'a, SPEC: Spec, DB: Database> EVMImpl<'a, SPEC, DB> { - /// If the transaction is not a deposit transaction, subtract the L1 data fee from the - /// caller's balance directly after minting the requested amount of ETH. - fn remove_l1_cost( - is_deposit: bool, - tx_caller: Address, - l1_cost: U256, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if is_deposit { - return Ok(()); - } - let acc = journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0; - if l1_cost.gt(&acc.info.balance) { - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(l1_cost), - balance: Box::new(acc.info.balance), - }, - )); - } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); - Ok(()) - } - - /// If the transaction is a deposit with a `mint` value, add the mint value - /// in wei to the caller's balance. This should be persisted to the database - /// prior to the rest of execution. - fn commit_mint_value( - tx_caller: Address, - tx_mint: Option, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if let Some(mint) = tx_mint { - journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0 - .info - .balance += U256::from(mint); - journal.checkpoint(); - } - Ok(()) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> EVMImpl<'a, SPEC, DB> { - pub fn new_with_spec( - db: &'a mut DB, - env: &'a mut Env, - inspector: Option<&'a mut dyn Inspector>, - precompiles: Precompiles, - ) -> Self { - let journaled_state = - JournaledState::new(SPEC::SPEC_ID, precompiles.addresses().copied().collect()); - // If T is present it should be a generic T that modifies handler. - let instruction_table = if inspector.is_some() { - let instruction_table = make_boxed_instruction_table::( - make_instruction_table::(), - inspector_instruction, - ); - InstructionTables::Boxed(Arc::new(instruction_table)) - } else { - InstructionTables::Plain(Arc::new(make_instruction_table::())) - }; - #[cfg(feature = "optimism")] - let mut handler = if env.cfg.optimism { - Handler::optimism::() - } else { - Handler::mainnet::() - }; - #[cfg(not(feature = "optimism"))] - let mut handler = Handler::mainnet::(); - - if env.cfg.is_beneficiary_reward_disabled() { - // do nothing - handler.reward_beneficiary = |_, _| Ok(()); - } - - Self { - context: EvmContext { - env, - journaled_state, - db, - error: None, - precompiles, - #[cfg(feature = "optimism")] - l1_block_info: None, - }, - inspector, - instruction_table, - handler, - _phantomdata: PhantomData {}, - } - } - - #[inline] - pub fn run( - &mut self, - instruction_table: &[FN; 256], - first_frame: Box, - ) -> InterpreterResult - where - FN: Fn(&mut Interpreter, &mut Self), - { - let mut call_stack: Vec> = Vec::with_capacity(1025); - call_stack.push(first_frame); - - #[cfg(feature = "memory_limit")] - let mut shared_memory = - SharedMemory::new_with_memory_limit(self.context.env.cfg.memory_limit); - #[cfg(not(feature = "memory_limit"))] - let mut shared_memory = SharedMemory::new(); - - shared_memory.new_context(); - - let mut stack_frame = call_stack.first_mut().unwrap(); - - loop { - // run interpreter - let action = stack_frame - .interpreter - .run(shared_memory, instruction_table, self); - // take shared memory back. - shared_memory = stack_frame.interpreter.take_memory(); - - let new_frame = match action { - InterpreterAction::SubCall { - inputs, - return_memory_offset, - } => self.handle_sub_call( - inputs, - stack_frame, - return_memory_offset, - &mut shared_memory, - ), - InterpreterAction::Create { inputs } => self.handle_sub_create(inputs, stack_frame), - InterpreterAction::Return { result } => { - // free memory context. - shared_memory.free_context(); - - let child = call_stack.pop().unwrap(); - let parent = call_stack.last_mut(); - - if let Some(result) = - self.handle_frame_return(child, parent, &mut shared_memory, result) - { - return result; - } - stack_frame = call_stack.last_mut().unwrap(); - continue; - } - }; - if let Some(new_frame) = new_frame { - shared_memory.new_context(); - call_stack.push(new_frame); - } - stack_frame = call_stack.last_mut().unwrap(); - } - } - - fn handle_frame_return( - &mut self, - mut child_stack_frame: Box, - parent_stack_frame: Option<&mut Box>, - shared_memory: &mut SharedMemory, - mut result: InterpreterResult, - ) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - result = if child_stack_frame.is_create { - let (result, address) = inspector.create_end( - &mut self.context, - result, - child_stack_frame.created_address, - ); - child_stack_frame.created_address = address; - result - } else { - inspector.call_end(&mut self.context, result) - }; - } - - // break from loop if this is last CallStackFrame. - let Some(parent_stack_frame) = parent_stack_frame else { - let result = if child_stack_frame.is_create { - self.context - .create_return::(result, child_stack_frame) - .0 - } else { - self.context.call_return(result, child_stack_frame) - }; - - return Some(result); - }; - - if child_stack_frame.is_create { - let (result, address) = self - .context - .create_return::(result, child_stack_frame); - parent_stack_frame - .interpreter - .insert_create_output(result, Some(address)) - } else { - let subcall_memory_return_offset = - child_stack_frame.subcall_return_memory_range.clone(); - let result = self.context.call_return(result, child_stack_frame); - - parent_stack_frame.interpreter.insert_call_output( - shared_memory, - result, - subcall_memory_return_offset, - ) - } - None - } - - /// Handle Action for new sub create call, return None if there is no need - /// to add new stack frame. - #[inline] - fn handle_sub_create( - &mut self, - mut inputs: Box, - curent_stack_frame: &mut CallStackFrame, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, address)) = inspector.create(&mut self.context, &mut inputs) { - curent_stack_frame - .interpreter - .insert_create_output(result, address); - return None; - } - } - - match self.context.make_create_frame::(&inputs) { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - let mut address = None; - if let Some(inspector) = self.inspector.as_mut() { - let ret = inspector.create_end( - &mut self.context, - result, - curent_stack_frame.created_address, - ); - result = ret.0; - address = ret.1; - } - // insert result of the failed creation of create CallStackFrame. - curent_stack_frame - .interpreter - .insert_create_output(result, address); - None - } - } - } - - /// Handles action for new sub call, return None if there is no need to add - /// new stack frame. - #[inline] - fn handle_sub_call( - &mut self, - mut inputs: Box, - curent_stake_frame: &mut CallStackFrame, - return_memory_offset: Range, - shared_memory: &mut SharedMemory, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, range)) = inspector.call(&mut self.context, &mut inputs) { - curent_stake_frame - .interpreter - .insert_call_output(shared_memory, result, range); - return None; - } - } - match self - .context - .make_call_frame(&inputs, return_memory_offset.clone()) - { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - if let Some(inspector) = &mut self.inspector { - result = inspector.call_end(&mut self.context, result); - } - curent_stake_frame.interpreter.insert_call_output( - shared_memory, - result, - return_memory_offset, - ); - None - } - } - } - - /// Pre verify transaction. - pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { - let env = self.env(); - - // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::()?; - - let initial_gas_spend = initial_tx_gas::( - &env.tx.data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit { - return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); - } - - // load acc - let tx_caller = env.tx.caller; - let (caller_account, _) = self - .context - .journaled_state - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - self.context - .env - .validate_tx_against_state(caller_account) - .map_err(Into::into) - } - - /// Transact preverified transaction. - pub fn transact_preverified_inner(&mut self) -> EVMResult { - let env = &self.context.env; - let tx_caller = env.tx.caller; - let tx_value = env.tx.value; - let tx_data = env.tx.data.clone(); - let tx_gas_limit = env.tx.gas_limit; - - // the L1-cost fee is only computed for Optimism non-deposit transactions. - #[cfg(feature = "optimism")] - let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.context.db).map_err(EVMError::Database)?; - - let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); - }; - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); - - // storage l1 block info for later use. - self.context.l1_block_info = Some(l1_block_info); - - tx_l1_cost - } else { - U256::ZERO - }; - - let initial_gas_spend = initial_tx_gas::( - &tx_data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { - self.context - .journaled_state - .initial_account_load(self.context.env.block.coinbase, &[], self.context.db) - .map_err(EVMError::Database)?; - } - - self.context.load_access_list()?; - - // load acc - let journal = &mut self.context.journaled_state; - - #[cfg(feature = "optimism")] - if self.context.env.cfg.optimism { - EVMImpl::::commit_mint_value( - tx_caller, - self.context.env.tx.optimism.mint, - self.context.db, - journal, - )?; - - let is_deposit = self.context.env.tx.optimism.source_hash.is_some(); - EVMImpl::::remove_l1_cost( - is_deposit, - tx_caller, - tx_l1_cost, - self.context.db, - journal, - )?; - } - - let (caller_account, _) = journal - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = - U256::from(tx_gas_limit).saturating_mul(self.context.env.effective_gas_price()); - - // EIP-4844 - if SPEC::enabled(CANCUN) { - let data_fee = self.context.env.calc_data_fee().expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // touch account so we know it is changed. - caller_account.mark_touch(); - - let transact_gas_limit = tx_gas_limit - initial_gas_spend; - - // call inner handling of call/create - let first_stack_frame = match self.context.env.tx.transact_to { - TransactTo::Call(address) => { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - - self.context.make_call_frame( - &CallInputs { - contract: address, - transfer: Transfer { - source: tx_caller, - target: address, - value: tx_value, - }, - input: tx_data, - gas_limit: transact_gas_limit, - context: CallContext { - caller: tx_caller, - address, - code_address: address, - apparent_value: tx_value, - scheme: CallScheme::Call, - }, - is_static: false, - }, - 0..0, - ) - } - TransactTo::Create(scheme) => self.context.make_create_frame::(&CreateInputs { - caller: tx_caller, - scheme, - value: tx_value, - init_code: tx_data, - gas_limit: transact_gas_limit, - }), - }; - // Some only if it is create. - let mut created_address = None; - - // start main loop if CallStackFrame is created correctly - let interpreter_result = match first_stack_frame { - Ok(first_stack_frame) => { - created_address = first_stack_frame.created_address; - let table = self.instruction_table.clone(); - match table { - InstructionTables::Plain(table) => self.run(&table, first_stack_frame), - InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), - } - } - Err(interpreter_result) => interpreter_result, - }; - - let handler = &self.handler; - let data = &mut self.context; - - // handle output of call/create calls. - let mut gas = - handler.call_return(data.env, interpreter_result.result, interpreter_result.gas); - - // set refund. Refund amount depends on hardfork. - gas.set_refund(handler.calculate_gas_refund(data.env, &gas) as i64); - - // Reimburse the caller - handler.reimburse_caller(data, &gas)?; - - // Reward beneficiary - handler.reward_beneficiary(data, &gas)?; - - // output of execution - let output = match data.env.tx.transact_to { - TransactTo::Call(_) => Output::Call(interpreter_result.output), - TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), - }; - - // main return - handler.main_return(data, interpreter_result.result, output, &gas) - } -} - -/// EVM transaction interface. -#[auto_impl(&mut, Box)] -pub trait Transact { - /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; - - /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; - - /// Execute transaction by running pre-verification steps and then transaction itself. - fn transact(&mut self) -> EVMResult; -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Transact for EVMImpl<'a, SPEC, DB> { - #[inline] - fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.preverify_transaction_inner() - } - - #[inline] - fn transact_preverified(&mut self) -> EVMResult { - let output = self.transact_preverified_inner(); - self.handler.end(&mut self.context, output) - } - - #[inline] - fn transact(&mut self) -> EVMResult { - let output = self - .preverify_transaction_inner() - .and_then(|()| self.transact_preverified_inner()); - self.handler.end(&mut self.context, output) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Host for EVMImpl<'a, SPEC, DB> { - fn env(&mut self) -> &mut Env { - self.context.env() - } - - fn block_hash(&mut self, number: U256) -> Option { - self.context.block_hash(number) - } - - fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { - self.context.load_account(address) - } - - fn balance(&mut self, address: Address) -> Option<(U256, bool)> { - self.context.balance(address) - } - - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { - self.context.code(address) - } - - /// Get code hash of address. - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { - self.context.code_hash(address) - } - - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { - self.context.sload(address, index) - } - - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)> { - self.context.sstore(address, index, value) - } - - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.context.tload(address, index) - } - - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.context.tstore(address, index, value) - } - - fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - if let Some(inspector) = self.inspector.as_mut() { - inspector.log(&mut self.context, &address, &topics, &data); - } - let log = Log { - address, - topics, - data, - }; - self.context.journaled_state.log(log); - } - - fn selfdestruct(&mut self, address: Address, target: Address) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - let acc = self.context.journaled_state.state.get(&address).unwrap(); - inspector.selfdestruct(address, target, acc.info.balance); - } - self.context - .journaled_state - .selfdestruct(address, target, self.context.db) - .map_err(|e| self.context.error = Some(e)) - .ok() - } -} - -/// Creates new EVM instance with erased types. -pub fn new_evm<'a, DB: Database>( - env: &'a mut Env, - db: &'a mut DB, - insp: Option<&'a mut dyn Inspector>, -) -> Box + 'a> { - macro_rules! create_evm { - ($spec:ident) => { - Box::new(EVMImpl::<'a, $spec, DB>::new_with_spec( - db, - env, - insp, - Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - )) - }; - } - - use specification::*; - match env.cfg.spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), - SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), - SpecId::TANGERINE => create_evm!(TangerineSpec), - SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), - SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), - SpecId::BERLIN => create_evm!(BerlinSpec), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - create_evm!(LondonSpec) - } - SpecId::MERGE => create_evm!(MergeSpec), - SpecId::SHANGHAI => create_evm!(ShanghaiSpec), - SpecId::CANCUN => create_evm!(CancunSpec), - SpecId::LATEST => create_evm!(LatestSpec), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => create_evm!(BedrockSpec), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => create_evm!(RegolithSpec), - #[cfg(feature = "optimism")] - SpecId::CANYON => create_evm!(CanyonSpec), - } -} - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod tests { - use super::*; - - use crate::db::InMemoryDB; - use crate::primitives::{specification::BedrockSpec, state::AccountInfo, SpecId}; - - #[test] - fn test_commit_mint_value() { - let caller = Address::ZERO; - let mint_value = Some(1u128); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::commit_mint_value( - caller, - mint_value, - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - - // No mint value should be a no-op. - assert!(EVMImpl::::commit_mint_value( - caller, - None, - &mut db, - &mut journal - ) - .is_ok(),); - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - } - - #[test] - fn test_remove_l1_cost_non_deposit() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - let slots = &[U256::from(100)]; - journal - .initial_account_load(caller, slots, &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - true, - caller, - U256::ZERO, - &mut db, - &mut journal - ) - .is_ok(),); - } - - #[test] - fn test_remove_l1_cost() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(1), - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(99)); - } - - #[test] - fn test_remove_l1_cost_lack_of_funds() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert_eq!( - EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(101), - &mut db, - &mut journal - ), - Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(U256::from(101)), - balance: Box::new(U256::from(100)), - }, - )) - ); - } -} diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index fc1ff95e18..9397732d12 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -1,18 +1,62 @@ -use crate::{interpreter::Interpreter, primitives::Address, JournalCheckpoint}; +use crate::{ + interpreter::{Interpreter, InterpreterResult}, + primitives::Address, + JournalCheckpoint, +}; +use alloc::boxed::Box; use core::ops::Range; /// Call CallStackFrame. #[derive(Debug)] pub struct CallStackFrame { - /// True if it is create false if it is call. - /// TODO make a enum for this. - pub is_create: bool, /// Journal checkpoint pub checkpoint: JournalCheckpoint, - /// temporary. If it is create it should have address. - pub created_address: Option
, - /// temporary. Call range - pub subcall_return_memory_range: Range, /// Interpreter pub interpreter: Interpreter, + /// Frame data + pub frame_data: FrameData, +} + +/// Specific data for call or create frame. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum FrameData { + /// Call frame has return memory range where output will be stored. + Call { return_memory_range: Range }, + /// Create frame has a created address. + Create { created_address: Address }, +} + +/// Contains either a frame or a result. +pub enum FrameOrResult { + /// Boxed stack frame + Frame(Box), + /// Interpreter result + Result(InterpreterResult), +} + +impl CallStackFrame { + /// Returns true if frame is call frame. + pub fn is_call(&self) -> bool { + matches!(self.frame_data, FrameData::Call { .. }) + } + + /// Returns true if frame is create frame. + pub fn is_create(&self) -> bool { + matches!(self.frame_data, FrameData::Create { .. }) + } + + /// Returns created address if frame is create otherwise returns None. + pub fn created_address(&self) -> Option
{ + match self.frame_data { + FrameData::Create { created_address } => Some(created_address), + _ => None, + } + } +} + +impl FrameOrResult { + /// Returns new frame. + pub fn new_frame(frame: CallStackFrame) -> Self { + Self::Frame(Box::new(frame)) + } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index eef328bd41..c43b3c3b2a 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,137 +1,128 @@ +// Modules. +mod handle_types; pub mod mainnet; -#[cfg(feature = "optimism")] -pub mod optimism; +pub mod register; +// Exports. +pub use handle_types::*; + +// Includes. use crate::{ - interpreter::{Gas, InstructionResult}, - primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, - EvmContext, + interpreter::{ + opcode::{make_instruction_table, InstructionTables}, + Host, + }, + primitives::{db::Database, spec_to_generic, Spec, SpecId}, }; - -/// Handle call return and return final gas value. -type CallReturnHandle = fn(&Env, InstructionResult, Gas) -> Gas; - -/// Reimburse the caller with ethereum it didn't spent. -type ReimburseCallerHandle = - fn(&mut EvmContext<'_, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; - -/// Reward beneficiary with transaction rewards. -type RewardBeneficiaryHandle = ReimburseCallerHandle; - -/// Calculate gas refund for transaction. -type CalculateGasRefundHandle = fn(&Env, &Gas) -> u64; - -/// Main return handle, takes state from journal and transforms internal result to external. -type MainReturnHandle = fn( - &mut EvmContext<'_, DB>, - InstructionResult, - Output, - &Gas, -) -> Result::Error>>; - -/// End handle, takes result and state and returns final result. -/// This will be called after all the other handlers. -/// -/// It is useful for catching errors and returning them in a different way. -type EndHandle = fn( - &mut EvmContext<'_, DB>, - evm_output: Result::Error>>, -) -> Result::Error>>; +use alloc::vec::Vec; +use register::{EvmHandler, HandleRegisters}; /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or /// to disable some mainnet behavior. -pub struct Handler { - // Uses env, call result and returned gas from the call to determine the gas - // that is returned from transaction execution.. - pub call_return: CallReturnHandle, - /// Reimburse the caller with ethereum it didn't spent. - pub reimburse_caller: ReimburseCallerHandle, - /// Reward the beneficiary with caller fee. - pub reward_beneficiary: RewardBeneficiaryHandle, - /// Calculate gas refund for transaction. - /// Some chains have it disabled. - pub calculate_gas_refund: CalculateGasRefundHandle, - /// Main return handle, returns the output of the transact. - pub main_return: MainReturnHandle, - /// End handle. - pub end: EndHandle, +pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { + /// Specification ID. + pub spec_id: SpecId, + /// Instruction table type. + pub instruction_table: Option>, + /// Registers that will be called on initialization. + pub registers: Vec>, + /// Validity handles. + pub validation: ValidationHandler<'a, EXT, DB>, + /// Pre execution handle + pub pre_execution: PreExecutionHandler<'a, EXT, DB>, + /// post Execution handle + pub post_execution: PostExecutionHandler<'a, EXT, DB>, + /// Execution loop that handles frames. + pub execution_loop: ExecutionLoopHandler<'a, EXT, DB>, } -impl Handler { +impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Handler for the mainnet - pub fn mainnet() -> Self { + pub fn mainnet() -> Self { Self { - call_return: mainnet::handle_call_return::, - calculate_gas_refund: mainnet::calculate_gas_refund::, - reimburse_caller: mainnet::handle_reimburse_caller::, - reward_beneficiary: mainnet::reward_beneficiary::, - main_return: mainnet::main_return::, - end: mainnet::end_handle::, + spec_id: SPEC::SPEC_ID, + instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), + registers: Vec::new(), + validation: ValidationHandler::new::(), + pre_execution: PreExecutionHandler::new::(), + post_execution: PostExecutionHandler::new::(), + execution_loop: ExecutionLoopHandler::new::(), } } - /// Handler for the optimism - #[cfg(feature = "optimism")] - pub fn optimism() -> Self { - Self { - call_return: optimism::handle_call_return::, - // we reinburse caller the same was as in mainnet. - // Refund is calculated differently then mainnet. - reimburse_caller: mainnet::handle_reimburse_caller::, - calculate_gas_refund: optimism::calculate_gas_refund::, - reward_beneficiary: optimism::reward_beneficiary::, - // In case of halt of deposit transaction return Error. - main_return: optimism::main_return::, - end: optimism::end_handle::, - } + /// Creates handler with variable spec id, inside it will call `mainnet::` for + /// appropriate spec. + pub fn mainnet_with_spec(spec_id: SpecId) -> Self { + spec_to_generic!(spec_id, Self::mainnet::()) + } + + /// Specification ID. + pub fn spec_id(&self) -> SpecId { + self.spec_id } - /// Handle call return, depending on instruction result gas will be reimbursed or not. - pub fn call_return(&self, env: &Env, call_result: InstructionResult, returned_gas: Gas) -> Gas { - (self.call_return)(env, call_result, returned_gas) + /// Take instruction table. + pub fn take_instruction_table(&mut self) -> Option> { + self.instruction_table.take() } - /// Reimburse the caller with gas that were not spend. - pub fn reimburse_caller( - &self, - context: &mut EvmContext<'_, DB>, - gas: &Gas, - ) -> Result<(), EVMError> { - (self.reimburse_caller)(context, gas) + /// Set instruction table. + pub fn set_instruction_table(&mut self, table: InstructionTables<'a, H>) { + self.instruction_table = Some(table); } - /// Calculate gas refund for transaction. Some chains have it disabled. - pub fn calculate_gas_refund(&self, env: &Env, gas: &Gas) -> u64 { - (self.calculate_gas_refund)(env, gas) + /// Returns reference to pre execution handler. + pub fn pre_execution(&self) -> &PreExecutionHandler<'a, EXT, DB> { + &self.pre_execution } - /// Reward beneficiary - pub fn reward_beneficiary( - &self, - context: &mut EvmContext<'_, DB>, - gas: &Gas, - ) -> Result<(), EVMError> { - (self.reward_beneficiary)(context, gas) + /// Returns reference to pre execution handler. + pub fn post_execution(&self) -> &PostExecutionHandler<'a, EXT, DB> { + &self.post_execution } - /// Main return. - pub fn main_return( - &self, - context: &mut EvmContext<'_, DB>, - call_result: InstructionResult, - output: Output, - gas: &Gas, - ) -> Result> { - (self.main_return)(context, call_result, output, gas) + /// Returns reference to frame handler. + pub fn execution_loop(&self) -> &ExecutionLoopHandler<'a, EXT, DB> { + &self.execution_loop } - /// End handler. - pub fn end( - &self, - context: &mut EvmContext<'_, DB>, - end_output: Result>, - ) -> Result> { - (self.end)(context, end_output) + /// Returns reference to validation handler. + pub fn validation(&self) -> &ValidationHandler<'a, EXT, DB> { + &self.validation + } +} + +impl<'a, EXT: 'a, DB: Database + 'a> EvmHandler<'a, EXT, DB> { + /// Append handle register. + pub fn append_handle_register(&mut self, register: HandleRegisters<'a, EXT, DB>) { + register.register(self); + self.registers.push(register); + } + + /// Creates the Handler with Generic Spec. + pub fn create_handle_generic(&mut self) -> EvmHandler<'a, EXT, DB> { + let registers = core::mem::take(&mut self.registers); + let mut base_handler = Handler::mainnet::(); + // apply all registers to default handeler and raw mainnet instruction table. + for register in registers { + base_handler.append_handle_register(register) + } + base_handler + } + + /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. + pub fn change_spec_id(mut self, spec_id: SpecId) -> EvmHandler<'a, EXT, DB> { + if self.spec_id == spec_id { + return self; + } + + let registers = core::mem::take(&mut self.registers); + let mut handler = Handler::mainnet_with_spec(spec_id); + // apply all registers to default handeler and raw mainnet instruction table. + for register in registers { + handler.append_handle_register(register) + } + handler } } diff --git a/crates/revm/src/handler/handle_types.rs b/crates/revm/src/handler/handle_types.rs new file mode 100644 index 0000000000..a37458c76a --- /dev/null +++ b/crates/revm/src/handler/handle_types.rs @@ -0,0 +1,25 @@ +// Modules + +pub mod execution_loop; +pub mod post_execution; +pub mod pre_execution; +pub mod validation; + +// Exports + +pub use validation::{ + ValidateEnvHandle, ValidateInitialTxGasHandle, ValidateTxEnvAgainstState, ValidationHandler, +}; + +pub use execution_loop::{ + CreateFirstFrameHandle, ExecutionLoopHandler, FrameReturnHandle, FrameSubCallHandle, + FrameSubCreateHandle, +}; + +pub use pre_execution::{ + DeductCallerHandle, LoadAccountsHandle, LoadPrecompilesHandle, PreExecutionHandler, +}; + +pub use post_execution::{ + EndHandle, OutputHandle, PostExecutionHandler, ReimburseCallerHandle, RewardBeneficiaryHandle, +}; diff --git a/crates/revm/src/handler/handle_types/execution_loop.rs b/crates/revm/src/handler/handle_types/execution_loop.rs new file mode 100644 index 0000000000..41a2ae9065 --- /dev/null +++ b/crates/revm/src/handler/handle_types/execution_loop.rs @@ -0,0 +1,154 @@ +use crate::{ + handler::mainnet, + interpreter::{ + CallInputs, CreateInputs, Gas, InstructionResult, InterpreterResult, SharedMemory, + }, + primitives::{db::Database, Env, Spec}, + CallStackFrame, Context, FrameOrResult, +}; +use alloc::{boxed::Box, sync::Arc}; +use core::ops::Range; + +/// Creates the first frame. +pub type CreateFirstFrameHandle<'a, EXT, DB> = + Arc, u64) -> FrameOrResult + 'a>; + +/// Handles first frame return handle. +pub type FirstFrameReturnHandle<'a> = Arc Gas + 'a>; + +/// After subcall is finished, call this function to handle return result. +/// +/// Return Some if we want to halt execution. This can be done on any stack frame. +pub type FrameReturnHandle<'a, EXT, DB> = Arc< + dyn Fn( + // context + &mut Context, + // returned frame + Box, + // parent frame if it exist. + Option<&mut Box>, + // shared memory to insert output of the call. + &mut SharedMemory, + // output of frame execution. + InterpreterResult, + ) -> Option + + 'a, +>; + +/// Handle sub call. +pub type FrameSubCallHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + Box, + &mut CallStackFrame, + &mut SharedMemory, + Range, + ) -> Option> + + 'a, +>; + +/// Handle sub create. +pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + &mut CallStackFrame, + Box, + ) -> Option> + + 'a, +>; + +/// Handles related to stack frames. +pub struct ExecutionLoopHandler<'a, EXT, DB: Database> { + /// Create Main frame + pub create_first_frame: CreateFirstFrameHandle<'a, EXT, DB>, + /// Validate Transaction against the state. + /// Uses env, call result and returned gas from the call to determine the gas + /// that is returned from transaction execution.. + pub first_frame_return: FirstFrameReturnHandle<'a>, + /// Frame return + pub frame_return: FrameReturnHandle<'a, EXT, DB>, + /// Frame sub call + pub sub_call: FrameSubCallHandle<'a, EXT, DB>, + /// Frame sub crate + pub sub_create: FrameSubCreateHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> ExecutionLoopHandler<'a, EXT, DB> { + /// Creates mainnet ExecutionLoopHandler.. + pub fn new() -> Self { + Self { + create_first_frame: Arc::new(mainnet::create_first_frame::), + first_frame_return: Arc::new(mainnet::first_frame_return::), + frame_return: Arc::new(mainnet::frame_return::), + sub_call: Arc::new(mainnet::sub_call::), + sub_create: Arc::new(mainnet::sub_create::), + } + } +} + +impl<'a, EXT, DB: Database> ExecutionLoopHandler<'a, EXT, DB> { + /// Create first call frame. + pub fn create_first_frame( + &self, + context: &mut Context, + gas_limit: u64, + ) -> FrameOrResult { + (self.create_first_frame)(context, gas_limit) + } + + /// Handle call return, depending on instruction result gas will be reimbursed or not. + pub fn first_frame_return( + &self, + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, + ) -> Gas { + (self.first_frame_return)(env, call_result, returned_gas) + } + + /// Call frame sub call handler. + pub fn sub_call( + &self, + context: &mut Context, + inputs: Box, + curent_stack_frame: &mut CallStackFrame, + shared_memory: &mut SharedMemory, + return_memory_offset: Range, + ) -> Option> { + (self.sub_call)( + context, + inputs, + curent_stack_frame, + shared_memory, + return_memory_offset, + ) + } + + /// Create sub frame + pub fn sub_create( + &self, + context: &mut Context, + curent_stack_frame: &mut CallStackFrame, + inputs: Box, + ) -> Option> { + (self.sub_create)(context, curent_stack_frame, inputs) + } + + /// Frame return + pub fn frame_return( + &self, + context: &mut Context, + child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + result: InterpreterResult, + ) -> Option { + (self.frame_return)( + context, + child_stack_frame, + parent_stack_frame, + shared_memory, + result, + ) + } +} diff --git a/crates/revm/src/handler/handle_types/post_execution.rs b/crates/revm/src/handler/handle_types/post_execution.rs new file mode 100644 index 0000000000..c665a30b75 --- /dev/null +++ b/crates/revm/src/handler/handle_types/post_execution.rs @@ -0,0 +1,101 @@ +// Includes. +use crate::{ + handler::mainnet, + interpreter::{Gas, InstructionResult}, + primitives::{db::Database, EVMError, EVMResultGeneric, Output, ResultAndState, Spec}, + Context, +}; +use alloc::sync::Arc; + +/// Reimburse the caller with ethereum it didn't spent. +pub type ReimburseCallerHandle<'a, EXT, DB> = + Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; + +/// Reward beneficiary with transaction rewards. +pub type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; + +/// Main return handle, takes state from journal and transforms internal result to external. +pub type OutputHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + InstructionResult, + Output, + &Gas, + ) -> Result::Error>> + + 'a, +>; + +/// End handle, takes result and state and returns final result. +/// This will be called after all the other handlers. +/// +/// It is useful for catching errors and returning them in a different way. +pub type EndHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + Result::Error>>, + ) -> Result::Error>> + + 'a, +>; + +/// Handles related to post execution after the stack loop is finished. +pub struct PostExecutionHandler<'a, EXT, DB: Database> { + /// Reimburse the caller with ethereum it didn't spent. + pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, + /// Reward the beneficiary with caller fee. + pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, + /// Main return handle, returns the output of the transact. + pub output: OutputHandle<'a, EXT, DB>, + /// End handle. + pub end: EndHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> PostExecutionHandler<'a, EXT, DB> { + /// Creates mainnet MainHandles. + pub fn new() -> Self { + Self { + reimburse_caller: Arc::new(mainnet::reimburse_caller::), + reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), + output: Arc::new(mainnet::output::), + end: Arc::new(mainnet::end::), + } + } +} + +impl<'a, EXT, DB: Database> PostExecutionHandler<'a, EXT, DB> { + /// Reimburse the caller with gas that were not spend. + pub fn reimburse_caller( + &self, + context: &mut Context, + gas: &Gas, + ) -> Result<(), EVMError> { + (self.reimburse_caller)(context, gas) + } + /// Reward beneficiary + pub fn reward_beneficiary( + &self, + context: &mut Context, + gas: &Gas, + ) -> Result<(), EVMError> { + (self.reward_beneficiary)(context, gas) + } + + /// Returns the output of transaction. + pub fn output( + &self, + context: &mut Context, + call_result: InstructionResult, + output: Output, + gas: &Gas, + ) -> Result> { + (self.output)(context, call_result, output, gas) + } + + /// End handler. + pub fn end( + &self, + context: &mut Context, + end_output: Result>, + ) -> Result> { + (self.end)(context, end_output) + } +} diff --git a/crates/revm/src/handler/handle_types/pre_execution.rs b/crates/revm/src/handler/handle_types/pre_execution.rs new file mode 100644 index 0000000000..0872a22e4d --- /dev/null +++ b/crates/revm/src/handler/handle_types/pre_execution.rs @@ -0,0 +1,59 @@ +// Includes. +use crate::{ + handler::mainnet, + primitives::{db::Database, EVMError, EVMResultGeneric, Spec}, + Context, +}; +use alloc::sync::Arc; +use revm_precompile::Precompiles; + +/// Loads precompiles into Evm +pub type LoadPrecompilesHandle<'a> = Arc Precompiles + 'a>; + +/// Load access list accounts and beneficiary. +/// There is no need to load Caller as it is assumed that +/// it will be loaded in DeductCallerHandle. +pub type LoadAccountsHandle<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + +/// Deduct the caller to its limit. +pub type DeductCallerHandle<'a, EXT, DB> = + Arc) -> EVMResultGeneric<(), ::Error> + 'a>; + +/// Handles related to pre execution before the stack loop is started. +pub struct PreExecutionHandler<'a, EXT, DB: Database> { + /// Load precompiles + pub load_precompiles: LoadPrecompilesHandle<'a>, + /// Main load handle + pub load_accounts: LoadAccountsHandle<'a, EXT, DB>, + /// Deduct max value from the caller. + pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> PreExecutionHandler<'a, EXT, DB> { + /// Creates mainnet MainHandles. + pub fn new() -> Self { + Self { + load_precompiles: Arc::new(mainnet::load_precompiles::), + load_accounts: Arc::new(mainnet::load::), + deduct_caller: Arc::new(mainnet::deduct_caller::), + } + } +} + +impl<'a, EXT, DB: Database> PreExecutionHandler<'a, EXT, DB> { + /// Deduct caller to its limit. + pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { + (self.deduct_caller)(context) + } + + /// Main load + pub fn load_accounts(&self, context: &mut Context) -> Result<(), EVMError> { + (self.load_accounts)(context) + } + + /// Load precompiles + pub fn load_precompiles(&self) -> Precompiles { + (self.load_precompiles)() + } +} diff --git a/crates/revm/src/handler/handle_types/validation.rs b/crates/revm/src/handler/handle_types/validation.rs new file mode 100644 index 0000000000..8516960d28 --- /dev/null +++ b/crates/revm/src/handler/handle_types/validation.rs @@ -0,0 +1,60 @@ +use crate::{ + handler::mainnet, + primitives::{db::Database, EVMError, Env, Spec}, + Context, +}; +use alloc::sync::Arc; + +/// Handle that validates env. +pub type ValidateEnvHandle<'a, DB> = + Arc Result<(), EVMError<::Error>> + 'a>; + +/// Handle that validates transaction environment against the state. +/// Second parametar is initial gas. +pub type ValidateTxEnvAgainstState<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + +/// Initial gas calculation handle +pub type ValidateInitialTxGasHandle<'a, DB> = + Arc Result::Error>> + 'a>; + +/// Handles related to validation. +pub struct ValidationHandler<'a, EXT, DB: Database> { + /// Validate and calculate initial transaction gas. + pub initial_tx_gas: ValidateInitialTxGasHandle<'a, DB>, + /// Validate transactions against state data. + pub tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, + /// Validate Env. + pub env: ValidateEnvHandle<'a, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> ValidationHandler<'a, EXT, DB> { + /// Create new ValidationHandles + pub fn new() -> Self { + Self { + initial_tx_gas: Arc::new(mainnet::validate_initial_tx_gas::), + env: Arc::new(mainnet::validate_env::), + tx_against_state: Arc::new(mainnet::validate_tx_against_state::), + } + } +} + +impl<'a, EXT, DB: Database> ValidationHandler<'a, EXT, DB> { + /// Validate env. + pub fn env(&self, env: &Env) -> Result<(), EVMError> { + (self.env)(env) + } + + /// Initial gas + pub fn initial_tx_gas(&self, env: &Env) -> Result> { + (self.initial_tx_gas)(env) + } + + /// Validate ttansaction against the state. + pub fn tx_against_state( + &self, + context: &mut Context, + ) -> Result<(), EVMError> { + (self.tx_against_state)(context) + } +} diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index c39293391f..cf92067938 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,210 +1,14 @@ //! Mainnet related handlers. -use crate::{ - interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, - primitives::{ - db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, - U256, - }, - EvmContext, +mod execution_loop; +mod post_execution; +mod pre_execution; +mod validation; + +pub use execution_loop::{ + create_first_frame, first_frame_return, frame_return, frame_return_with_refund_flag, sub_call, + sub_create, }; - -/// Handle output of the transaction -#[inline] -pub fn handle_call_return( - env: &Env, - call_result: InstructionResult, - returned_gas: Gas, -) -> Gas { - let tx_gas_limit = env.tx.gas_limit; - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - let mut gas = Gas::new(tx_gas_limit); - gas.record_cost(tx_gas_limit); - - match call_result { - return_ok!() => { - gas.erase_cost(returned_gas.remaining()); - gas.record_refund(returned_gas.refunded()); - } - return_revert!() => { - gas.erase_cost(returned_gas.remaining()); - } - _ => {} - } - gas -} - -#[inline] -pub fn handle_reimburse_caller( - context: &mut EvmContext<'_, DB>, - gas: &Gas, -) -> Result<(), EVMError> { - let caller = context.env.tx.caller; - let effective_gas_price = context.env.effective_gas_price(); - - // return balance of not spend gas. - let (caller_account, _) = context - .journaled_state - .load_account(caller, context.db) - .map_err(EVMError::Database)?; - - caller_account.info.balance = caller_account - .info - .balance - .saturating_add(effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64)); - - Ok(()) -} - -/// Reward beneficiary with gas fee. -#[inline] -pub fn reward_beneficiary( - context: &mut EvmContext<'_, DB>, - gas: &Gas, -) -> Result<(), EVMError> { - let beneficiary = context.env.block.coinbase; - let effective_gas_price = context.env.effective_gas_price(); - - // transfer fee to coinbase/beneficiary. - // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. - let coinbase_gas_price = if SPEC::enabled(LONDON) { - effective_gas_price.saturating_sub(context.env.block.basefee) - } else { - effective_gas_price - }; - - let (coinbase_account, _) = context - .journaled_state - .load_account(beneficiary, context.db) - .map_err(EVMError::Database)?; - - coinbase_account.mark_touch(); - coinbase_account.info.balance = coinbase_account - .info - .balance - .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas.refunded() as u64)); - - Ok(()) -} - -/// Calculate gas refund for transaction. -/// -/// If config is set to disable gas refund, it will return 0. -/// -/// If spec is set to london, it will decrease the maximum refund amount to 5th part of -/// gas spend. (Before london it was 2th part of gas spend) -#[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - if env.cfg.is_gas_refund_disabled() { - 0 - } else { - // EIP-3529: Reduction in refunds - let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; - (gas.refunded() as u64).min(gas.spend() / max_refund_quotient) - } -} - -//pub fn main_first_call - -/// Main return handle, returns the output of the transaction. -#[inline] -pub fn main_return( - context: &mut EvmContext<'_, DB>, - call_result: InstructionResult, - output: Output, - gas: &Gas, -) -> Result> { - // used gas with refund calculated. - let gas_refunded = gas.refunded() as u64; - let final_gas_used = gas.spend() - gas_refunded; - - // reset journal and return present state. - let (state, logs) = context.journaled_state.finalize(); - - let result = match call_result.into() { - SuccessOrHalt::Success(reason) => ExecutionResult::Success { - reason, - gas_used: final_gas_used, - gas_refunded, - logs, - output, - }, - SuccessOrHalt::Revert => ExecutionResult::Revert { - gas_used: final_gas_used, - output: match output { - Output::Call(return_value) => return_value, - Output::Create(return_value, _) => return_value, - }, - }, - SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { - reason, - gas_used: final_gas_used, - }, - SuccessOrHalt::FatalExternalError => { - return Err(EVMError::Database(context.error.take().unwrap())); - } - // Only two internal return flags. - SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { - panic!("Internal return flags should remain internal {call_result:?}") - } - }; - - Ok(ResultAndState { result, state }) -} - -/// Mainnet end handle does not change the output. -#[inline] -pub fn end_handle( - _context: &mut EvmContext<'_, DB>, - evm_output: Result>, -) -> Result> { - evm_output -} - -#[cfg(test)] -mod tests { - use revm_interpreter::primitives::CancunSpec; - - use super::*; - - #[test] - fn test_consume_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let mut return_gas = Gas::new(90); - return_gas.record_refund(30); - - let gas = handle_call_return::(&env, InstructionResult::Stop, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 30); - - let gas = handle_call_return::(&env, InstructionResult::Revert, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_revert_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } -} +pub use post_execution::{end, output, reimburse_caller, reward_beneficiary}; +pub use pre_execution::{deduct_caller, deduct_caller_inner, load, load_precompiles}; +pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state}; diff --git a/crates/revm/src/handler/mainnet/execution_loop.rs b/crates/revm/src/handler/mainnet/execution_loop.rs new file mode 100644 index 0000000000..9de9d2e37d --- /dev/null +++ b/crates/revm/src/handler/mainnet/execution_loop.rs @@ -0,0 +1,210 @@ +use crate::{ + db::Database, + interpreter::{ + return_ok, return_revert, CallInputs, CreateInputs, Gas, InstructionResult, + InterpreterResult, SharedMemory, + }, + primitives::{Env, Spec, TransactTo}, + CallStackFrame, Context, FrameData, FrameOrResult, +}; +use alloc::boxed::Box; +use core::ops::Range; + +/// Creates first frame. +#[inline] +pub fn create_first_frame( + context: &mut Context, + gas_limit: u64, +) -> FrameOrResult { + // call inner handling of call/create + match context.evm.env.tx.transact_to { + TransactTo::Call(_) => context.evm.make_call_frame( + &CallInputs::new(&context.evm.env.tx, gas_limit).unwrap(), + 0..0, + ), + TransactTo::Create(_) => context.evm.make_create_frame( + SPEC::SPEC_ID, + &CreateInputs::new(&context.evm.env.tx, gas_limit).unwrap(), + ), + } +} + +/// Helper function called inside [`first_frame_return`] +#[inline] +pub fn frame_return_with_refund_flag( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, + refund_enabled: bool, +) -> Gas { + // Spend the gas limit. Gas is reimbursed when the tx returns successfully. + let mut gas = Gas::new(env.tx.gas_limit); + gas.record_cost(env.tx.gas_limit); + + match call_result { + return_ok!() => { + gas.erase_cost(returned_gas.remaining()); + gas.record_refund(returned_gas.refunded()); + } + return_revert!() => { + gas.erase_cost(returned_gas.remaining()); + } + _ => {} + } + // Calculate gas refund for transaction. + // If config is set to disable gas refund, it will return 0. + // If spec is set to london, it will decrease the maximum refund amount to 5th part of + // gas spend. (Before london it was 2th part of gas spend) + if refund_enabled { + // EIP-3529: Reduction in refunds + gas.set_final_refund::() + }; + + gas +} + +/// Handle output of the transaction +#[inline] +pub fn first_frame_return( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, +) -> Gas { + frame_return_with_refund_flag::(env, call_result, returned_gas, true) +} + +/// Handle frame return. +#[inline] +pub fn frame_return( + context: &mut Context, + child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + result: InterpreterResult, +) -> Option { + match child_stack_frame.frame_data { + FrameData::Create { created_address } => { + let result = context.evm.create_return::( + result, + created_address, + child_stack_frame.checkpoint, + ); + let Some(parent_stack_frame) = parent_stack_frame else { + return Some(result); + }; + parent_stack_frame + .interpreter + .insert_create_output(result, Some(created_address)) + } + FrameData::Call { + return_memory_range, + } => { + let result = context + .evm + .call_return(result, child_stack_frame.checkpoint); + let Some(parent_stack_frame) = parent_stack_frame else { + return Some(result); + }; + + parent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_range, + ) + } + } + None +} + +/// Handle frame sub call. +#[inline] +pub fn sub_call( + context: &mut Context, + inputs: Box, + curent_stack_frame: &mut CallStackFrame, + shared_memory: &mut SharedMemory, + return_memory_offset: Range, +) -> Option> { + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + curent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_offset, + ); + None + } + } +} + +/// Handle frame sub create. +#[inline] +pub fn sub_create( + context: &mut Context, + curent_stack_frame: &mut CallStackFrame, + inputs: Box, +) -> Option> { + match context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + // insert result of the failed creation of create CallStackFrame. + curent_stack_frame + .interpreter + .insert_create_output(result, None); + None + } + } +} + +#[cfg(test)] +mod tests { + use revm_interpreter::primitives::CancunSpec; + + use super::*; + + #[test] + fn test_consume_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = first_frame_return::(&env, InstructionResult::Stop, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + // TODO + #[test] + fn test_consume_gas_with_refund() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let mut return_gas = Gas::new(90); + return_gas.record_refund(30); + + let gas = first_frame_return::(&env, InstructionResult::Stop, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 2); + + let gas = first_frame_return::(&env, InstructionResult::Revert, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_revert_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = first_frame_return::(&env, InstructionResult::Revert, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } +} diff --git a/crates/revm/src/handler/mainnet/post_execution.rs b/crates/revm/src/handler/mainnet/post_execution.rs new file mode 100644 index 0000000000..7f31290e27 --- /dev/null +++ b/crates/revm/src/handler/mainnet/post_execution.rs @@ -0,0 +1,117 @@ +use crate::{ + interpreter::{Gas, InstructionResult, SuccessOrHalt}, + primitives::{ + db::Database, EVMError, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, U256, + }, + Context, +}; + +/// Mainnet end handle does not change the output. +#[inline] +pub fn end( + _context: &mut Context, + evm_output: Result>, +) -> Result> { + evm_output +} + +/// Reward beneficiary with gas fee. +#[inline] +pub fn reward_beneficiary( + context: &mut Context, + gas: &Gas, +) -> Result<(), EVMError> { + let beneficiary = context.evm.env.block.coinbase; + let effective_gas_price = context.evm.env.effective_gas_price(); + + // transfer fee to coinbase/beneficiary. + // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. + let coinbase_gas_price = if SPEC::enabled(LONDON) { + effective_gas_price.saturating_sub(context.evm.env.block.basefee) + } else { + effective_gas_price + }; + + let (coinbase_account, _) = context + .evm + .journaled_state + .load_account(beneficiary, &mut context.evm.db) + .map_err(EVMError::Database)?; + + coinbase_account.mark_touch(); + coinbase_account.info.balance = coinbase_account + .info + .balance + .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas.refunded() as u64)); + + Ok(()) +} + +#[inline] +pub fn reimburse_caller( + context: &mut Context, + gas: &Gas, +) -> Result<(), EVMError> { + let caller = context.evm.env.tx.caller; + let effective_gas_price = context.evm.env.effective_gas_price(); + + // return balance of not spend gas. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + caller_account.info.balance = caller_account + .info + .balance + .saturating_add(effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64)); + + Ok(()) +} + +/// Main return handle, returns the output of the transaction. +#[inline] +pub fn output( + context: &mut Context, + call_result: InstructionResult, + output: Output, + gas: &Gas, +) -> Result> { + // used gas with refund calculated. + let gas_refunded = gas.refunded() as u64; + let final_gas_used = gas.spend() - gas_refunded; + + // reset journal and return present state. + let (state, logs) = context.evm.journaled_state.finalize(); + + let result = match call_result.into() { + SuccessOrHalt::Success(reason) => ExecutionResult::Success { + reason, + gas_used: final_gas_used, + gas_refunded, + logs, + output, + }, + SuccessOrHalt::Revert => ExecutionResult::Revert { + gas_used: final_gas_used, + output: match output { + Output::Call(return_value) => return_value, + Output::Create(return_value, _) => return_value, + }, + }, + SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { + reason, + gas_used: final_gas_used, + }, + SuccessOrHalt::FatalExternalError => { + return Err(EVMError::Database(context.evm.error.take().unwrap())); + } + // Only two internal return flags. + SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { + panic!("Internal return flags should remain internal {call_result:?}") + } + }; + + Ok(ResultAndState { result, state }) +} diff --git a/crates/revm/src/handler/mainnet/pre_execution.rs b/crates/revm/src/handler/mainnet/pre_execution.rs new file mode 100644 index 0000000000..84bd2952f8 --- /dev/null +++ b/crates/revm/src/handler/mainnet/pre_execution.rs @@ -0,0 +1,96 @@ +//! Handles related to the main function of the EVM. +//! +//! They handle initial setup of the EVM, call loop and the final return of the EVM + +use crate::{ + precompile::{PrecompileSpecId, Precompiles}, + primitives::{ + db::Database, + Account, EVMError, Env, Spec, + SpecId::{CANCUN, SHANGHAI}, + TransactTo, U256, + }, + Context, +}; + +/// Main precompile load +#[inline] +pub fn load_precompiles() -> Precompiles { + Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone() +} + +/// Main load handle +#[inline] +pub fn load( + context: &mut Context, +) -> Result<(), EVMError> { + // set journaling state flag. + context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID); + + // the L1-cost fee is only computed for Optimism non-deposit transactions. + #[cfg(feature = "optimism")] + if context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_none() { + let l1_block_info = crate::optimism::L1BlockInfo::try_fetch(&mut context.evm.db) + .map_err(EVMError::Database)?; + + // storage l1 block info for later use. + context.evm.l1_block_info = Some(l1_block_info); + } + + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if SPEC::enabled(SHANGHAI) { + context + .evm + .journaled_state + .initial_account_load(context.evm.env.block.coinbase, &[], &mut context.evm.db) + .map_err(EVMError::Database)?; + } + + context.evm.load_access_list()?; + Ok(()) +} + +/// Helper function that deducts the caller balance. +#[inline] +pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price()); + + // EIP-4844 + if SPEC::enabled(CANCUN) { + let data_fee = env.calc_data_fee().expect("already checked"); + gas_cost = gas_cost.saturating_add(data_fee); + } + + // set new caller account balance. + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + + // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. + if matches!(env.tx.transact_to, TransactTo::Call(_)) { + // Nonce is already checked + caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + } + + // touch account so we know it is changed. + caller_account.mark_touch(); +} + +/// Deducts the caller balance to the transaction limit. +#[inline] +pub fn deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + // deduct gas cost from caller's account. + deduct_caller_inner::(caller_account, &context.evm.env); + + Ok(()) +} diff --git a/crates/revm/src/handler/mainnet/validation.rs b/crates/revm/src/handler/mainnet/validation.rs new file mode 100644 index 0000000000..aef1d032c5 --- /dev/null +++ b/crates/revm/src/handler/mainnet/validation.rs @@ -0,0 +1,52 @@ +use revm_interpreter::gas; + +use crate::{ + primitives::{db::Database, EVMError, Env, InvalidTransaction, Spec}, + Context, +}; + +/// Validate environment for the mainnet. +pub fn validate_env(env: &Env) -> Result<(), EVMError> { + // Important: validate block before tx. + env.validate_block_env::()?; + env.validate_tx::()?; + Ok(()) +} + +/// Validates transaction against the state. +pub fn validate_tx_against_state( + context: &mut Context, +) -> Result<(), EVMError> { + // load acc + let tx_caller = context.evm.env.tx.caller; + let (caller_account, _) = context + .evm + .journaled_state + .load_account(tx_caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + context + .evm + .env + .validate_tx_against_state::(caller_account) + .map_err(EVMError::Transaction)?; + + Ok(()) +} + +/// Validate initial transaction gas. +pub fn validate_initial_tx_gas( + env: &Env, +) -> Result> { + let input = &env.tx.data; + let is_create = env.tx.transact_to.is_create(); + let access_list = &env.tx.access_list; + + let initial_gas_spend = gas::validate_initial_tx_gas::(input, is_create, access_list); + + // Additional check to see if limit is big enough to cover initial gas. + if initial_gas_spend > env.tx.gas_limit { + return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); + } + Ok(initial_gas_spend) +} diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs new file mode 100644 index 0000000000..2a5e9df89e --- /dev/null +++ b/crates/revm/src/handler/register.rs @@ -0,0 +1,31 @@ +use crate::{db::Database, handler::Handler, interpreter::opcode::InstructionTables, Evm}; +use alloc::boxed::Box; + +/// EVM Handler +pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>; + +/// EVM Instruction Tables +pub type EvmInstructionTables<'a, EXT, DB> = InstructionTables<'a, Evm<'a, EXT, DB>>; + +// Handle register +pub type HandleRegister<'a, EXT, DB> = fn(&mut EvmHandler<'a, EXT, DB>); + +// Boxed handle register +pub type HandleRegisterBox<'a, EXT, DB> = Box)>; + +pub enum HandleRegisters<'a, EXT, DB: Database> { + /// Plain function register + Plain(HandleRegister<'a, EXT, DB>), + /// Boxed function register. + Box(HandleRegisterBox<'a, EXT, DB>), +} + +impl<'a, EXT, DB: Database> HandleRegisters<'a, EXT, DB> { + /// Call register function to modify EvmHandler. + pub fn register(&self, handler: &mut EvmHandler<'a, EXT, DB>) { + match self { + HandleRegisters::Plain(f) => f(handler), + HandleRegisters::Box(f) => f(handler), + } + } +} diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 0f9435fa85..60ce286ce6 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -2,7 +2,7 @@ use core::ops::Range; use crate::{ interpreter::{CallInputs, CreateInputs, Interpreter}, - primitives::{db::Database, Address, Bytes, B256, U256}, + primitives::{db::Database, Address, Log, U256}, EvmContext, }; use auto_impl::auto_impl; @@ -12,10 +12,12 @@ mod customprinter; #[cfg(all(feature = "std", feature = "serde"))] mod eip3155; mod gas; -mod instruction; +mod handler_register; mod noop; -pub use instruction::inspector_instruction; +// Exports. + +pub use handler_register::{inspector_handle_register, inspector_instruction, GetInspector}; use revm_interpreter::InterpreterResult; /// [Inspector] implementations. pub mod inspectors { @@ -35,7 +37,7 @@ pub trait Inspector { /// If `interp.instruction_result` is set to anything other than [crate::interpreter::InstructionResult::Continue] then the execution of the interpreter /// is skipped. #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } @@ -49,43 +51,35 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } - /// Called when a log is emitted. - #[inline] - fn log( - &mut self, - context: &mut EvmContext<'_, DB>, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - let _ = context; - let _ = address; - let _ = topics; - let _ = data; - } - /// Called after `step` when the instruction has been executed. /// /// Setting `interp.instruction_result` to anything other than [crate::interpreter::InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } + /// Called when a log is emitted. + #[inline] + fn log(&mut self, context: &mut EvmContext, log: &Log) { + let _ = context; + let _ = log; + } + /// Called whenever a call to a contract is about to start. /// /// InstructionResulting anything other than [crate::interpreter::InstructionResult::Continue] overrides the result of the call. #[inline] fn call( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { let _ = context; @@ -100,7 +94,7 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { let _ = context; @@ -113,7 +107,7 @@ pub trait Inspector { #[inline] fn create( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { let _ = context; @@ -128,7 +122,7 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 9ea86d5f9b..fc65544b56 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -7,7 +7,7 @@ use crate::{ inspectors::GasInspector, interpreter::{opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{Address, U256}, - Database, EvmContext, Inspector, + Database, EvmContext, GetInspector, Inspector, }; /// Custom print [Inspector], it has step level information of execution. @@ -18,14 +18,20 @@ pub struct CustomPrintTracer { gas_inspector: GasInspector, } +impl<'a, DB: Database> GetInspector<'a, DB> for CustomPrintTracer { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} + impl Inspector for CustomPrintTracer { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let opcode = interp.current_opcode(); let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; @@ -50,13 +56,13 @@ impl Inspector for CustomPrintTracer { self.gas_inspector.step(interp, context); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); } fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { self.gas_inspector.call_end(context, result) @@ -64,7 +70,7 @@ impl Inspector for CustomPrintTracer { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -73,7 +79,7 @@ impl Inspector for CustomPrintTracer { fn call( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { println!( @@ -89,7 +95,7 @@ impl Inspector for CustomPrintTracer { fn create( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { println!( @@ -109,31 +115,42 @@ impl Inspector for CustomPrintTracer { #[cfg(test)] mod test { + use crate::{ + inspector_handle_register, + inspectors::CustomPrintTracer, + primitives::{address, bytes, SpecId}, + Evm, InMemoryDB, + }; #[test] - #[cfg(not(feature = "optimism"))] fn gas_calculation_underflow() { - use crate::primitives::{address, bytes}; + let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); // https://github.com/bluealloy/revm/issues/277 // checks this use case - let mut evm = crate::new(); - let mut database = crate::InMemoryDB::default(); - let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); - - let acc_info = crate::primitives::AccountInfo { - balance: "0x100c5d668240db8e00".parse().unwrap(), - code_hash: crate::primitives::keccak256(&code), - code: Some(crate::primitives::Bytecode::new_raw(code.clone())), - nonce: 1, - }; - let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); - database.insert_account_info(callee, acc_info); - evm.database(database); - evm.env.tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); - evm.env.tx.transact_to = crate::primitives::TransactTo::Call(callee); - evm.env.tx.data = crate::primitives::Bytes::new(); - evm.env.tx.value = crate::primitives::U256::ZERO; - let _ = evm.inspect_commit(super::CustomPrintTracer::default()); + let mut evm = Evm::builder() + .with_db(InMemoryDB::default()) + .modify_db(|db| { + let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); + let info = crate::primitives::AccountInfo { + balance: "0x100c5d668240db8e00".parse().unwrap(), + code_hash: crate::primitives::keccak256(&code), + code: Some(crate::primitives::Bytecode::new_raw(code.clone())), + nonce: 1, + }; + db.insert_account_info(callee, info); + }) + .modify_tx_env(|tx| { + tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); + tx.transact_to = crate::primitives::TransactTo::Call(callee); + tx.data = crate::primitives::Bytes::new(); + tx.value = crate::primitives::U256::ZERO; + }) + .with_external_context(CustomPrintTracer::default()) + .spec_id(SpecId::BERLIN) + .append_handler_register(inspector_handle_register) + .build(); + + evm.transact().expect("Transaction to work"); } } diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index a10dbf265a..22269082a3 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -2,7 +2,7 @@ use crate::{ inspectors::GasInspector, interpreter::{opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{db::Database, hex, Address, U256}, - EvmContext, Inspector, + EvmContext, GetInspector, Inspector, }; use core::ops::Range; use serde_json::json; @@ -26,6 +26,19 @@ pub struct TracerEip3155 { skip: bool, } +impl TracerEip3155 { + /// Sets the writer to use for the output. + pub fn set_writer(&mut self, writer: Box) { + self.output = writer; + } +} + +impl<'a, DB: Database> GetInspector<'a, DB> for TracerEip3155 { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} + impl TracerEip3155 { pub fn new(output: Box, trace_mem: bool, trace_return_data: bool) -> Self { Self { @@ -44,13 +57,13 @@ impl TracerEip3155 { } impl Inspector for TracerEip3155 { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step(interp, context); self.stack = interp.stack.data().clone(); self.pc = interp.program_counter(); @@ -59,7 +72,7 @@ impl Inspector for TracerEip3155 { self.gas = interp.gas.remaining(); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); if self.skip { self.skip = false; @@ -74,7 +87,7 @@ impl Inspector for TracerEip3155 { fn call( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, _inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { None @@ -82,7 +95,7 @@ impl Inspector for TracerEip3155 { fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { let result = self.gas_inspector.call_end(context, result); @@ -103,7 +116,7 @@ impl Inspector for TracerEip3155 { fn create( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, _inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { None @@ -111,7 +124,7 @@ impl Inspector for TracerEip3155 { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 3c5462265e..f1266177e5 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -28,7 +28,7 @@ impl Inspector for GasInspector { fn initialize_interp( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, ) { self.gas_remaining = interp.gas.limit(); } @@ -36,7 +36,7 @@ impl Inspector for GasInspector { fn step_end( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, ) { let last_gas = core::mem::replace(&mut self.gas_remaining, interp.gas.remaining()); self.last_gas_cost = last_gas.saturating_sub(self.last_gas_cost); @@ -44,7 +44,7 @@ impl Inspector for GasInspector { fn call_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, mut result: InterpreterResult, ) -> InterpreterResult { if result.result.is_error() { @@ -56,7 +56,7 @@ impl Inspector for GasInspector { fn create_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -66,14 +66,14 @@ impl Inspector for GasInspector { #[cfg(test)] mod tests { - use core::ops::Range; - use crate::{ + inspector::GetInspector, inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, - primitives::{Address, Bytes, B256}, + primitives::{Address, Log}, Database, EvmContext, Inspector, }; + use core::ops::Range; #[derive(Default, Debug)] struct StackInspector { @@ -82,31 +82,27 @@ mod tests { gas_remaining_steps: Vec<(usize, u64)>, } + impl GetInspector<'_, DB> for StackInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } + } + impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext<'_, DB>, - ) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); } - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.pc = interp.program_counter(); self.gas_inspector.step(interp, context); } - fn log( - &mut self, - context: &mut EvmContext<'_, DB>, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - self.gas_inspector.log(context, address, topics, data); + fn log(&mut self, context: &mut EvmContext, log: &Log) { + self.gas_inspector.log(context, log); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); self.gas_remaining_steps .push((self.pc, self.gas_inspector.gas_remaining())); @@ -114,7 +110,7 @@ mod tests { fn call( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, call: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { self.gas_inspector.call(context, call) @@ -122,7 +118,7 @@ mod tests { fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { self.gas_inspector.call_end(context, result) @@ -130,7 +126,7 @@ mod tests { fn create( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, call: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { self.gas_inspector.create(context, call); @@ -139,7 +135,7 @@ mod tests { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -148,12 +144,13 @@ mod tests { } #[test] - #[cfg(not(feature = "optimism"))] fn test_gas_inspector() { use crate::{ db::BenchmarkDB, + inspector::inspector_handle_register, interpreter::opcode, primitives::{address, Bytecode, Bytes, TransactTo}, + Evm, }; let contract_data: Bytes = Bytes::from(vec![ @@ -173,15 +170,23 @@ mod tests { ]); let bytecode = Bytecode::new_raw(contract_data); - let mut evm = crate::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = - TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.gas_limit = 21100; - - let mut inspector = StackInspector::default(); - evm.inspect(&mut inspector).unwrap(); + let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .with_external_context(StackInspector::default()) + .modify_tx_env(|tx| { + tx.clear(); + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = + TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.gas_limit = 21100; + }) + .append_handler_register(inspector_handle_register) + .build(); + + // run evm. + evm.transact().unwrap(); + + let inspector = evm.into_context().external; // starting from 100gas let steps = vec![ diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs new file mode 100644 index 0000000000..d46d52a8fe --- /dev/null +++ b/crates/revm/src/inspector/handler_register.rs @@ -0,0 +1,436 @@ +use crate::{ + db::Database, + handler::register::{EvmHandler, EvmInstructionTables}, + interpreter::{ + opcode, opcode::BoxedInstruction, CallInputs, InstructionResult, Interpreter, + InterpreterResult, + }, + primitives::TransactTo, + CallStackFrame, Evm, FrameData, FrameOrResult, Inspector, JournalEntry, +}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use revm_interpreter::CreateInputs; + +pub trait GetInspector<'a, DB: Database> { + fn get_inspector(&mut self) -> &mut dyn Inspector; +} + +/// Register Inspector handles that interact with Inspector instance. +/// +/// +/// # Note +/// +/// Handles that are overwritten: +/// * SubCreate +/// * SubCall +/// * CreateFirstFrame +/// +/// +/// Few instructions handlers are wrapped twice once for `step` and `step_end` +/// and in case of Logs and Selfdestruct wrapper is wrapped again for the +/// `log` and `selfdestruct` calls. +/// +/// `frame_return` is also wrapped so that Inspector could call `call_end` or `create_end`. +/// +/// `create_first_frame` is also wrapped so that Inspector could call `call` and `crate` on it. +/// While return for first frame is handled by `frame_return`. +pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( + handler: &mut EvmHandler<'a, EXT, DB>, +) { + let spec_id = handler.spec_id; + // Every instruction inside flat table that is going to be wrapped by inspector calls. + let table = handler + .instruction_table + .take() + .expect("Handler must have instruction table"); + let mut table = match table { + EvmInstructionTables::Plain(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + EvmInstructionTables::Boxed(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + }; + + // Register inspector Log instruction. + let mut inspect_log = |index: u8| { + if let Some(i) = table.get_mut(index as usize) { + let old = core::mem::replace(i, Box::new(|_, _| ())); + *i = Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + let old_log_len = host.context.evm.journaled_state.logs.len(); + old(interpreter, host); + // check if log was added. It is possible that revert happened + // cause of gas or stack underflow. + if host.context.evm.journaled_state.logs.len() == old_log_len + 1 { + // clone log. + // TODO decide if we should remove this and leave the comment + // that log can be found as journaled_state. + let last_log = host + .context + .evm + .journaled_state + .logs + .last() + .unwrap() + .clone(); + // call Inspector + host.context + .external + .get_inspector() + .log(&mut host.context.evm, &last_log); + } + }, + ) + } + }; + + inspect_log(opcode::LOG0); + inspect_log(opcode::LOG1); + inspect_log(opcode::LOG2); + inspect_log(opcode::LOG3); + inspect_log(opcode::LOG4); + + // wrap first frame create and main frame return. + handler.execution_loop.create_first_frame = + Arc::new(move |context, gas_limit| -> FrameOrResult { + // call inner handling of call/create + let mut first_frame = match context.evm.env.tx.transact_to { + TransactTo::Call(_) => { + let mut call_inputs = CallInputs::new(&context.evm.env.tx, gas_limit).unwrap(); + // call inspector and return of inspector returns result. + if let Some(output) = context + .external + .get_inspector() + .call(&mut context.evm, &mut call_inputs) + { + return FrameOrResult::Result(output.0); + } + // first call frame does not have return range. + context.evm.make_call_frame(&call_inputs, 0..0) + } + TransactTo::Create(_) => { + let mut create_inputs = + CreateInputs::new(&context.evm.env.tx, gas_limit).unwrap(); + if let Some(output) = context + .external + .get_inspector() + .create(&mut context.evm, &mut create_inputs) + { + return FrameOrResult::Result(output.0); + }; + context.evm.make_create_frame(spec_id, &create_inputs) + } + }; + + // call initialize interpreter from inspector. + if let FrameOrResult::Frame(ref mut frame) = first_frame { + context + .external + .get_inspector() + .initialize_interp(&mut frame.interpreter, &mut context.evm); + } + + first_frame + }); + + // register selfdestruct function. + if let Some(i) = table.get_mut(opcode::SELFDESTRUCT as usize) { + let old = core::mem::replace(i, Box::new(|_, _| ())); + *i = Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + // execute selfdestruct + old(interpreter, host); + // check if selfdestruct was successful and if journal entry is made. + if let Some(JournalEntry::AccountDestroyed { + address, + target, + had_balance, + .. + }) = host + .context + .evm + .journaled_state + .journal + .last() + .unwrap() + .last() + { + host.context.external.get_inspector().selfdestruct( + *address, + *target, + *had_balance, + ); + } + }, + ) + } + + // cast vector to array. + handler.instruction_table = Some(EvmInstructionTables::Boxed( + table.try_into().unwrap_or_else(|_| unreachable!()), + )); + + // handle sub create + handler.execution_loop.sub_create = Arc::new( + move |context, frame, mut inputs| -> Option> { + let inspector = context.external.get_inspector(); + if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { + frame.interpreter.insert_create_output(result, address); + return None; + } + + match context.evm.make_create_frame(spec_id, &inputs) { + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } + FrameOrResult::Result(result) => { + let (result, address) = + inspector.create_end(&mut context.evm, result, frame.created_address()); + // insert result of the failed creation of create CallStackFrame. + frame.interpreter.insert_create_output(result, address); + None + } + } + }, + ); + + // handle sub call + handler.execution_loop.sub_call = Arc::new( + move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { + // inspector handle + let inspector = context.external.get_inspector(); + if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { + frame.interpreter.insert_call_output(memory, result, range); + return None; + } + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } + FrameOrResult::Result(result) => { + // inspector handle + let result = inspector.call_end(&mut context.evm, result); + frame + .interpreter + .insert_call_output(memory, result, return_memory_offset); + None + } + } + }, + ); + + // return frame handle + let old_handle = handler.execution_loop.frame_return.clone(); + handler.execution_loop.frame_return = Arc::new( + move |context, mut child, parent, memory, mut result| -> Option { + let inspector = &mut context.external.get_inspector(); + result = match &mut child.frame_data { + FrameData::Create { created_address } => { + let (result, address) = + inspector.create_end(&mut context.evm, result, Some(*created_address)); + if let Some(address) = address { + *created_address = address; + } + result + } + FrameData::Call { .. } => inspector.call_end(&mut context.evm, result), + }; + old_handle(context, child, parent, memory, result) + }, + ); +} + +/// Outer closure that calls Inspector for every instruction. +pub fn inspector_instruction< + 'a, + INSP: GetInspector<'a, DB>, + DB: Database, + Instruction: Fn(&mut Interpreter, &mut Evm<'a, INSP, DB>) + 'a, +>( + instruction: Instruction, +) -> BoxedInstruction<'a, Evm<'a, INSP, DB>> { + Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, INSP, DB>| { + // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the + // old Inspector behavior. + interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; + + host.context + .external + .get_inspector() + .step(interpreter, &mut host.context.evm); + if interpreter.instruction_result != InstructionResult::Continue { + return; + } + + // return PC to old value + interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; + + // execute instruction. + instruction(interpreter, host); + + host.context + .external + .get_inspector() + .step_end(interpreter, &mut host.context.evm); + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + db::EmptyDB, + inspector::GetInspector, + inspectors::NoOpInspector, + interpreter::{opcode::*, CallInputs, CreateInputs, Interpreter, InterpreterResult}, + primitives::{Address, BerlinSpec}, + Database, Evm, EvmContext, Inspector, + }; + use core::ops::Range; + + #[test] + fn test_make_boxed_instruction_table() { + // test that this pattern builds. + let inst: InstructionTable> = + make_instruction_table::, BerlinSpec>(); + let _test: BoxedInstructionTable<'_, Evm<'_, _, _>> = + make_boxed_instruction_table::<'_, Evm<'_, NoOpInspector, EmptyDB>, BerlinSpec, _>( + inst, + inspector_instruction, + ); + } + + #[derive(Default, Debug)] + struct StackInspector { + initialize_interp_called: bool, + step: u32, + step_end: u32, + call: bool, + call_end: bool, + } + + impl GetInspector<'_, DB> for StackInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } + } + + impl Inspector for StackInspector { + fn initialize_interp(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + if self.initialize_interp_called { + unreachable!("initialize_interp should not be called twice") + } + self.initialize_interp_called = true; + } + + fn step(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + self.step += 1; + } + + fn step_end(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + self.step_end += 1; + } + + fn call( + &mut self, + _context: &mut EvmContext, + _call: &mut CallInputs, + ) -> Option<(InterpreterResult, Range)> { + if self.call { + unreachable!("call should not be called twice") + } + self.call = true; + None + } + + fn call_end( + &mut self, + _context: &mut EvmContext, + result: InterpreterResult, + ) -> InterpreterResult { + if self.call_end { + unreachable!("call_end should not be called twice") + } + self.call_end = true; + result + } + + fn create( + &mut self, + _context: &mut EvmContext, + _call: &mut CreateInputs, + ) -> Option<(InterpreterResult, Option
)> { + None + } + + fn create_end( + &mut self, + _context: &mut EvmContext, + result: InterpreterResult, + address: Option
, + ) -> (InterpreterResult, Option
) { + (result, address) + } + } + + #[test] + fn test_gas_inspector() { + use crate::{ + db::BenchmarkDB, + inspector::inspector_handle_register, + interpreter::opcode, + primitives::{address, Bytecode, Bytes, TransactTo}, + Evm, + }; + + let contract_data: Bytes = Bytes::from(vec![ + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0xb, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::CREATE, + opcode::STOP, + ]); + let bytecode = Bytecode::new_raw(contract_data); + + let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .with_external_context(StackInspector::default()) + .modify_tx_env(|tx| { + tx.clear(); + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = + TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.gas_limit = 21100; + }) + .append_handler_register(inspector_handle_register) + .build(); + + // run evm. + evm.transact().unwrap(); + + let inspector = evm.into_context().external; + + assert_eq!(inspector.step, 6); + assert_eq!(inspector.step_end, 6); + assert!(inspector.initialize_interp_called); + assert!(inspector.call); + assert!(inspector.call_end); + } +} diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs deleted file mode 100644 index b78be6ccea..0000000000 --- a/crates/revm/src/inspector/instruction.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::EVMImpl; -use alloc::boxed::Box; -use revm_interpreter::{ - opcode::{BoxedInstruction, Instruction}, - primitives::{db::Database, Spec}, - InstructionResult, Interpreter, -}; - -/// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction<'a, SPEC: Spec + 'static, DB: Database>( - instruction: Instruction>, -) -> BoxedInstruction<'a, EVMImpl<'a, SPEC, DB>> { - Box::new( - move |interpreter: &mut Interpreter, host: &mut EVMImpl<'a, SPEC, DB>| { - if let Some(inspector) = host.inspector.as_mut() { - // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the - // old Inspector behavior. - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; - - inspector.step(interpreter, &mut host.context); - if interpreter.instruction_result != InstructionResult::Continue { - return; - } - - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; - } - - // execute instruction. - instruction(interpreter, host); - - // step ends - if let Some(inspector) = host.inspector.as_mut() { - inspector.step_end(interpreter, &mut host.context); - } - }, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, EVMImpl}; - - #[test] - fn test_make_boxed_instruction_table() { - // test that this pattern builds. - let inst: InstructionTable> = - make_instruction_table::, BerlinSpec>(); - let _test: BoxedInstructionTable<'_, EVMImpl<'_, BerlinSpec, _>> = - make_boxed_instruction_table::<'_, EVMImpl<'_, BerlinSpec, EmptyDB>, BerlinSpec, _>( - inst, - inspector_instruction, - ); - } -} diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 05d7292fc4..70d32b133b 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -1,7 +1,13 @@ +use super::GetInspector; use crate::{Database, Inspector}; - /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; impl Inspector for NoOpInspector {} + +impl<'a, DB: Database> GetInspector<'a, DB> for NoOpInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 845ff985cb..8aa4d51fd5 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,6 +1,6 @@ use crate::interpreter::{InstructionResult, SelfDestructResult}; use crate::primitives::{ - db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, Log, Spec, SpecId::*, + db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, HashSet, Log, SpecId::*, State, StorageSlot, TransientStorage, KECCAK_EMPTY, PRECOMPILE3, U256, }; use alloc::vec::Vec; @@ -26,26 +26,28 @@ pub struct JournaledState { /// Spec is needed for two things SpuriousDragon's `EIP-161 State clear`, /// and for Cancun's `EIP-6780: SELFDESTRUCT in same transaction` pub spec: SpecId, - /// Precompiles addresses are used to check if loaded address - /// should be considered cold or hot loaded. It is cloned from - /// EvmContext to be directly accessed from JournaledState. + /// Warm loaded addresses are used to check if loaded address + /// should be considered cold or warm loaded when the account + /// is first accessed. /// - /// Note that addresses are sorted. - pub precompile_addresses: Vec
, + /// Note that this not include newly loaded accounts, account and storage + /// is considered warm if it is found in the `State`. + pub warm_preloaded_addresses: HashSet
, } impl JournaledState { /// Create new JournaledState. /// - /// precompile_addresses is used to determine if address is precompile or not. + /// warm_preloaded_addresses is used to determine if address is considered warm loaded. + /// In ordinary case this is precompile or beneficiary. /// /// Note: This function will journal state after Spurious Dragon fork. /// And will not take into account if account is not existing or empty. /// /// # Note /// - /// Precompile addresses should be sorted. - pub fn new(spec: SpecId, precompile_addresses: Vec
) -> JournaledState { + /// + pub fn new(spec: SpecId, warm_preloaded_addresses: HashSet
) -> JournaledState { Self { state: HashMap::new(), transient_storage: TransientStorage::default(), @@ -53,7 +55,7 @@ impl JournaledState { journal: vec![vec![]], depth: 0, spec, - precompile_addresses, + warm_preloaded_addresses, } } @@ -63,6 +65,12 @@ impl JournaledState { &mut self.state } + /// Sets SpecId. + #[inline] + pub fn set_spec_id(&mut self, spec: SpecId) { + self.spec = spec; + } + /// Mark account as touched as only touched accounts will be added to state. /// This is especially important for state clear where touched empty accounts needs to /// be removed from state. @@ -207,11 +215,12 @@ impl JournaledState { /// Panics if the caller is not loaded inside of the EVM state. /// This is should have been done inside `create_inner`. #[inline] - pub fn create_account_checkpoint( + pub fn create_account_checkpoint( &mut self, caller: Address, address: Address, balance: U256, + spec_id: SpecId, ) -> Result { // Enter subroutine let checkpoint = self.checkpoint(); @@ -226,7 +235,7 @@ impl JournaledState { // Account is not precompile. if account.info.code_hash != KECCAK_EMPTY || account.info.nonce != 0 - || self.precompile_addresses.binary_search(&address).is_ok() + || self.warm_preloaded_addresses.contains(&address) { self.checkpoint_revert(checkpoint); return Err(InstructionResult::CreateCollision); @@ -260,7 +269,7 @@ impl JournaledState { account.info.balance = new_balance; // EIP-161: State trie clearing (invariant-preserving alternative) - if SPEC::enabled(SPURIOUS_DRAGON) { + if spec_id.is_enabled_in(SPURIOUS_DRAGON) { // nonce is going to be reset to zero in AccountCreated journal entry. account.info.nonce = 1; } @@ -539,7 +548,7 @@ impl JournaledState { .push(JournalEntry::AccountLoaded { address }); // precompiles are warm loaded so we need to take that into account - let is_cold = self.precompile_addresses.binary_search(&address).is_err(); + let is_cold = !self.warm_preloaded_addresses.contains(&address); (vac.insert(account), is_cold) } diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 3ffd746be6..120a47102a 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -6,56 +6,51 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(all(feature = "with-serde", not(feature = "serde")))] -compile_error!("`with-serde` feature has been renamed to `serde`."); - #[macro_use] extern crate alloc; +// Define modules. + +mod builder; +mod context; + #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; pub mod db; mod evm; -mod evm_context; -mod evm_impl; mod frame; pub mod handler; mod inspector; mod journaled_state; - #[cfg(feature = "optimism")] pub mod optimism; -pub type DummyStateDB = InMemoryDB; +// Export items. + +pub use builder::EvmBuilder; +pub use context::{Context, EvmContext}; #[cfg(feature = "std")] pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; -pub use evm::{new, EVM}; -pub use evm_context::EvmContext; -pub use evm_impl::{new_evm, EVMImpl, Transact, CALL_STACK_LIMIT}; -pub use frame::CallStackFrame; +pub use evm::{Evm, CALL_STACK_LIMIT}; +pub use frame::{CallStackFrame, FrameData, FrameOrResult}; +pub use handler::Handler; +pub use inspector::{ + inspector_handle_register, inspector_instruction, inspectors, GetInspector, Inspector, +}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; +// export Optimism types, helpers, and constants +#[cfg(feature = "optimism")] +pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; -// reexport `revm_precompiles` -#[doc(inline)] -pub use revm_precompile as precompile; +// Reexport libraries -// reexport `revm_interpreter` #[doc(inline)] pub use revm_interpreter as interpreter; - -// reexport `revm_primitives` #[doc(inline)] pub use revm_interpreter::primitives; - -// reexport inspector implementations -pub use inspector::{inspector_instruction, inspectors, Inspector}; - -// export Optimism types, helpers, and constants -#[cfg(feature = "optimism")] -pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; - -pub use handler::Handler; +#[doc(inline)] +pub use revm_precompile as precompile; diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index a2d511b3b3..01276e4f2b 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,170 +1,9 @@ //! Optimism-specific constants, types, and helpers. -use crate::primitives::{address, db::Database, Address, Spec, SpecId, U256}; -use core::ops::Mul; +mod handler_register; +mod l1block; -const ZERO_BYTE_COST: u64 = 4; -const NON_ZERO_BYTE_COST: u64 = 16; - -const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); -const L1_OVERHEAD_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); -const L1_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); - -/// The address of L1 fee recipient. -pub const L1_FEE_RECIPIENT: Address = address!("420000000000000000000000000000000000001A"); - -/// The address of the base fee recipient. -pub const BASE_FEE_RECIPIENT: Address = address!("4200000000000000000000000000000000000019"); - -/// The address of the L1Block contract. -pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); - -/// L1 block info -/// -/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` -/// transaction data. This data is then used to calculate the L1 cost of a transaction. -/// -/// Here is the format of the `setL1BlockValues` transaction data: -/// -/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, -/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) -/// -/// For now, we only care about the fields necessary for L1 cost calculation. -#[derive(Clone, Debug)] -pub struct L1BlockInfo { - /// The base fee of the L1 origin block. - pub l1_base_fee: U256, - /// The current L1 fee overhead. - pub l1_fee_overhead: U256, - /// The current L1 fee scalar. - pub l1_fee_scalar: U256, -} - -impl L1BlockInfo { - /// Try to fetch the L1 block info from the database. - pub fn try_fetch(db: &mut DB) -> Result { - let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; - let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; - let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; - - Ok(L1BlockInfo { - l1_base_fee, - l1_fee_overhead, - l1_fee_scalar, - }) - } - - /// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per non-zero - /// byte and 4 gas per zero byte. - /// - /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to - /// account for the empty signature. - pub fn data_gas(&self, input: &[u8]) -> U256 { - let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { - acc + if *byte == 0x00 { - ZERO_BYTE_COST - } else { - NON_ZERO_BYTE_COST - } - })); - - // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. - if !SPEC::enabled(SpecId::REGOLITH) { - rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); - } - - rollup_data_gas_cost - } - - /// Calculate the gas cost of a transaction based on L1 block data posted on L2 - pub fn calculate_tx_l1_cost(&self, input: &[u8]) -> U256 { - // If the input is not a deposit transaction, the default value is zero. - if input.is_empty() || input.first() == Some(&0x7F) { - return U256::ZERO; - } - - let rollup_data_gas_cost = self.data_gas::(input); - rollup_data_gas_cost - .saturating_add(self.l1_fee_overhead) - .saturating_mul(self.l1_base_fee) - .saturating_mul(self.l1_fee_scalar) - / U256::from(1_000_000) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::primitives::{bytes, specification::*}; - - #[test] - fn test_data_gas_non_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFACADE = 6 nibbles = 3 bytes - // 0xFACADE = 1111 1010 . 1100 1010 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 - // gas cost = 3 * 16 + 68 * 16 = 1136 - let input = bytes!("FACADE"); - let bedrock_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(bedrock_data_gas, U256::from(1136)); - - // Regolith has no added 68 non zero bytes - // gas cost = 3 * 16 = 48 - let regolith_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(regolith_data_gas, U256::from(48)); - } - - #[test] - fn test_data_gas_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFA00CA00DE = 10 nibbles = 5 bytes - // 0xFA00CA00DE = 1111 1010 . 0000 0000 . 1100 1010 . 0000 0000 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 - // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 - let input = bytes!("FA00CA00DE"); - let bedrock_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(bedrock_data_gas, U256::from(1144)); - - // Regolith has no added 68 non zero bytes - // gas cost = 3 * 16 + 2 * 4 = 56 - let regolith_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(regolith_data_gas, U256::from(56)); - } - - #[test] - fn test_calculate_tx_l1_cost() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: U256::from(1_000), - l1_fee_scalar: U256::from(1_000), - }; - - let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input); - assert_eq!(gas_cost, U256::from(1048)); - - // Zero rollup data gas cost should result in zero - let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input); - assert_eq!(gas_cost, U256::ZERO); - - // Deposit transactions with the EIP-2718 type of 0x7F should result in zero - let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input); - assert_eq!(gas_cost, U256::ZERO); - } -} +pub use handler_register::{ + deduct_caller, end, handle_call_return, optimism_handle_register, output, reward_beneficiary, +}; +pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/optimism/handler_register.rs similarity index 51% rename from crates/revm/src/handler/optimism.rs rename to crates/revm/src/optimism/handler_register.rs index cbb248b3f7..e3ff387fda 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -1,17 +1,33 @@ //! Handler related to Optimism chain -use super::mainnet; use crate::{ + handler::{ + mainnet::{self, deduct_caller_inner}, + register::EvmHandler, + }, interpreter::{return_ok, return_revert, Gas, InstructionResult}, optimism, primitives::{ - db::Database, Account, EVMError, Env, ExecutionResult, HaltReason, HashMap, - InvalidTransaction, Output, ResultAndState, Spec, SpecId::REGOLITH, U256, + db::Database, spec_to_generic, Account, EVMError, Env, ExecutionResult, HaltReason, + HashMap, InvalidTransaction, Output, ResultAndState, Spec, SpecId, SpecId::REGOLITH, U256, }, - EvmContext, + Context, }; +use alloc::sync::Arc; use core::ops::Mul; +pub fn optimism_handle_register(handler: &mut EvmHandler<'_, EXT, DB>) { + spec_to_generic!(handler.spec_id, { + // Refund is calculated differently then mainnet. + handler.execution_loop.first_frame_return = Arc::new(handle_call_return::); + // we reimburse caller the same was as in mainnet. + handler.post_execution.reward_beneficiary = Arc::new(reward_beneficiary::); + // In case of halt of deposit transaction return Error. + handler.post_execution.output = Arc::new(output::); + handler.post_execution.end = Arc::new(end::); + }); +} + /// Handle output of the transaction #[inline] pub fn handle_call_return( @@ -73,53 +89,97 @@ pub fn handle_call_return( } _ => {} } + // Prior to Regolith, deposit transactions did not receive gas refunds. + if !is_deposit && SPEC::enabled(REGOLITH) { + gas.set_final_refund::() + } + gas } +/// Deduct max balance from caller #[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - let is_deposit = env.cfg.optimism && env.tx.optimism.source_hash.is_some(); +pub fn deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; - // Prior to Regolith, deposit transactions did not receive gas refunds. - let is_gas_refund_disabled = env.cfg.optimism && is_deposit && !SPEC::enabled(REGOLITH); - if is_gas_refund_disabled { - 0 - } else { - mainnet::calculate_gas_refund::(env, gas) + // If the transaction is a deposit with a `mint` value, add the mint value + // in wei to the caller's balance. This should be persisted to the database + // prior to the rest of execution. + if let Some(mint) = context.evm.env.tx.optimism.mint { + caller_account.info.balance += U256::from(mint); } + + // We deduct caller max balance after minting and before deducing the + // l1 cost, max values is already checked in pre_validate but l1 cost wasn't. + deduct_caller_inner::(caller_account, &context.evm.env); + + // If the transaction is not a deposit transaction, subtract the L1 data fee from the + // caller's balance directly after minting the requested amount of ETH. + if context.evm.env.tx.optimism.source_hash.is_none() { + // get envelope + let Some(enveloped_tx) = context.evm.env.tx.optimism.enveloped_tx.clone() else { + panic!("[OPTIMISM] Failed to load enveloped transaction."); + }; + + let tx_l1_cost = context + .evm + .l1_block_info + .as_ref() + .expect("L1BlockInfo should be loaded") + .calculate_tx_l1_cost(&enveloped_tx, SPEC::SPEC_ID); + if tx_l1_cost.gt(&caller_account.info.balance) { + return Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: tx_l1_cost.into(), + balance: caller_account.info.balance.into(), + }, + )); + } + caller_account.info.balance = caller_account.info.balance.saturating_sub(tx_l1_cost); + } + Ok(()) } /// Reward beneficiary with gas fee. #[inline] -pub fn reward_beneficiary( - context: &mut EvmContext<'_, DB>, +pub fn reward_beneficiary( + context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { - let is_deposit = context.env.cfg.optimism && context.env.tx.optimism.source_hash.is_some(); - let disable_coinbase_tip = context.env.cfg.optimism && is_deposit; + let is_deposit = + context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_some(); + let disable_coinbase_tip = context.evm.env.cfg.optimism && is_deposit; // transfer fee to coinbase/beneficiary. if !disable_coinbase_tip { - mainnet::reward_beneficiary::(context, gas)?; + mainnet::reward_beneficiary::(context, gas)?; } - if context.env.cfg.optimism && !is_deposit { + if context.evm.env.cfg.optimism && !is_deposit { // If the transaction is not a deposit transaction, fees are paid out // to both the Base Fee Vault as well as the L1 Fee Vault. - let Some(l1_block_info) = context.l1_block_info.clone() else { + let Some(l1_block_info) = context.evm.l1_block_info.clone() else { panic!("[OPTIMISM] Failed to load L1 block information."); }; - let Some(enveloped_tx) = &context.env.tx.optimism.enveloped_tx else { + let Some(enveloped_tx) = &context.evm.env.tx.optimism.enveloped_tx else { panic!("[OPTIMISM] Failed to load enveloped transaction."); }; - let l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); + let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, SPEC::SPEC_ID); // Send the L1 cost of the transaction to the L1 Fee Vault. let Ok((l1_fee_vault_account, _)) = context + .evm .journaled_state - .load_account(optimism::L1_FEE_RECIPIENT, context.db) + .load_account(optimism::L1_FEE_RECIPIENT, &mut context.evm.db) else { panic!("[OPTIMISM] Failed to load L1 Fee Vault account"); }; @@ -128,13 +188,15 @@ pub fn reward_beneficiary( // Send the base fee of the transaction to the Base Fee Vault. let Ok((base_fee_vault_account, _)) = context + .evm .journaled_state - .load_account(optimism::BASE_FEE_RECIPIENT, context.db) + .load_account(optimism::BASE_FEE_RECIPIENT, &mut context.evm.db) else { panic!("[OPTIMISM] Failed to load Base Fee Vault account"); }; base_fee_vault_account.mark_touch(); base_fee_vault_account.info.balance += context + .evm .env .block .basefee @@ -145,20 +207,20 @@ pub fn reward_beneficiary( /// Main return handle, returns the output of the transaction. #[inline] -pub fn main_return( - context: &mut EvmContext<'_, DB>, +pub fn output( + context: &mut Context, call_result: InstructionResult, output: Output, gas: &Gas, ) -> Result> { - let result = mainnet::main_return::(context, call_result, output, gas)?; + let result = mainnet::output::(context, call_result, output, gas)?; if result.result.is_halt() { - // Post-regolith, if the transaction is a deposit transaction and it haults, + // Post-regolith, if the transaction is a deposit transaction and it halts, // we bubble up to the global return handler. The mint value will be persisted // and the caller nonce will be incremented there. - let is_deposit = context.env.tx.optimism.source_hash.is_some(); - let optimism_regolith = context.env.cfg.optimism && SPEC::enabled(REGOLITH); + let is_deposit = context.evm.env.tx.optimism.source_hash.is_some(); + let optimism_regolith = context.evm.env.cfg.optimism && SPEC::enabled(REGOLITH); if is_deposit && optimism_regolith { return Err(EVMError::Transaction( InvalidTransaction::HaltedDepositPostRegolith, @@ -170,14 +232,14 @@ pub fn main_return( /// Optimism end handle changes output if the transaction is a deposit transaction. /// Deposit transaction can't be reverted and is always successful. #[inline] -pub fn end_handle( - context: &mut EvmContext<'_, DB>, +pub fn end( + context: &mut Context, evm_output: Result>, ) -> Result> { evm_output.or_else(|err| { if matches!(err, EVMError::Transaction(_)) - && context.env().cfg.optimism - && context.env().tx.optimism.source_hash.is_some() + && context.evm.env().cfg.optimism + && context.evm.env().tx.optimism.source_hash.is_some() { // If the transaction is a deposit transaction and it failed // for any reason, the caller nonce must be bumped, and the @@ -185,13 +247,14 @@ pub fn end_handle( // also returned as a special Halt variant so that consumers can more // easily distinguish between a failed deposit and a failed // normal transaction. - let caller = context.env().tx.caller; + let caller = context.evm.env().tx.caller; // Increment sender nonce and account balance for the mint amount. Deposits // always persist the mint amount, even if the transaction fails. let account = { let mut acc = Account::from( context + .evm .db .basic(caller) .unwrap_or_default() @@ -201,7 +264,7 @@ pub fn end_handle( acc.info.balance = acc .info .balance - .saturating_add(U256::from(context.env().tx.optimism.mint.unwrap_or(0))); + .saturating_add(U256::from(context.evm.env().tx.optimism.mint.unwrap_or(0))); acc.mark_touch(); acc }; @@ -212,13 +275,14 @@ pub fn end_handle( // of the transaction for non system transactions and 0 for system // transactions. let is_system_tx = context + .evm .env() .tx .optimism .is_system_transaction .unwrap_or(false); let gas_used = if SPEC::enabled(REGOLITH) || !is_system_tx { - context.env().tx.gas_limit + context.evm.env().tx.gas_limit } else { 0 }; @@ -238,9 +302,12 @@ pub fn end_handle( #[cfg(test)] mod tests { - use crate::primitives::{BedrockSpec, RegolithSpec, B256}; - use super::*; + use crate::{ + db::InMemoryDB, + primitives::{bytes, state::AccountInfo, Address, BedrockSpec, Env, RegolithSpec, B256}, + L1BlockInfo, + }; #[test] fn test_revert_gas() { @@ -315,4 +382,134 @@ mod tests { assert_eq!(gas.spend(), 100); assert_eq!(gas.refunded(), 0); } + + #[test] + fn test_commit_mint_value() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(1000), + ..Default::default() + }, + ); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // Enveloped needs to be some but it will deduce zero fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("")); + // added mint value is 10. + context.evm.env.tx.optimism.mint = Some(10); + + deduct_caller::(&mut context).unwrap(); + + // Check the account balance is updated. + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .unwrap(); + assert_eq!(account.info.balance, U256::from(1010)); + } + + #[test] + fn test_remove_l1_cost_non_deposit() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(1000), + ..Default::default() + }, + ); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + // added mint value is 10. + context.evm.env.tx.optimism.mint = Some(10); + // Putting source_hash to some makes it a deposit transaction. + // so enveloped_tx gas cost is ignored. + context.evm.env.tx.optimism.source_hash = Some(B256::ZERO); + + deduct_caller::(&mut context).unwrap(); + + // Check the account balance is updated. + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .unwrap(); + assert_eq!(account.info.balance, U256::from(1010)); + } + + #[test] + fn test_remove_l1_cost() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(1049), + ..Default::default() + }, + ); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + deduct_caller::(&mut context).unwrap(); + + // Check the account balance is updated. + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .unwrap(); + assert_eq!(account.info.balance, U256::from(1)); + } + + #[test] + fn test_remove_l1_cost_lack_of_funds() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(48), + ..Default::default() + }, + ); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + + assert_eq!( + deduct_caller::(&mut context), + Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(U256::from(1048)), + balance: Box::new(U256::from(48)), + }, + )) + ); + } } diff --git a/crates/revm/src/optimism/l1block.rs b/crates/revm/src/optimism/l1block.rs new file mode 100644 index 0000000000..85f8c7a620 --- /dev/null +++ b/crates/revm/src/optimism/l1block.rs @@ -0,0 +1,168 @@ +use crate::primitives::{address, db::Database, Address, SpecId, U256}; +use core::ops::Mul; + +const ZERO_BYTE_COST: u64 = 4; +const NON_ZERO_BYTE_COST: u64 = 16; + +const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); +const L1_OVERHEAD_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); +const L1_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); + +/// The address of L1 fee recipient. +pub const L1_FEE_RECIPIENT: Address = address!("420000000000000000000000000000000000001A"); + +/// The address of the base fee recipient. +pub const BASE_FEE_RECIPIENT: Address = address!("4200000000000000000000000000000000000019"); + +/// The address of the L1Block contract. +pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); + +/// L1 block info +/// +/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` +/// transaction data. This data is then used to calculate the L1 cost of a transaction. +/// +/// Here is the format of the `setL1BlockValues` transaction data: +/// +/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, +/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) +/// +/// For now, we only care about the fields necessary for L1 cost calculation. +#[derive(Clone, Debug)] +pub struct L1BlockInfo { + /// The base fee of the L1 origin block. + pub l1_base_fee: U256, + /// The current L1 fee overhead. + pub l1_fee_overhead: U256, + /// The current L1 fee scalar. + pub l1_fee_scalar: U256, +} + +impl L1BlockInfo { + /// Try to fetch the L1 block info from the database. + pub fn try_fetch(db: &mut DB) -> Result { + let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; + let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; + let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; + + Ok(L1BlockInfo { + l1_base_fee, + l1_fee_overhead, + l1_fee_scalar, + }) + } + + /// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per non-zero + /// byte and 4 gas per zero byte. + /// + /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to + /// account for the empty signature. + pub fn data_gas(&self, input: &[u8], spec_id: SpecId) -> U256 { + let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { + acc + if *byte == 0x00 { + ZERO_BYTE_COST + } else { + NON_ZERO_BYTE_COST + } + })); + + // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. + if !spec_id.is_enabled_in(SpecId::REGOLITH) { + rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); + } + + rollup_data_gas_cost + } + + /// Calculate the gas cost of a transaction based on L1 block data posted on L2 + pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: SpecId) -> U256 { + // If the input is not a deposit transaction, the default value is zero. + if input.is_empty() || input.first() == Some(&0x7F) { + return U256::ZERO; + } + + let rollup_data_gas_cost = self.data_gas(input, spec_id); + rollup_data_gas_cost + .saturating_add(self.l1_fee_overhead) + .saturating_mul(self.l1_base_fee) + .saturating_mul(self.l1_fee_scalar) + / U256::from(1_000_000) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::primitives::bytes; + + #[test] + fn test_data_gas_non_zero_bytes() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000_000), + l1_fee_overhead: U256::from(1_000_000), + l1_fee_scalar: U256::from(1_000_000), + }; + + // 0xFACADE = 6 nibbles = 3 bytes + // 0xFACADE = 1111 1010 . 1100 1010 . 1101 1110 + + // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes + // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 + // gas cost = 3 * 16 + 68 * 16 = 1136 + let input = bytes!("FACADE"); + let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); + assert_eq!(bedrock_data_gas, U256::from(1136)); + + // Regolith has no added 68 non zero bytes + // gas cost = 3 * 16 = 48 + let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); + assert_eq!(regolith_data_gas, U256::from(48)); + } + + #[test] + fn test_data_gas_zero_bytes() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000_000), + l1_fee_overhead: U256::from(1_000_000), + l1_fee_scalar: U256::from(1_000_000), + }; + + // 0xFA00CA00DE = 10 nibbles = 5 bytes + // 0xFA00CA00DE = 1111 1010 . 0000 0000 . 1100 1010 . 0000 0000 . 1101 1110 + + // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes + // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 + // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 + let input = bytes!("FA00CA00DE"); + let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); + assert_eq!(bedrock_data_gas, U256::from(1144)); + + // Regolith has no added 68 non zero bytes + // gas cost = 3 * 16 + 2 * 4 = 56 + let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); + assert_eq!(regolith_data_gas, U256::from(56)); + } + + #[test] + fn test_calculate_tx_l1_cost() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }; + + let input = bytes!("FACADE"); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + assert_eq!(gas_cost, U256::from(1048)); + + // Zero rollup data gas cost should result in zero + let input = bytes!(""); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + assert_eq!(gas_cost, U256::ZERO); + + // Deposit transactions with the EIP-2718 type of 0x7F should result in zero + let input = bytes!("7FFACADE"); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + assert_eq!(gas_cost, U256::ZERO); + } +} diff --git a/crates/revm/src/test_utils.rs b/crates/revm/src/test_utils.rs index 46801c3dd6..98fa21378f 100644 --- a/crates/revm/src/test_utils.rs +++ b/crates/revm/src/test_utils.rs @@ -1,2 +1,2 @@ #[doc(hidden)] -pub use crate::evm_context::test_utils::*; +pub use crate::context::test_utils::*; diff --git a/documentation/src/SUMMARY.md b/documentation/src/SUMMARY.md index 9cc423b5b3..bfa087b036 100644 --- a/documentation/src/SUMMARY.md +++ b/documentation/src/SUMMARY.md @@ -3,9 +3,10 @@ - [Introduction](./introduction.md) - [Revm](./crates/revm.md) - [evm](./crates/revm/evm.md) - - [evm_impl](./crates/revm/evm_impl.md) - - [The Host Trait](./crates/revm/host_trait.md) + - [builder](./crates/revm/builder.md) + - [handler](./crates/revm/handler.md) - [inspector](./crates/revm/inspector.md) + - [state](./crates/revm/state.md) - [journaled_state](./crates/revm/journaled_state.md) - [Interpreter](./crates/interpreter.md) - [gas](./crates/interpreter/gas.md) diff --git a/documentation/src/crates/interpreter/host.md b/documentation/src/crates/interpreter/host.md index a3a7f0ab39..90752f3a7d 100644 --- a/documentation/src/crates/interpreter/host.md +++ b/documentation/src/crates/interpreter/host.md @@ -2,11 +2,10 @@ The `host.rs` module in this Rust EVM implementation defines a crucial trait `Host`. The `Host` trait outlines an interface for the interaction of the EVM interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking transactions. +The [`Evm`](./../revm/evm.md) struct implements this `Host` trait. ## Trait Methods -- `step` & `step_end`: These methods manage the execution of EVM opcodes. The `step` method is invoked before executing an opcode, while `step_end` is invoked after. These methods can modify the EVM state or halt execution based on certain conditions. - - `env`: This method provides access to the EVM environment, including information about the current block and transaction. - `load_account`: Retrieves information about a given Ethereum account. @@ -21,7 +20,5 @@ The `host.rs` module in this Rust EVM implementation defines a crucial trait `Ho - `selfdestruct`: Marks an Ethereum account to be self-destructed, transferring its funds to a target account. -- `create` & `call`: These methods handle the creation of new smart contracts and the invocation of smart contract functions, respectively. - The `Host` trait provides a standard interface that any host environment for the EVM must implement. This abstraction allows the EVM code to interact with the state of the Ethereum network in a generic way, thereby enhancing modularity and interoperability. Different implementations of the `Host` trait can be used to simulate different environments for testing or for connecting to different Ethereum-like networks. \ No newline at end of file diff --git a/documentation/src/crates/revm.md b/documentation/src/crates/revm.md index adee43a5ea..80b7d64a27 100644 --- a/documentation/src/crates/revm.md +++ b/documentation/src/crates/revm.md @@ -1,19 +1,16 @@ # Rust Ethereum Virtual Machine (revm) -The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including database handling, state journaling, and an inspection system for observing and logging the execution of EVM. This crate pulls together everything described prior to deliver the rust evm. +The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including call loop and host implementation, database handling, state journaling and powerful logic handlers that can be overwritten. This crate pulls Primitives, Interpreter and Precompiles together to deliver the rust evm. -Modules: +Starting point of the documentation should be a [`Evm`](./revm/evm.md) that is main structure of EVM. Then i would recomend reading about the `EvmBuilder` that is used to create the `Evm` and modify. After that you can read about the `Handler` that is used to modify the logic of the Evm and it will tie with how introspection of Evm can be done. And lastly you can read about the `Inspector` that is legacy interface for inspecting execution that is repurposed as one of example of handler registers. -- `db`: This module includes structures and functions for database interaction. -- `evm`: This module contains a Struct that takes Database and enabled transact to update state directly to database. Additionally it allows user to set all environment parameters. -- `evm_impl`: This module includes more specific implementations related to the EVM regarding state transitions. -- `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. +Modules: +- `evm`: This is main module that executed EVM calls. +- `builder`: This modules build the Evm, sets database, handlers and other parameters. Here is where we set handlers for specific fork or external state for inspection. +- `db`: This module includes structures and functions for database interaction. It is a glue between EVM and database. It transforms or aggregates the EVM changes. +- `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. This was main way to inspect EVM execution before the Builder and Handlers were introduced. It is still enabled through the Builder. - `journaled_state`: This module manages the state of the EVM and implements a journaling system to handle changes and reverts. -External Crates: - -- `alloc`: The alloc crate is used to provide the ability to allocate memory on the heap. It's a part of Rust's standard library that can be used in environments without a full host OS. - Re-exported Crates: - `revm_precompile`: This crate is re-exported, providing the precompiled contracts used in the EVM implementation. diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md new file mode 100644 index 0000000000..dad889ba5b --- /dev/null +++ b/documentation/src/crates/revm/builder.md @@ -0,0 +1,103 @@ + +# Evm Builder + +It build or modifies the EVM and applies different handler, and allows setting external context and registering handler custom logic. + +`Evm` inside revm consist of the few parts `Context` and `Handler`. `Context` is additionally split between `EvmContext` that contains generic `Database` and `External` context that is generic without restrain. Read here for more information on [`Evm`](./evm.md) internals. + +Builder ties dependencies between generic `Database`, `External` context and `Spec` and allows handle registers to be added that implement logic on those generics. As they are interconected setting `Database` or `ExternalContext` wound reset handle registers, and builder stages are introduced to mitigate those misuses. + +Simple example of using `EvmBuilder`: + +```rust,ignore + use crate::evm::Evm; + + // build Evm with default values. + let mut evm = Evm::builder().build(); + let output = evm.transact(); +``` + +## Builder Stages + +There are two builder stages that are used to mitigate potential misuse of the builder: + +* `SetGenericStage`: Initial stage that allows setting the database and external context. +* `HandlerStage`: Allows setting the handler registers but is explicit about setting new generic type as it will void the handler registers. + +Functions from one stage are just a renamed function from other stage, it is made so that user is more aware of what underlying function does. Example of this, in `SettingDbStage` we have `with_db` function while in `HandlerStage` we have `reset_handler_with_db`, both of them set the database but the latter also resets the handler. There are multiple functions that are common to both stages as in `build`. + +There is naming convention for the functions that can be found in builder: +* In both stages we have: + * `build` is to create the Evm. + * `spec_id` is used to set the spec id, this is common to both stages and will create new mainnet handler and reapply all the handler registers. + * `modify_*` are used to modify the database, external context or Env. + * `clear_*` allows setting default values for Environment. + * `append_handler_register_*` are used to push handler registers. This will transition the builder to the `HandlerStage`. +* `SetGenericStage` have: + * `with_*` are found in `SetGenericStage` and are used to set the generics. +* `HandlerStage` have: + * `reset_handler_with_*` is used if we want to change some of the generic types this will reset the handler registers. This will transition the builder to the `SetGenericStage`. + +# Creating and modification of Evm + +Evm implements function that allows using of EvmBuilder without even knowing that it exist. Most obvious one is `Evm::builder()` that would create a new builder with default values. + +Additionally function that is very important is `evm.modify()` that allows modifying the Evm. It will return the builder and will allow user to modify the Evm. + +# Examples + +Example of using builder to create Evm with inspector: +```rust,ignore + use crate::{ + db::EmptyDB, Context, EvmContext, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, + }; + + // Create the evm. + let evm = Evm::builder() + .with_db(EmptyDB::default()) + .with_external_context(NoOpInspector) + // Register will modify Handler and call NoOpInspector. + .append_handler_register(inspector_handle_register) + // .with_db(..) would not compile as we already locked the builder generics, + // alternative fn is reset_handler_with_db(..) + .build(); + + // Execute the evm. + let output = evm.transact(); + + // Extract evm context. + let Context { + external, + evm: EvmContext { db, .. }, + } = evm.into_context(); +``` + +Example of changing spec id and Environment of already build evm. +```rust,ignore + use crate::{Evm,SpecId::BERLIN}; + + // Create default evm. + let evm = Evm::builder().build(); + + // Modify evm spec. + let evm = evm.modify().spec_id(BERLIN).build(); + + // Shortcut for above. + let mut evm = evm.modify_spec_id(BERLIN); + + // Execute the evm. + let output1 = evm.transact(); + + // Example of modifying the tx env. + let mut evm = evm.modify().modify_tx_env(|env| env.gas_price = 0.into()).build(); + + // Execute the evm with modified tx env. + let output2 = evm.transact(); +``` + +## Appending handler registers + +Handler registers are simple function that allow modifying the `Handler` logic by replacing +the handler functions. They are used to add custom logic to the evm execution but as they are free to modify the `Handler` in any form they want there can be conflicts if handlers that override the same function are added. + +Most common use case for adding new logic to `Handler` is `Inspector` that is used to inspect the execution of the evm. Example of this can be found in [`Inspector`](./inspector.md) documentation. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index b314ead3c1..ec6434747b 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -1,17 +1,36 @@ -# EVM Module Documentation +# EVM -This document provides the documentation for the `EVM` module. +`Evm` is the primary structure that implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. -## The `EVM` +## What is inside -The primary struct in this module is `EVM`. The EVM struct is generic over a type DB. This means that when you use the EVM struct, you can specify the type that DB should represent. It adds flexibility to the struct, allowing it to store different types of databases or data structures in the db field depending on the use case. The `EVM` struct enables `transact` to update the state directly to the database. Additionally, it allows the user to set all environment parameters. +It is consisting of two main parts the `Context` and the `Handler`. `Context` represent the state that is needed for execution and `Handler` contains list of functions that act as a logic. -The parameters that can be set are divided between `Config`, `Block`, and `Transaction` (tx). For transacting on the EVM, you can call `transact_commit` that will automatically apply changes to the database. +`Context` is additionally split between `EvmContext` and `External` context. `EvmContext` is internal and contains `Database`, `Environment`, `JournaledState` and `Precompiles`. And `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to save state in runtime or allows hooks to be added (For example external contexts can be a Inspector), more on its usage can be seen in [`EvmBuilder`](./builder.md). -## Database Abstractions +`Evm` implements the [`Host`](./../interpreter/host.md) trait, which defines an interface for the interaction of the EVM Interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking sub calls and selfdestruct. -You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. +Data structures of block and transaction can be found inside `Environment`. And more information on journaled state can be found in [`JournaledState`](../revm/journaled_state.md) documentation. -- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. -- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. -- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. +## Runtime + +Runtime consist of list of functions from `Handler` that are called in predefined order. They are grouped by functionality on `Verification`, `PreExecution`, `ExecutionLoop`, `PostExecution` and `Instruction` functions. Verification function are related to the preverification of set `Environment` data. Pre/Post execution function are function that deduct and reward caller beneficiary. And `ExecutionLoop` functions handles initial call and creates and sub calls. `Instruction` functions are instruction table that is used inside Interpreter to execute opcodes. + +`Evm` execution runs **two** loops. First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions it is handled by `ExecutionLoopHandler`. Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction from `InstructionTable`. + +First loop, the call loop, implements stack of `Frames` and it is responsible for handling sub calls and its return outputs. At the start Evm creates first `Frame` that contains `Interpreter` and starts the loop. `Interpreter` returns the `InterpreterAction` and action can be a `Return` of a call this means this interpreter finished its run or `SubCall`/`SubCreate` that means that new `Frame` needs to be created and pushed to the stack. When `Interpreter` returns `Return` action `Frame` is popped from the stack and its return value is pushed to the parent `Frame` stack. When `Interpreter` returns `SubCall`/`SubCreate` action new `Frame` is created and pushed to the stack and the loop continues. When the stack is empty the loop finishes. + +Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../interpreter.md) crate. + +To dive deeper into the `Evm` logic check [`Handler`](./handler.md) documentation. + +# Functionalities + +Function of Evm is to start execution but setting up what Evm is going to execute is done by `EvmBuilder`. + +Main function inside evm are: +* `preverify` - that only preverifies transaction information. +* `transact preverified` - is next step after preverification that executes transaction. +* `transact` - it calls both preverifies and it executes transaction. +* `builder` and `modify` function - allows building or modifying the Evm, more on this can be found in [`EvmBuilder`](./builder.md) documentation. `builder` is main way of creating Evm and `modify` allows you to modify parts of it without dissolving `Evm`. +* `into_context` - is used we want to get the `Context` from the Evm. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm_impl.md b/documentation/src/crates/revm/evm_impl.md deleted file mode 100644 index afb0a0ee99..0000000000 --- a/documentation/src/crates/revm/evm_impl.md +++ /dev/null @@ -1,35 +0,0 @@ -# EVM Implementation - -This module implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. The following methods are exposed through the `EVMImpl` struct. - -## Methods - -- `run_interpreter` - - This method is responsible for setting up and running the interpreter for a specific contract. - - - `contract`: A `Contract` instance that the interpreter will execute. - - `gas_limit`: A `u64` that determines the maximum amount of gas that the execution can consume. - - `is_static`: A boolean flag indicating if the execution is static. Static executions cannot modify the state. - - The method returns a tuple containing the result of the execution and the interpreter instance. The result is an `InstructionResult` enumeration value that indicates if the execution was successful or if an error occurred. - - This creates a contract with a specific bytecode and a gas price, then runs the interpreter on this contract with a specified gas limit. The is_static flag is set to false which means the execution can modify the state. The stack machine implements the following instructions: - -- `call_precompile` - - This method handles the execution of precompiled contracts. These are a special set of contracts that are part of the Ethereum protocol and implemented in native code for efficiency. - - - `gas`: A `Gas` instance representing the amount of gas available for execution. - - `contract`: The address of the precompiled contract in the form of a `B160` instance. - - `input_data`: The input data for the contract as a `Bytes` instance. - - The method returns a tuple containing the result of the contract execution, the remaining gas, and any output data as a `Bytes` instance. - -- `call_inner` - - This method performs a contract call within the EVM. - - - `inputs`: A mutable reference to a `CallInputs` instance, which contains all the necessary information for the contract call. - - The method returns a tuple containing the result of the call (as an `InstructionResult`), the remaining gas (as a `Gas` instance), and any output data from the call (as a `Bytes` instance). diff --git a/documentation/src/crates/revm/handler.md b/documentation/src/crates/revm/handler.md new file mode 100644 index 0000000000..5db00daee2 --- /dev/null +++ b/documentation/src/crates/revm/handler.md @@ -0,0 +1,100 @@ +# Handler + +Is logic part of the Evm, it contains the Specification ID, list of functions that do the logic and list of registers that can change behavior of the Handler when `Handler` is build. + +Functions can be grouped in five categories and are marked in that way in the code: +* Validation functions: `ValidateHandler` +* Pre execution functions: `PreExecutionHandler` +* Execution loop functions: `LoopExecutionHandler` +* Post execution functions: `PostExecutionHandler` +* Instruction table: `InstructionTable` + +### Handle Registers + +Simple function that is used to modify handler functions. Amazing thing about them is that they can be done over generic external type. For example this allows to have a register over trait that would allow to add hooks to the any type that implements the trait, that trait can be a GetInspector trait so anyone that implement it would be able to register inspector related functions. It is used inside the `EvmBuilder` to change behavior of the default mainnet Handler. + +Handle registers are set in `EvmBuilder`. + +Order of the registers is important as they are called in the order they are registered. And it matters if register overrides the previous handle or just wraps it, overriding handle can disrupt the logic of previous registered handles. + +Registers are very powerful as they allow modification of any part of the Evm and with additional of the `External` context it becomes a powerful combo. Simple example would be to register new precompiles for the Evm. + +### ValidationHandler + +Consist of functions that are used to validate transaction and block data. They are called before the execution of the transaction and they are used to check if the data (`Environment`) is valid. They are called in the following order: + +* validate_env + + That verifies if all data is set in `Environment` and if they are valid, for example if `gas_limit` is smaller than block `gas_limit`. + +* validate_initial_tx_gas + + It calculated initial gas needed for transaction to be executed and checks if it is less them the transaction gas_limit. Note that this does not touch the `Database` or state + +* validate_tx_against_state + + It loads the caller account and checks those information. Among them the nonce, if there is enough balance to pay for max gas spent and balance transferred. + +### PreExecutionHandler + +Consist of functions that are called before execution loop. They are called in the following order: + +* load + + Loads access list and beneficiary from `Database`. Cold load is done here. + +* load precompiles + + Load precompiles. + +* deduct_caller: + + Deducts values from the caller to the maximum amount of gas that can be spent on the transaction. This loads the caller account from the `Database`. + +### ExecutionLoopHandler + +Consist of the function that handles the call stack and the first loop. They are called in the following order: + +* create_first_frame + + This handler crates first frame of the call stack. It is called only once per transaction. + +* first_frame_return + + This handler is called after the first frame is executed. It is used to calculate the gas that is returned from the first frame. + +* frame_return + + This handler is called after every frame is executed (Expect first), it will calculate the gas that is returned from the frame and apply output to the parent frame. + +* sub_call + Create new call frame or return the Interpreter result if the call is not possible (has a error) or it is precompile call. + +* sub_crate + + Create new create call frame, create new account and execute bytecode that outputs the code of the new account. + +### InstructionTable + +Is a list of 256 function pointers that are used to execute instructions. They have two types, first is simple function that is faster and second is BoxedInstraction that has a small performance penalty but allows to capture the data. Look at the Interpreter documentation for more information. + +### PostExecutionHandler + +Is a list of functions that are called after the execution loop. They are called in the following order: + +* reimburse_caller + + Reimburse the caller with gas that was not spent during the execution of the transaction. + Or balance of gas that needs to be refunded. + +* reward_beneficiary + + At the end of every transaction beneficiary needs to be rewarded with the fee. + +* output + + It returns the changes state and the result of the execution. + +* end + + It will be called always as the last function of the handler. \ No newline at end of file diff --git a/documentation/src/crates/revm/host_trait.md b/documentation/src/crates/revm/host_trait.md deleted file mode 100644 index 4abf291f5d..0000000000 --- a/documentation/src/crates/revm/host_trait.md +++ /dev/null @@ -1,57 +0,0 @@ -# Host Implementation - -The `Host` trait provides an interface that allows the EVM to interact with the external world. It contains methods to access environmental information, manipulate account balances, and interact with contract code and storage. - -The [`EVMImpl`](./evm_impl.md) struct implements this `Host` trait. - -- `step` & `step_end` - - These methods are used to control the interpreter's execution. They move the interpreter forward one step, allowing the user to inspect the state of the interpreter after each individual operation. These control the execution of the interpreter, allowing step-by-step execution and inspection. - -- `env` - - This method returns a mutable reference to the environment information that the EVM uses for its execution. The `Env` struct contains details about the current block, such as the timestamp, block number, difficulty, and gas limit. - -- `block_hash` - - This method retrieves the hash of a block given its number. It's typically used within smart contracts for actions like random number generation. - -- `load_account` - - This method loads the account associated with a given address and returns information about the account's existence and if it's a contract. - -- `balance` - - This method retrieves the balance of an Ethereum account given its address. It returns a tuple containing the balance and a boolean indicating whether the account was "cold" (accessed for the first time in the current transaction). - -- `code` - - This method retrieves the bytecode of a given address. It returns a tuple containing the bytecode and a boolean indicating whether the account was "cold". - -- `code_hash` - - This method retrieves the code_hash at a given address. It returns a tuple containing the hash and a boolean indicating whether the account was "cold". - -- `sload` & `sstore` - - These methods interact with the contract storage. The `sload` method retrieves a value from contract storage, while `sstore` sets a value in contract storage. - -- `tload` & `tstore` - - As defined in [EIP1153](https://eips.ethereum.org/EIPS/eip-1153), for transient storage reads and writes. - -- `log` - - This method is used to create log entries, which are a way for contracts to produce output that external observers (like dapps or the frontend of a blockchain explorer) can listen for and react to. - -- `selfdestruct` - - The selfdestruct method attempts to terminate the specified address, transferring its remaining balance to a given target address. If the `Inspector` is `Some`, the self-destruction event is observed or logged via an inspector. The method returns an Option, encapsulating the outcome of the operation: Some(SelfDestructResult) on success and None if an error occurs, with the error being stored internally for later reference. - -- `create` - - The create method initiates the creation of a contract with the provided CreateInputs. If the `Inspector` is `Some`, the creation process is observed or logged using an inspector, both at the start and end of the creation. The method returns a tuple consisting of the operation's result (InstructionResult), the optional address (Option) of the newly created contract, the amount of gas consumed (Gas), and the output data (Bytes). If the inspector intervenes and determines the instruction shouldn't continue, an early return occurs with the observed outcomes. - -- `call` - - The call method manages a contract invocation using the provided CallInputs. If the `Inspector` is `Some`, the call event is observed or logged via an inspector before execution. The method yields a tuple representing the outcome of the call: the result status (InstructionResult), the consumed gas (Gas), and the output data (Bytes). If the inspector suggests early termination, the method returns immediately with the observed results. Otherwise, the main call execution is processed, and the outcomes, either raw or observed, are returned accordingly. \ No newline at end of file diff --git a/documentation/src/crates/revm/state.md b/documentation/src/crates/revm/state.md new file mode 100644 index 0000000000..0dc646b088 --- /dev/null +++ b/documentation/src/crates/revm/state.md @@ -0,0 +1,11 @@ +# State implementations + +State inheritis the `Database` trait and implements fetching of external state and storage. and various functionality on output of the EVM execution, most notable caching changes while execution multiple transactions. + +## Database Abstractions + +You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. + +- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. +- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. +- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. \ No newline at end of file diff --git a/examples/fork_ref_transact.rs b/examples/fork_ref_transact.rs index 8abcacfd5c..9086467720 100644 --- a/examples/fork_ref_transact.rs +++ b/examples/fork_ref_transact.rs @@ -4,7 +4,7 @@ use ethers_providers::{Http, Provider}; use revm::{ db::{CacheDB, EmptyDB, EthersDB}, primitives::{address, ExecutionResult, Output, TransactTo, U256}, - Database, EVM, + Database, Evm, }; use std::sync::Arc; @@ -65,23 +65,23 @@ async fn main() -> anyhow::Result<()> { .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 struct - // change that to whatever caller you want to be - evm.env.tx.caller = address!("0000000000000000000000000000000000000000"); - // account you want to transact with - evm.env.tx.transact_to = TransactTo::Call(pool_address); - // calldata formed via abigen - evm.env.tx.data = encoded.0.into(); - // transaction value in wei - evm.env.tx.value = U256::from(0); + let mut evm = Evm::builder() + .with_db(cache_db) + .modify_tx_env(|tx| { + // fill in missing bits of env struct + // change that to whatever caller you want to be + tx.caller = address!("0000000000000000000000000000000000000000"); + // account you want to transact with + tx.transact_to = TransactTo::Call(pool_address); + // calldata formed via abigen + tx.data = encoded.0.into(); + // transaction value in wei + tx.value = U256::from(0); + }) + .build(); // execute transaction without writing to the DB - let ref_tx = evm.transact_ref().unwrap(); + let ref_tx = evm.transact().unwrap(); // select ExecutionResult struct let result = ref_tx.result; diff --git a/examples/generate_block_traces.rs b/examples/generate_block_traces.rs index 37579b7f2c..ed71988a85 100644 --- a/examples/generate_block_traces.rs +++ b/examples/generate_block_traces.rs @@ -6,8 +6,8 @@ use ethers_providers::{Http, Provider}; use indicatif::ProgressBar; use revm::db::{CacheDB, EthersDB, StateBuilder}; use revm::inspectors::TracerEip3155; -use revm::primitives::{Address, Env, TransactTo, U256}; -use revm::EVM; +use revm::primitives::{Address, TransactTo, U256}; +use revm::{inspector_handle_register, Evm}; use std::fs::OpenOptions; use std::io::BufWriter; use std::io::Write; @@ -74,25 +74,27 @@ async fn main() -> anyhow::Result<()> { let state_db = EthersDB::new(Arc::clone(&client), Some(prev_id)).expect("panic"); let cache_db: CacheDB>> = CacheDB::new(state_db); let mut state = StateBuilder::new_with_database(cache_db).build(); - let mut evm = EVM::new(); - evm.database(&mut state); - - let mut env = Env::default(); - if let Some(number) = block.number { - let nn = number.0[0]; - env.block.number = U256::from(nn); - } - local_fill!(env.block.coinbase, block.author); - local_fill!(env.block.timestamp, Some(block.timestamp), U256::from_limbs); - local_fill!( - env.block.difficulty, - Some(block.difficulty), - U256::from_limbs - ); - local_fill!(env.block.gas_limit, Some(block.gas_limit), U256::from_limbs); - if let Some(base_fee) = block.base_fee_per_gas { - local_fill!(env.block.basefee, Some(base_fee), U256::from_limbs); - } + let mut evm = Evm::builder() + .with_db(&mut state) + .with_external_context(TracerEip3155::new(Box::new(std::io::stdout()), true, true)) + .modify_block_env(|b| { + if let Some(number) = block.number { + let nn = number.0[0]; + b.number = U256::from(nn); + } + local_fill!(b.coinbase, block.author); + local_fill!(b.timestamp, Some(block.timestamp), U256::from_limbs); + local_fill!(b.difficulty, Some(block.difficulty), U256::from_limbs); + local_fill!(b.gas_limit, Some(block.gas_limit), U256::from_limbs); + if let Some(base_fee) = block.base_fee_per_gas { + local_fill!(b.basefee, Some(base_fee), U256::from_limbs); + } + }) + .modify_cfg_env(|c| { + c.chain_id = chain_id; + }) + .append_handler_register(inspector_handle_register) + .build(); let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -104,45 +106,49 @@ async fn main() -> anyhow::Result<()> { std::fs::create_dir_all("traces").expect("Failed to create traces directory"); // Fill in CfgEnv - env.cfg.chain_id = chain_id; for tx in block.transactions { - env.tx.caller = Address::from(tx.from.as_fixed_bytes()); - env.tx.gas_limit = tx.gas.as_u64(); - local_fill!(env.tx.gas_price, tx.gas_price, U256::from_limbs); - local_fill!(env.tx.value, Some(tx.value), U256::from_limbs); - env.tx.data = tx.input.0.into(); - let mut gas_priority_fee = U256::ZERO; - local_fill!( - gas_priority_fee, - tx.max_priority_fee_per_gas, - U256::from_limbs - ); - env.tx.gas_priority_fee = Some(gas_priority_fee); - env.tx.chain_id = Some(chain_id); - env.tx.nonce = Some(tx.nonce.as_u64()); - if let Some(access_list) = tx.access_list { - env.tx.access_list = access_list - .0 - .into_iter() - .map(|item| { - let new_keys: Vec = item - .storage_keys + evm = evm + .modify() + .modify_tx_env(|etx| { + etx.caller = Address::from(tx.from.as_fixed_bytes()); + etx.gas_limit = tx.gas.as_u64(); + local_fill!(etx.gas_price, tx.gas_price, U256::from_limbs); + local_fill!(etx.value, Some(tx.value), U256::from_limbs); + etx.data = tx.input.0.into(); + let mut gas_priority_fee = U256::ZERO; + local_fill!( + gas_priority_fee, + tx.max_priority_fee_per_gas, + U256::from_limbs + ); + etx.gas_priority_fee = Some(gas_priority_fee); + etx.chain_id = Some(chain_id); + etx.nonce = Some(tx.nonce.as_u64()); + if let Some(access_list) = tx.access_list { + etx.access_list = access_list + .0 .into_iter() - .map(|h256| U256::from_le_bytes(h256.0)) + .map(|item| { + let new_keys: Vec = item + .storage_keys + .into_iter() + .map(|h256| U256::from_le_bytes(h256.0)) + .collect(); + (Address::from(item.address.as_fixed_bytes()), new_keys) + }) .collect(); - (Address::from(item.address.as_fixed_bytes()), new_keys) - }) - .collect(); - } else { - env.tx.access_list = Default::default(); - } - - env.tx.transact_to = match tx.to { - Some(to_address) => TransactTo::Call(Address::from(to_address.as_fixed_bytes())), - None => TransactTo::create(), - }; - - evm.env = env.clone(); + } else { + etx.access_list = Default::default(); + } + + etx.transact_to = match tx.to { + Some(to_address) => { + TransactTo::Call(Address::from(to_address.as_fixed_bytes())) + } + None => TransactTo::create(), + }; + }) + .build(); // Construct the file writer to write the trace to let tx_number = tx.transaction_index.unwrap().0[0]; @@ -154,8 +160,8 @@ async fn main() -> anyhow::Result<()> { let writer = FlushWriter::new(Arc::clone(&inner)); // Inspect and commit the transaction to the EVM - let inspector = TracerEip3155::new(Box::new(writer), true, true); - if let Err(error) = evm.inspect_commit(inspector) { + evm.context.external.set_writer(Box::new(writer)); + if let Err(error) = evm.transact_commit() { println!("Got error: {:?}", error); }