From 884b124badcfbbba1604488204d4579681b6802c Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Mon, 6 Feb 2023 10:36:11 +0530 Subject: [PATCH 01/18] added new api to support conditional transactions (EIP-4337) (#700) --- accounts/abi/bind/backend.go | 3 ++ accounts/abi/bind/backends/simulated.go | 28 +++++++++++++++ accounts/abi/bind/backends/simulated_test.go | 33 ++++++++++-------- accounts/abi/bind/base_test.go | 4 +++ ethclient/ethclient.go | 9 +++++ ethclient/ethclient_test.go | 36 ++++++++++++++++++++ internal/ethapi/api.go | 35 +++++++++++++++++++ internal/jsre/deps/web3.js | 8 +++++ mobile/ethclient.go | 5 +++ 9 files changed, 146 insertions(+), 15 deletions(-) diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index c16990f395..a8e1abcdc2 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -96,6 +96,9 @@ type ContractTransactor interface { // SendTransaction injects the transaction into the pending pool for execution. SendTransaction(ctx context.Context, tx *types.Transaction) error + + // SendTransactionConditional injects the conditional transaction into the pending pool for execution after verification. + SendTransactionConditional(ctx context.Context, tx *types.Transaction, knownAccounts map[string]map[common.Address]interface{}) error } // ContractFilterer defines the methods needed to access log events using one-off diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 2431b5644a..b116ebe436 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "math/big" + "reflect" "sync" "time" @@ -675,6 +676,33 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa return nil } +// a replica of above `SendTransaction` function +func (b *SimulatedBackend) SendTransactionConditional(ctx context.Context, tx *types.Transaction, knownAccounts map[string]map[common.Address]interface{}) error { + // check knownAccounts + currentState, _ := b.blockchain.State() + + for k, v := range knownAccounts["knownAccounts"] { + // check if the value is hex string or an object + if object, ok := v.(string); ok { + actualRootHash := currentState.StorageTrie(k).Hash() + if common.HexToHash(object) != actualRootHash { + return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, common.HexToHash(object), actualRootHash) + } + } else if object, ok := v.(map[string]interface{}); ok { + for slot, value := range object { + actualValue := currentState.GetState(k, common.HexToHash(slot)) + if common.HexToHash(value.(string)) != actualValue { + return fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", k, slot, value, actualValue) + } + } + } else { + return fmt.Errorf("invalid type in knownAccounts %v", reflect.TypeOf(v)) + } + } + + return b.SendTransaction(ctx, tx) +} + // FilterLogs executes a log filter operation, blocking during execution and // returning all the results in one batch. // diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go index 7df1a823ec..e05f6be704 100644 --- a/accounts/abi/bind/backends/simulated_test.go +++ b/accounts/abi/bind/backends/simulated_test.go @@ -98,17 +98,17 @@ func TestSimulatedBackend(t *testing.T) { var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") -// the following is based on this contract: -// contract T { -// event received(address sender, uint amount, bytes memo); -// event receivedAddr(address sender); +// the following is based on this contract: +// contract T { +// event received(address sender, uint amount, bytes memo); +// event receivedAddr(address sender); // -// function receive(bytes calldata memo) external payable returns (string memory res) { -// emit received(msg.sender, msg.value, memo); -// emit receivedAddr(msg.sender); -// return "hello world"; -// } -// } +// function receive(bytes calldata memo) external payable returns (string memory res) { +// emit received(msg.sender, msg.value, memo); +// emit receivedAddr(msg.sender); +// return "hello world"; +// } +// } const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]` const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` const deployedCode = `60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` @@ -1000,7 +1000,8 @@ func TestCodeAt(t *testing.T) { } // When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt: -// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} +// +// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} func TestPendingAndCallContract(t *testing.T) { testAddr := crypto.PubkeyToAddress(testKey.PublicKey) sim := simTestBackend(testAddr) @@ -1211,10 +1212,11 @@ func TestFork(t *testing.T) { Example contract to test event emission: pragma solidity >=0.7.0 <0.9.0; -contract Callable { - event Called(); - function Call() public { emit Called(); } -} + + contract Callable { + event Called(); + function Call() public { emit Called(); } + } */ const callableAbi = "[{\"anonymous\":false,\"inputs\":[],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" @@ -1232,6 +1234,7 @@ const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3f // 7. Mine two blocks to trigger a reorg. // 8. Check that the event was removed. // 9. Re-send the transaction and mine a block. +// // 10. Check that the event was reborn. func TestForkLogsReborn(t *testing.T) { testAddr := crypto.PubkeyToAddress(testKey.PublicKey) diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index 08ba18f95e..31fa41bb8c 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -74,6 +74,10 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac return nil } +func (mt *mockTransactor) SendTransactionConditional(ctx context.Context, tx *types.Transaction, ownAccounts map[string]map[common.Address]interface{}) error { + return nil +} + type mockCaller struct { codeAtBlockNumber *big.Int callContractBlockNumber *big.Int diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 13a2e46d25..132beb94a2 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -529,6 +529,15 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)) } +func (ec *Client) SendTransactionConditional(ctx context.Context, tx *types.Transaction, knownAccounts map[string]map[common.Address]interface{}) error { + data, err := tx.MarshalBinary() + if err != nil { + return err + } + + return ec.c.CallContext(ctx, nil, "eth_sendRawTransactionConditional", hexutil.Encode(data), knownAccounts) +} + func toBlockNumArg(number *big.Int) string { if number == nil { return "latest" diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index a75d17ded6..5c7ab7cd03 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -569,6 +569,10 @@ func testCallContract(t *testing.T, client *rpc.Client) { func testAtFunctions(t *testing.T, client *rpc.Client) { ec := NewClient(client) + if err := sendTransactionConditional(ec); err != nil { + t.Fatalf("error: %v", err) + } + // send a transaction for some interesting pending status sendTransaction(ec) time.Sleep(100 * time.Millisecond) @@ -694,3 +698,35 @@ func sendTransaction(ec *Client) error { } return ec.SendTransaction(context.Background(), tx) } + +func sendTransactionConditional(ec *Client) error { + chainID, err := ec.ChainID(context.Background()) + if err != nil { + return err + } + + nonce, err := ec.PendingNonceAt(context.Background(), testAddr) + if err != nil { + return err + } + + signer := types.LatestSignerForChainID(chainID) + + tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{ + Nonce: nonce, + To: &common.Address{2}, + Value: big.NewInt(1), + Gas: 22000, + GasPrice: big.NewInt(params.InitialBaseFee), + }) + if err != nil { + return err + } + + return ec.SendTransactionConditional(context.Background(), tx, map[string]map[common.Address]interface{}{ + "knownAccounts": { + common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"): "0x313AaDcA1750CaadC7BCB26FF08175c95DCf8E38", + common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"): "0x413AaDcA1750CaadC7BCB26FF08175c95DCf8E38", + }, + }) +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7df46b1f33..6a3372579f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "math/big" + "reflect" "runtime" "strings" "time" @@ -1928,6 +1929,40 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, input return SubmitTransaction(ctx, s.b, tx) } +// SendRawTransactionConditional will add the signed transaction to the transaction pool. +// The sender/bundler is responsible for signing the transaction +func (s *PublicTransactionPoolAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, knownAccounts map[string]map[common.Address]interface{}) (common.Hash, error) { + tx := new(types.Transaction) + if err := tx.UnmarshalBinary(input); err != nil { + return common.Hash{}, err + } + + // check knownAccounts + currentBlock := s.b.CurrentBlock() + currentState, _, _ := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(currentBlock.Number().Int64())) + + for k, v := range knownAccounts["knownAccounts"] { + // check if the value is hex string or an object + if object, ok := v.(string); ok { + actualRootHash := currentState.StorageTrie(k).Hash() + if common.HexToHash(object) != actualRootHash { + return common.Hash{}, fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, common.HexToHash(object), actualRootHash) + } + } else if object, ok := v.(map[string]interface{}); ok { + for slot, value := range object { + actualValue := currentState.GetState(k, common.HexToHash(slot)) + if common.HexToHash(value.(string)) != actualValue { + return common.Hash{}, fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", k, slot, value, actualValue) + } + } + } else { + return common.Hash{}, fmt.Errorf("invalid type in knownAccounts %v", reflect.TypeOf(v)) + } + } + + return SubmitTransaction(ctx, s.b, tx) +} + // Sign calculates an ECDSA signature for: // keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). // diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index cd9c9fbda2..bbd915ab03 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -5383,6 +5383,13 @@ var methods = function () { inputFormatter: [null] }); + var sendRawTransactionConditional = new Method({ + name: 'sendRawTransactionConditional', + call: 'eth_sendRawTransactionConditional', + params: 2, + inputFormatter: [null] + }); + var sendTransaction = new Method({ name: 'sendTransaction', call: 'eth_sendTransaction', @@ -5471,6 +5478,7 @@ var methods = function () { call, estimateGas, sendRawTransaction, + sendRawTransactionConditional, signTransaction, sendTransaction, sign, diff --git a/mobile/ethclient.go b/mobile/ethclient.go index 662125c4ad..53202b254b 100644 --- a/mobile/ethclient.go +++ b/mobile/ethclient.go @@ -21,6 +21,7 @@ package geth import ( "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" ) @@ -314,3 +315,7 @@ func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _ func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { return ec.client.SendTransaction(ctx.context, tx.tx) } + +func (ec *EthereumClient) SendTransactionConditional(ctx *Context, tx *Transaction, knownAccounts map[string]map[common.Address]interface{}) error { + return ec.client.SendTransactionConditional(ctx.context, tx.tx, knownAccounts) +} From 84ed0588d03e0d67cd7002b3954fd4c500d9d192 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 18 Apr 2023 12:09:32 +0530 Subject: [PATCH 02/18] Refactored the code and updated the miner to check for the validity of options (#793) * refactored the code and updated the miner to check for the validity of options * added new errors -32003 and -32005 * added unit tests * addressed comments * Aa 4337 update generics (#799) * poc * minor bug fix * use common.Hash * updated UnmarshalJSON function (reference - tynes) * fix * done * linters * with test * undo some unintentional changes --------- Co-authored-by: Pratik Patil * handelling the block range and timestamp range, also made timestamp a pointer --------- Co-authored-by: Evgeny Danilenko <6655321@bk.ru> --- common/types.go | 9 +- core/blockchain.go | 7 + core/rawdb/bor_receipt.go | 1 + core/state/state_test.go | 71 +++++++++ core/state/statedb.go | 28 ++++ core/tx_pool_test.go | 32 ++++ core/types/block.go | 36 +++++ core/types/block_test.go | 164 +++++++++++++++++++++ core/types/transaction.go | 11 ++ core/types/transaction_conditional.go | 163 ++++++++++++++++++++ core/types/transaction_conditional_test.go | 47 ++++++ core/vm/contracts.go | 9 +- eth/filters/bor_api.go | 3 +- internal/ethapi/api.go | 46 +++--- miner/worker.go | 27 ++++ rpc/errors.go | 12 ++ rpc/handler.go | 17 ++- rpc/inproc.go | 3 + rpc/ipc.go | 3 + rpc/server.go | 8 +- 20 files changed, 660 insertions(+), 37 deletions(-) create mode 100644 core/types/transaction_conditional.go create mode 100644 core/types/transaction_conditional_test.go diff --git a/common/types.go b/common/types.go index 2205835cb5..c837b430d7 100644 --- a/common/types.go +++ b/common/types.go @@ -28,8 +28,9 @@ import ( "reflect" "strings" - "github.com/ethereum/go-ethereum/common/hexutil" "golang.org/x/crypto/sha3" + + "github.com/ethereum/go-ethereum/common/hexutil" ) // Lengths of hashes and addresses in bytes. @@ -64,6 +65,12 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } // If b is larger than len(h), b will be cropped from the left. func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } +func HexToRefHash(s string) *Hash { + v := BytesToHash(FromHex(s)) + + return &v +} + // Bytes gets the byte representation of the underlying hash. func (h Hash) Bytes() []byte { return h[:] } diff --git a/core/blockchain.go b/core/blockchain.go index 74fd4bfeda..2def2ea9ab 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1363,14 +1363,18 @@ func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types // the chain mutex to be held. func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { var stateSyncLogs []*types.Log + if stateSyncLogs, err = bc.writeBlockWithState(block, receipts, logs, state); err != nil { return NonStatTy, err } + currentBlock := bc.CurrentBlock() + reorg, err := bc.forker.ReorgNeeded(currentBlock.Header(), block.Header()) if err != nil { return NonStatTy, err } + if reorg { // Reorganise the chain if the parent is not the head block if block.ParentHash() != currentBlock.Hash() { @@ -1378,14 +1382,17 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types return NonStatTy, err } } + status = CanonStatTy } else { status = SideStatTy } + // Set new head. if status == CanonStatTy { bc.writeHeadBlock(block) } + bc.futureBlocks.Remove(block.Hash()) if status == CanonStatTy { diff --git a/core/rawdb/bor_receipt.go b/core/rawdb/bor_receipt.go index e225083741..d061dedc9e 100644 --- a/core/rawdb/bor_receipt.go +++ b/core/rawdb/bor_receipt.go @@ -75,6 +75,7 @@ func ReadBorReceiptRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.Raw return data } } + return nil // Can't find the data anywhere. } diff --git a/core/state/state_test.go b/core/state/state_test.go index 0a55d7781f..a4c4f6131e 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -18,11 +18,13 @@ package state import ( "bytes" + "fmt" "math/big" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" ) @@ -251,3 +253,72 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) { } } } + +func TestValidateKnownAccounts(t *testing.T) { + t.Parallel() + + knownAccounts := make(types.KnownAccounts) + + types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce")) + types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"), map[common.Hash]common.Hash{ + common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"), + common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"), + }) + + stateobjaddr1 := common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1") + stateobjaddr2 := common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2") + + storageaddr1 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000zzz") + storageaddr21 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa") + storageaddr22 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc") + + data1 := common.BytesToHash([]byte{24}) + data21 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb") + data22 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd") + + s := newStateTest() + + // set initial state object value + s.state.SetState(stateobjaddr1, storageaddr1, data1) + s.state.SetState(stateobjaddr2, storageaddr21, data21) + s.state.SetState(stateobjaddr2, storageaddr22, data22) + + fmt.Println("\nStorageTrie.Hash()", s.state.StorageTrie(stateobjaddr1).Hash()) + + if err := s.state.ValidateKnownAccounts(knownAccounts); err != nil { + t.Fatalf(err.Error()) + } + + types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf")) + + stateobjaddr3 := common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2") + storageaddr3 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000yyy") + data3 := common.BytesToHash([]byte{24}) + + s.state.SetState(stateobjaddr3, storageaddr3, data3) + + // expected error + if err := s.state.ValidateKnownAccounts(knownAccounts); err == nil { + t.Fatalf("should have been an error") + } + + // correct the previous mistake "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf" -> "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce" + types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce")) + types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3"), map[common.Hash]common.Hash{ + common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb"), + common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc"): common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ddd"), + }) + + stateobjaddr4 := common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add3") + storageaddr41 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000aaa") + storageaddr42 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000ccc") + data4 := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000bbb") + + s.state.SetState(stateobjaddr4, storageaddr41, data4) + s.state.SetState(stateobjaddr4, storageaddr42, data4) + + // expected error + if err := s.state.ValidateKnownAccounts(knownAccounts); err == nil { + t.Fatalf("should have been an error") + } +} diff --git a/core/state/statedb.go b/core/state/statedb.go index c236a79b5a..2e3b59be1b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1046,3 +1046,31 @@ func (s *StateDB) AddressInAccessList(addr common.Address) bool { func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { return s.accessList.Contains(addr, slot) } + +func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error { + if knownAccounts == nil { + return types.ErrEmptyKnownAccounts + } + + for k, v := range knownAccounts { + // check if the value is hex string or an object + switch { + case v.IsSingle(): + actualRootHash := s.StorageTrie(k).Hash() + if *v.Single != actualRootHash { + return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, actualRootHash) + } + case v.IsStorage(): + for slot, value := range v.Storage { + actualValue := s.GetState(k, slot) + if value != actualValue { + return fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", k, slot, value, actualValue) + } + } + default: + return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, s.StorageTrie(k).Hash()) + } + } + + return nil +} diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 63f712bb9c..7434c54e6a 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -1836,6 +1836,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { keys[i], _ = crypto.GenerateKey() testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) } + // Generate and queue a batch of transactions, both pending and queued txs := types.Transactions{} @@ -1854,61 +1855,78 @@ func TestTransactionPoolUnderpricing(t *testing.T) { if pending != 3 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) } + if queued != 1 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) } + if err := validateEvents(events, 3); err != nil { t.Fatalf("original event firing failed: %v", err) } + if err := validateTxPoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } + // Ensure that adding an underpriced transaction on block limit fails if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); !errors.Is(err, ErrUnderpriced) { t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced) } + // Ensure that adding high priced transactions drops cheap ones, but not own if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil { // +K1:0 => -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que - t.Fatalf("failed to add well priced transaction: %v", err) } + if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(4), keys[1])); err != nil { // +K1:2 => -K0:0 => Pend K1:0, K2:0; Que K0:1 K1:2 t.Fatalf("failed to add well priced transaction: %v", err) } + if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(5), keys[1])); err != nil { // +K1:3 => -K0:1 => Pend K1:0, K2:0; Que K1:2 K1:3 t.Fatalf("failed to add well priced transaction: %v", err) } + pending, queued = pool.Stats() if pending != 2 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) } + if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) } + if err := validateEvents(events, 1); err != nil { t.Fatalf("additional event firing failed: %v", err) } + if err := validateTxPoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } + // Ensure that adding local transactions can push out even higher priced ones ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2]) if err := pool.AddLocal(ltx); err != nil { t.Fatalf("failed to append underpriced local transaction: %v", err) } + ltx = pricedTransaction(0, 100000, big.NewInt(0), keys[3]) if err := pool.AddLocal(ltx); err != nil { t.Fatalf("failed to add new underpriced local transaction: %v", err) } + pending, queued = pool.Stats() if pending != 3 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) } + if queued != 1 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) } + if err := validateEvents(events, 2); err != nil { t.Fatalf("local event firing failed: %v", err) } + if err := validateTxPoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -2024,12 +2042,15 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { if pending != 3 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) } + if queued != 1 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) } + if err := validateEvents(events, 3); err != nil { t.Fatalf("original event firing failed: %v", err) } + if err := validateTxPoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } @@ -2050,42 +2071,53 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) { if err := pool.AddRemote(tx); err != nil { // +K1:2, -K0:1 => Pend K0:0 K1:0, K2:0; Que K1:2 t.Fatalf("failed to add well priced transaction: %v", err) } + tx = dynamicFeeTx(3, 100000, big.NewInt(4), big.NewInt(1), keys[1]) if err := pool.AddRemote(tx); err != nil { // +K1:3, -K1:0 => Pend K0:0 K2:0; Que K1:2 K1:3 t.Fatalf("failed to add well priced transaction: %v", err) } + pending, queued = pool.Stats() if pending != 2 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) } + if queued != 2 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) } + if err := validateEvents(events, 1); err != nil { t.Fatalf("additional event firing failed: %v", err) } + if err := validateTxPoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } + // Ensure that adding local transactions can push out even higher priced ones ltx = dynamicFeeTx(1, 100000, big.NewInt(0), big.NewInt(0), keys[2]) if err := pool.AddLocal(ltx); err != nil { t.Fatalf("failed to append underpriced local transaction: %v", err) } + ltx = dynamicFeeTx(0, 100000, big.NewInt(0), big.NewInt(0), keys[3]) if err := pool.AddLocal(ltx); err != nil { t.Fatalf("failed to add new underpriced local transaction: %v", err) } + pending, queued = pool.Stats() if pending != 3 { t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) } + if queued != 1 { t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1) } + if err := validateEvents(events, 2); err != nil { t.Fatalf("local event firing failed: %v", err) } + if err := validateTxPoolInternals(pool); err != nil { t.Fatalf("pool internal state corrupted: %v", err) } diff --git a/core/types/block.go b/core/types/block.go index 314990dc99..898fe9d7d2 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -155,6 +155,42 @@ func (h *Header) EmptyReceipts() bool { return h.ReceiptHash == EmptyRootHash } +func (h *Header) ValidateBlockNumberOptions4337(minBlockNumber *big.Int, maxBlockNumber *big.Int) error { + currentBlockNumber := h.Number + + if minBlockNumber != nil { + if currentBlockNumber.Cmp(minBlockNumber) == -1 { + return fmt.Errorf("current block number %v is less than minimum block number: %v", currentBlockNumber, minBlockNumber) + } + } + + if maxBlockNumber != nil { + if currentBlockNumber.Cmp(maxBlockNumber) == 1 { + return fmt.Errorf("current block number %v is greater than maximum block number: %v", currentBlockNumber, maxBlockNumber) + } + } + + return nil +} + +func (h *Header) ValidateTimestampOptions4337(minTimestamp *uint64, maxTimestamp *uint64) error { + currentBlockTime := h.Time + + if minTimestamp != nil { + if currentBlockTime < *minTimestamp { + return fmt.Errorf("current block time %v is less than minimum timestamp: %v", currentBlockTime, minTimestamp) + } + } + + if maxTimestamp != nil { + if currentBlockTime > *maxTimestamp { + return fmt.Errorf("current block time %v is greater than maximum timestamp: %v", currentBlockTime, maxTimestamp) + } + } + + return nil +} + // Body is a simple (mutable, non-safe) data container for storing and moving // a block's data contents (transactions and uncles) together. type Body struct { diff --git a/core/types/block_test.go b/core/types/block_test.go index aa1db2f4fa..0e7bb3bc93 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -342,3 +342,167 @@ func TestRlpDecodeParentHash(t *testing.T) { } } } + +func TestValidateBlockNumberOptions4337(t *testing.T) { + t.Parallel() + + testsPass := []struct { + number string + header Header + minBlockNumber *big.Int + maxBlockNumber *big.Int + }{ + { + "1", + Header{Number: big.NewInt(10)}, + big.NewInt(0), + big.NewInt(20), + }, + { + "2", + Header{Number: big.NewInt(10)}, + big.NewInt(10), + big.NewInt(10), + }, + { + "3", + Header{Number: big.NewInt(10)}, + big.NewInt(10), + big.NewInt(11), + }, + { + "4", + Header{Number: big.NewInt(10)}, + big.NewInt(0), + big.NewInt(10), + }, + } + + testsFail := []struct { + number string + header Header + minBlockNumber *big.Int + maxBlockNumber *big.Int + }{ + { + "5", + Header{Number: big.NewInt(10)}, + big.NewInt(0), + big.NewInt(0), + }, + { + "6", + Header{Number: big.NewInt(10)}, + big.NewInt(0), + big.NewInt(9), + }, + { + "7", + Header{Number: big.NewInt(10)}, + big.NewInt(11), + big.NewInt(9), + }, + { + "8", + Header{Number: big.NewInt(10)}, + big.NewInt(11), + big.NewInt(20), + }, + } + + for _, test := range testsPass { + if err := test.header.ValidateBlockNumberOptions4337(test.minBlockNumber, test.maxBlockNumber); err != nil { + t.Fatalf("test number %v should not have failed. err: %v", test.number, err) + } + } + + for _, test := range testsFail { + if err := test.header.ValidateBlockNumberOptions4337(test.minBlockNumber, test.maxBlockNumber); err == nil { + t.Fatalf("test number %v should have failed. err is nil", test.number) + } + } +} + +func TestValidateTimestampOptions4337(t *testing.T) { + t.Parallel() + + u64Ptr := func(n uint64) *uint64 { + return &n + } + + testsPass := []struct { + number string + header Header + minTimestamp *uint64 + maxTimestamp *uint64 + }{ + { + "1", + Header{Time: 1600000000}, + u64Ptr(1500000000), + u64Ptr(1700000000), + }, + { + "2", + Header{Time: 1600000000}, + u64Ptr(1600000000), + u64Ptr(1600000000), + }, + { + "3", + Header{Time: 1600000000}, + u64Ptr(1600000000), + u64Ptr(1700000000), + }, + { + "4", + Header{Time: 1600000000}, + u64Ptr(1500000000), + u64Ptr(1600000000), + }, + } + + testsFail := []struct { + number string + header Header + minTimestamp *uint64 + maxTimestamp *uint64 + }{ + { + "5", + Header{Time: 1600000000}, + u64Ptr(1500000000), + u64Ptr(1500000000), + }, + { + "6", + Header{Time: 1600000000}, + u64Ptr(1400000000), + u64Ptr(1500000000), + }, + { + "7", + Header{Time: 1600000000}, + u64Ptr(1700000000), + u64Ptr(1500000000), + }, + { + "8", + Header{Time: 1600000000}, + u64Ptr(1700000000), + u64Ptr(1800000000), + }, + } + + for _, test := range testsPass { + if err := test.header.ValidateTimestampOptions4337(test.minTimestamp, test.maxTimestamp); err != nil { + t.Fatalf("test number %v should not have failed. err: %v", test.number, err) + } + } + + for _, test := range testsFail { + if err := test.header.ValidateTimestampOptions4337(test.minTimestamp, test.maxTimestamp); err == nil { + t.Fatalf("test number %v should have failed. err is nil", test.number) + } + } +} diff --git a/core/types/transaction.go b/core/types/transaction.go index 9b89f12517..ab401ddda2 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -54,6 +54,9 @@ type Transaction struct { inner TxData // Consensus contents of a transaction time time.Time // Time first seen locally (spam avoidance) + // knownAccounts (EIP-4337) + optionsAA4337 *OptionsAA4337 + // caches hash atomic.Pointer[common.Hash] size atomic.Pointer[common.StorageSize] @@ -92,6 +95,14 @@ type TxData interface { setSignatureValues(chainID, v, r, s *big.Int) } +func (tx *Transaction) PutOptions(options *OptionsAA4337) { + tx.optionsAA4337 = options +} + +func (tx *Transaction) GetOptions() *OptionsAA4337 { + return tx.optionsAA4337 +} + // EncodeRLP implements rlp.Encoder func (tx *Transaction) EncodeRLP(w io.Writer) error { if tx.Type() == LegacyTxType { diff --git a/core/types/transaction_conditional.go b/core/types/transaction_conditional.go new file mode 100644 index 0000000000..a0d86e2f76 --- /dev/null +++ b/core/types/transaction_conditional.go @@ -0,0 +1,163 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +type KnownAccounts map[common.Address]*Value + +type Value struct { + Single *common.Hash + Storage map[common.Hash]common.Hash +} + +func SingleFromHex(hex string) *Value { + return &Value{Single: common.HexToRefHash(hex)} +} + +func FromMap(m map[string]string) *Value { + res := map[common.Hash]common.Hash{} + + for k, v := range m { + res[common.HexToHash(k)] = common.HexToHash(v) + } + + return &Value{Storage: res} +} + +func (v *Value) IsSingle() bool { + return v != nil && v.Single != nil && !v.IsStorage() +} + +func (v *Value) IsStorage() bool { + return v != nil && v.Storage != nil +} + +const EmptyValue = "{}" + +func (v *Value) MarshalJSON() ([]byte, error) { + if v.IsSingle() { + return json.Marshal(v.Single) + } + + if v.IsStorage() { + return json.Marshal(v.Storage) + } + + return []byte(EmptyValue), nil +} + +const hashTypeName = "Hash" + +func (v *Value) UnmarshalJSON(data []byte) error { + if len(data) == 0 { + return nil + } + + var m map[string]json.RawMessage + + err := json.Unmarshal(data, &m) + if err != nil { + // single Hash value case + v.Single = new(common.Hash) + + innerErr := json.Unmarshal(data, v.Single) + if innerErr != nil { + return fmt.Errorf("can't unmarshal to single value with error: %v value %q", innerErr, string(data)) + } + + return nil + } + + res := make(map[common.Hash]common.Hash, len(m)) + + for k, v := range m { + // check k if it is a Hex value + var kHash common.Hash + + err = hexutil.UnmarshalFixedText(hashTypeName, []byte(k), kHash[:]) + if err != nil { + return fmt.Errorf("%w by key: %s with key %q and value %q", ErrKnownAccounts, err, k, string(v)) + } + + // check v if it is a Hex value + var vHash common.Hash + + err = hexutil.UnmarshalFixedText("hashTypeName", bytes.Trim(v, "\""), vHash[:]) + if err != nil { + return fmt.Errorf("%w by value: %s with key %q and value %q", ErrKnownAccounts, err, k, string(v)) + } + + res[kHash] = vHash + } + + v.Storage = res + + return nil +} + +func InsertKnownAccounts[T common.Hash | map[common.Hash]common.Hash](accounts KnownAccounts, k common.Address, v T) { + switch typedV := any(v).(type) { + case common.Hash: + accounts[k] = &Value{Single: &typedV} + case map[common.Hash]common.Hash: + accounts[k] = &Value{Storage: typedV} + } +} + +type OptionsAA4337 struct { + KnownAccounts KnownAccounts `json:"knownAccounts"` + BlockNumberMin *big.Int `json:"blockNumberMin"` + BlockNumberMax *big.Int `json:"blockNumberMax"` + TimestampMin *uint64 `json:"timestampMin"` + TimestampMax *uint64 `json:"timestampMax"` +} + +var ErrEmptyKnownAccounts = errors.New("knownAccounts cannot be nil") +var ErrKnownAccounts = errors.New("an incorrect list of knownAccounts") + +func (ka KnownAccounts) ValidateLength() error { + if ka == nil { + return ErrEmptyKnownAccounts + } + + length := 0 + + for _, v := range ka { + // check if the value is hex string or an object + if v.IsSingle() { + length += 1 + } else { + length += len(v.Storage) + } + } + + if length >= 1000 { + return fmt.Errorf("number of slots/accounts in KnownAccounts %v exceeds the limit of 1000", length) + } + + return nil +} diff --git a/core/types/transaction_conditional_test.go b/core/types/transaction_conditional_test.go new file mode 100644 index 0000000000..1789095ac9 --- /dev/null +++ b/core/types/transaction_conditional_test.go @@ -0,0 +1,47 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" +) + +func TestKnownAccounts(t *testing.T) { + t.Parallel() + + requestRaw := []byte(`{"0xadd1add1add1add1add1add1add1add1add1add1": "0x000000000000000000000000313aadca1750caadc7bcb26ff08175c95dcf8e38", "0xadd2add2add2add2add2add2add2add2add2add2": {"0x0000000000000000000000000000000000000000000000000000000000000aaa": "0x0000000000000000000000000000000000000000000000000000000000000bbb", "0x0000000000000000000000000000000000000000000000000000000000000ccc": "0x0000000000000000000000000000000000000000000000000000000000000ddd"}}`) + + accs := &KnownAccounts{} + + err := json.Unmarshal(requestRaw, accs) + require.NoError(t, err) + + expected := &KnownAccounts{ + common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"): SingleFromHex("0x000000000000000000000000313aadca1750caadc7bcb26ff08175c95dcf8e38"), + common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"): FromMap(map[string]string{ + "0x0000000000000000000000000000000000000000000000000000000000000aaa": "0x0000000000000000000000000000000000000000000000000000000000000bbb", + "0x0000000000000000000000000000000000000000000000000000000000000ccc": "0x0000000000000000000000000000000000000000000000000000000000000ddd", + }), + } + + require.Equal(t, expected, accs) +} diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 9210f5486c..f418dafcb1 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -266,9 +266,10 @@ var ( // modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198 // // def mult_complexity(x): -// if x <= 64: return x ** 2 -// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 -// else: return x ** 2 // 16 + 480 * x - 199680 +// +// if x <= 64: return x ** 2 +// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 +// else: return x ** 2 // 16 + 480 * x - 199680 // // where is x is max(length_of_MODULUS, length_of_BASE) func modexpMultComplexity(x *big.Int) *big.Int { @@ -383,10 +384,12 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) ) + if mod.BitLen() == 0 { // Modulo 0 is undefined, return zero return common.LeftPadBytes([]byte{}, int(modLen)), nil } + return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil } diff --git a/eth/filters/bor_api.go b/eth/filters/bor_api.go index db13c95959..fe0f682a3b 100644 --- a/eth/filters/bor_api.go +++ b/eth/filters/bor_api.go @@ -1,7 +1,6 @@ package filters import ( - "bytes" "context" "errors" @@ -67,7 +66,7 @@ func (api *PublicFilterAPI) NewDeposits(ctx context.Context, crit ethereum.State for { select { case h := <-stateSyncData: - if crit.ID == h.ID || bytes.Compare(crit.Contract.Bytes(), h.Contract.Bytes()) == 0 || + if crit.ID == h.ID || crit.Contract == h.Contract || (crit.ID == 0 && crit.Contract == common.Address{}) { notifier.Notify(rpcSub.ID, h) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6a3372579f..03363236d0 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "math/big" - "reflect" "runtime" "strings" "time" @@ -1931,35 +1930,38 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, input // SendRawTransactionConditional will add the signed transaction to the transaction pool. // The sender/bundler is responsible for signing the transaction -func (s *PublicTransactionPoolAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, knownAccounts map[string]map[common.Address]interface{}) (common.Hash, error) { +func (s *PublicTransactionPoolAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options types.OptionsAA4337) (common.Hash, error) { tx := new(types.Transaction) if err := tx.UnmarshalBinary(input); err != nil { return common.Hash{}, err } + currentHeader := s.b.CurrentHeader() + currentState, _, _ := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(currentHeader.Number.Int64())) + + // check block number range + if err := currentHeader.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil { + return common.Hash{}, &rpc.OptionsValidateError{Message: "out of block range. err: " + err.Error()} + } + + // check timestamp range + if err := currentHeader.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil { + return common.Hash{}, &rpc.OptionsValidateError{Message: "out of time range. err: " + err.Error()} + } + + // check knownAccounts length (number of slots/accounts) should be less than 1000 + if err := options.KnownAccounts.ValidateLength(); err != nil { + return common.Hash{}, &rpc.KnownAccountsLimitExceededError{Message: "limit exceeded. err: " + err.Error()} + } + // check knownAccounts - currentBlock := s.b.CurrentBlock() - currentState, _, _ := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(currentBlock.Number().Int64())) - - for k, v := range knownAccounts["knownAccounts"] { - // check if the value is hex string or an object - if object, ok := v.(string); ok { - actualRootHash := currentState.StorageTrie(k).Hash() - if common.HexToHash(object) != actualRootHash { - return common.Hash{}, fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, common.HexToHash(object), actualRootHash) - } - } else if object, ok := v.(map[string]interface{}); ok { - for slot, value := range object { - actualValue := currentState.GetState(k, common.HexToHash(slot)) - if common.HexToHash(value.(string)) != actualValue { - return common.Hash{}, fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", k, slot, value, actualValue) - } - } - } else { - return common.Hash{}, fmt.Errorf("invalid type in knownAccounts %v", reflect.TypeOf(v)) - } + if err := currentState.ValidateKnownAccounts(options.KnownAccounts); err != nil { + return common.Hash{}, &rpc.OptionsValidateError{Message: "storage error. err: " + err.Error()} } + // put options data in Tx, to use it later while block building + tx.PutOptions(&options) + return SubmitTransaction(ctx, s.b, tx) } diff --git a/miner/worker.go b/miner/worker.go index cc6a2e1eec..a977d17a40 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1001,6 +1001,33 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP // // We use the eip155 signer regardless of the current hf. from, _ := types.Sender(env.signer, tx) + + // not prioritising conditional transaction, yet. + + //nolint:nestif + if options := tx.GetOptions(); options != nil { + if err := env.header.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil { + log.Trace("Dropping conditional transaction from", from, "with hash", tx.Hash(), "reason", err) + txs.Pop() + + continue + } + + if err := env.header.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil { + log.Trace("Dropping conditional transaction from", from, "with hash", tx.Hash(), "reason", err) + txs.Pop() + + continue + } + + if err := env.state.ValidateKnownAccounts(options.KnownAccounts); err != nil { + log.Trace("Dropping conditional transaction from", from, "with hash", tx.Hash(), "reason", err) + txs.Pop() + + continue + } + } + // Check whether the tx is replay protected. If we're not in the EIP155 hf // phase, start ignoring the sender until we do. if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { diff --git a/rpc/errors.go b/rpc/errors.go index 75425b925a..7007cf0c77 100644 --- a/rpc/errors.go +++ b/rpc/errors.go @@ -111,3 +111,15 @@ type CustomError struct { func (e *CustomError) ErrorCode() int { return e.Code } func (e *CustomError) Error() string { return e.ValidationError } + +type OptionsValidateError struct{ Message string } + +func (e *OptionsValidateError) ErrorCode() int { return -32003 } + +func (e *OptionsValidateError) Error() string { return e.Message } + +type KnownAccountsLimitExceededError struct{ Message string } + +func (e *KnownAccountsLimitExceededError) ErrorCode() int { return -32005 } + +func (e *KnownAccountsLimitExceededError) Error() string { return e.Message } diff --git a/rpc/handler.go b/rpc/handler.go index 488a29300a..bc25e55256 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -34,21 +34,20 @@ import ( // // The entry points for incoming messages are: // -// h.handleMsg(message) -// h.handleBatch(message) +// h.handleMsg(message) +// h.handleBatch(message) // // Outgoing calls use the requestOp struct. Register the request before sending it // on the connection: // -// op := &requestOp{ids: ...} -// h.addRequestOp(op) +// op := &requestOp{ids: ...} +// h.addRequestOp(op) // // Now send the request, then wait for the reply to be delivered through handleMsg: // -// if err := op.wait(...); err != nil { -// h.removeRequestOp(op) // timeout, etc. -// } -// +// if err := op.wait(...); err != nil { +// h.removeRequestOp(op) // timeout, etc. +// } type handler struct { reg *serviceRegistry unsubscribeCb *callback @@ -219,10 +218,12 @@ func (h *handler) cancelServerSubscriptions(err error) { // startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group. func (h *handler) startCallProc(fn func(*callProc)) { h.callWG.Add(1) + go func() { ctx, cancel := context.WithCancel(h.rootCtx) defer h.callWG.Done() defer cancel() + fn(&callProc{ctx: ctx}) }() } diff --git a/rpc/inproc.go b/rpc/inproc.go index fbe9a40cec..ccb1ac598c 100644 --- a/rpc/inproc.go +++ b/rpc/inproc.go @@ -26,6 +26,9 @@ func DialInProc(handler *Server) *Client { initctx := context.Background() c, _ := newClient(initctx, func(context.Context) (ServerCodec, error) { p1, p2 := net.Pipe() + + //fixme: it should take context with timeout and cancellation + //nolint: contextcheck go handler.ServeCodec(NewCodec(p1), 0) return NewCodec(p2), nil }) diff --git a/rpc/ipc.go b/rpc/ipc.go index 07a211c627..dac7da955b 100644 --- a/rpc/ipc.go +++ b/rpc/ipc.go @@ -30,11 +30,14 @@ func (s *Server) ServeListener(l net.Listener) error { conn, err := l.Accept() if netutil.IsTemporaryError(err) { log.Warn("RPC accept error", "err", err) + continue } else if err != nil { return err } + log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr()) + go s.ServeCodec(NewCodec(conn), 0) } } diff --git a/rpc/server.go b/rpc/server.go index babc5688e2..ed9375c1e3 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -22,6 +22,7 @@ import ( "sync/atomic" mapset "github.com/deckarep/golang-set" + "github.com/ethereum/go-ethereum/log" ) @@ -105,11 +106,16 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { reqs, batch, err := codec.readBatch() if err != nil { if err != io.EOF { - codec.writeJSON(ctx, errorMessage(&invalidMessageError{"parse error"})) + innerErr := codec.writeJSON(ctx, errorMessage(&invalidMessageError{"parse error"})) //nolint:errcheck + if innerErr != nil { + log.Warn("got error while returning a response error", "originalErr", err, "returnErr", innerErr) + } } return } if batch { + //fixme: it should take context with timeout and cancellation + //nolint: contextcheck h.handleBatch(reqs) } else { h.handleMsg(reqs[0]) From 0f9d7d61c52be18daea66b06f61c43a0a55e135d Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 4 Jul 2023 10:01:59 +0530 Subject: [PATCH 03/18] Added filtering of conditional transactions in txpool (#920) * added filtering of conditional transactions in txpool * minor fix in ValidateKnownAccounts --- core/state/statedb.go | 11 ++++++-- core/tx_list.go | 27 ++++++++++++++++++ core/tx_list_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++ core/tx_pool.go | 12 ++++++-- 4 files changed, 111 insertions(+), 5 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 602d0519c0..6dc34d4aee 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1439,9 +1439,14 @@ func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error // check if the value is hex string or an object switch { case v.IsSingle(): - actualRootHash := s.StorageTrie(k).Hash() - if *v.Single != actualRootHash { - return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, actualRootHash) + trie := s.StorageTrie(k) + if trie != nil { + actualRootHash := trie.Hash() + if *v.Single != actualRootHash { + return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, actualRootHash) + } + } else { + return fmt.Errorf("Storage Trie is nil for: %v", k) } case v.IsStorage(): for slot, value := range v.Storage { diff --git a/core/tx_list.go b/core/tx_list.go index 851f732905..65171ac710 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" ) @@ -555,6 +556,32 @@ func (l *txList) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transact return removed, invalids } +// Returns the conditional transactions with invalid KnownAccounts +// TODO - We will also have to check block range and time stamp range! +func (l *txList) FilterTxConditional(state *state.StateDB) types.Transactions { + removed := l.txs.filter(func(tx *types.Transaction) bool { + if options := tx.GetOptions(); options != nil { + err := state.ValidateKnownAccounts(options.KnownAccounts) + if err != nil { + log.Error("Error while Filtering Tx Conditional", "err", err) + return true + } + + return false + } + + return false + }) + + if len(removed) == 0 { + return nil + } + + l.txs.reheap() + + return removed +} + // Cap places a hard limit on the number of items, returning all transactions // exceeding that limit. func (l *txList) Cap(threshold int) types.Transactions { diff --git a/core/tx_list_test.go b/core/tx_list_test.go index 80b8c1ef32..daafd18721 100644 --- a/core/tx_list_test.go +++ b/core/tx_list_test.go @@ -22,6 +22,9 @@ import ( "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" ) @@ -75,3 +78,66 @@ func BenchmarkTxListAdd(b *testing.B) { } } } + +func TestFilterTxConditional(t *testing.T) { + // Create an in memory state db to test against. + memDb := rawdb.NewMemoryDatabase() + db := state.NewDatabase(memDb) + state, _ := state.New(common.Hash{}, db, nil) + + // Create a private key to sign transactions. + key, _ := crypto.GenerateKey() + + // Create a list. + list := newTxList(true) + + // Create a transaction with no defined tx options + // and add to the list. + tx := transaction(0, 1000, key) + list.Add(tx, DefaultTxPoolConfig.PriceBump) + + // There should be no drops at this point. + // No state has been modified. + drops := list.FilterTxConditional(state) + + if count := len(drops); count != 0 { + t.Fatalf("got %d filtered by TxOptions when there should not be any", count) + } + + // Create another transaction with a known account storage root tx option + // and add to the list. + tx2 := transaction(1, 1000, key) + + var options types.OptionsAA4337 + + options.KnownAccounts = types.KnownAccounts{ + common.Address{19: 1}: &types.Value{ + Single: common.HexToRefHash("0xe734938daf39aae1fa4ee64dc3155d7c049f28b57a8ada8ad9e86832e0253bef"), + }, + } + + state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{30: 1}) + tx2.PutOptions(&options) + list.Add(tx2, DefaultTxPoolConfig.PriceBump) + + // There should still be no drops as no state has been modified. + drops = list.FilterTxConditional(state) + + if count := len(drops); count != 0 { + t.Fatalf("got %d filtered by TxOptions when there should not be any", count) + } + + // Set state that conflicts with tx2's policy + state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{31: 1}) + + // tx2 should be the single transaction filtered out + drops = list.FilterTxConditional(state) + + if count := len(drops); count != 1 { + t.Fatalf("got %d filtered by TxOptions when there should be a single one", count) + } + + if drops[0] != tx2 { + t.Fatalf("Got %x, expected %x", drops[0].Hash(), tx2.Hash()) + } +} diff --git a/core/tx_pool.go b/core/tx_pool.go index f33f075260..a63baeaac0 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -2199,10 +2199,18 @@ func (pool *TxPool) demoteUnexecutables() { pool.enqueueTx(hash, tx, false, false) } - pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen)) + // Drop all transactions that no longer have valid TxOptions + txConditionalsRemoved := list.FilterTxConditional(pool.currentState) + + for _, tx := range txConditionalsRemoved { + hash := tx.Hash() + log.Trace("Removed invalid conditional transaction", "hash", hash) + pool.all.Remove(hash) + } + pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved))) if pool.locals.contains(addr) { - localGauge.Dec(int64(oldsLen + dropsLen + invalidsLen)) + localGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved))) } // If there's a gap in front, alert (should never happen) and postpone all transactions if list.Len() > 0 && list.txs.Get(nonce) == nil { From 4b086aa5d0ec0cfbda86b5ad7f299c1b2caaf2e9 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 25 Jul 2023 17:41:27 +0530 Subject: [PATCH 04/18] bug fix --- core/tx_list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tx_list.go b/core/tx_list.go index 65171ac710..d86808c526 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -577,7 +577,7 @@ func (l *txList) FilterTxConditional(state *state.StateDB) types.Transactions { return nil } - l.txs.reheap() + l.txs.reheap(true) return removed } From 10ed2d92f9aa473dba3d0f522d020f29dbf2f2b7 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Mon, 31 Jul 2023 14:54:13 +0530 Subject: [PATCH 05/18] Supporting nil knownAccounts --- core/state/statedb.go | 2 +- core/types/transaction_conditional.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 6dc34d4aee..b3706c60ca 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1432,7 +1432,7 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error { if knownAccounts == nil { - return types.ErrEmptyKnownAccounts + return nil } for k, v := range knownAccounts { diff --git a/core/types/transaction_conditional.go b/core/types/transaction_conditional.go index a0d86e2f76..42bd926abb 100644 --- a/core/types/transaction_conditional.go +++ b/core/types/transaction_conditional.go @@ -136,12 +136,11 @@ type OptionsAA4337 struct { TimestampMax *uint64 `json:"timestampMax"` } -var ErrEmptyKnownAccounts = errors.New("knownAccounts cannot be nil") var ErrKnownAccounts = errors.New("an incorrect list of knownAccounts") func (ka KnownAccounts) ValidateLength() error { if ka == nil { - return ErrEmptyKnownAccounts + return nil } length := 0 From 0d2def5f2d0c9f9da12d1a7d06e518b77d9b08b7 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Mon, 31 Jul 2023 16:12:50 +0530 Subject: [PATCH 06/18] lints --- core/tx_list_test.go | 2 ++ core/tx_pool.go | 1 + 2 files changed, 3 insertions(+) diff --git a/core/tx_list_test.go b/core/tx_list_test.go index daafd18721..2de805ac53 100644 --- a/core/tx_list_test.go +++ b/core/tx_list_test.go @@ -80,6 +80,8 @@ func BenchmarkTxListAdd(b *testing.B) { } func TestFilterTxConditional(t *testing.T) { + t.Parallel() + // Create an in memory state db to test against. memDb := rawdb.NewMemoryDatabase() db := state.NewDatabase(memDb) diff --git a/core/tx_pool.go b/core/tx_pool.go index a63baeaac0..cf5a0f137e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -2207,6 +2207,7 @@ func (pool *TxPool) demoteUnexecutables() { log.Trace("Removed invalid conditional transaction", "hash", hash) pool.all.Remove(hash) } + pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved))) if pool.locals.contains(addr) { From 6826ae748ba95c4b1a38af2e269ec893c6c61a69 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Wed, 9 Aug 2023 09:54:13 +0530 Subject: [PATCH 07/18] bundled transactions are not announced/broadcasted to the peers --- eth/protocols/eth/broadcast.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go index 614e6361c6..ba2f37832a 100644 --- a/eth/protocols/eth/broadcast.go +++ b/eth/protocols/eth/broadcast.go @@ -80,7 +80,10 @@ func (p *Peer) broadcastTransactions() { size common.StorageSize ) for i := 0; i < len(queue) && size < maxTxPacketSize; i++ { - if tx := p.txpool.Get(queue[i]); tx != nil { + tx := p.txpool.Get(queue[i]) + + // Skip EIP-4337 bundled transactions + if tx != nil && tx.GetOptions() == nil { txs = append(txs, tx) size += tx.Size() } @@ -147,7 +150,10 @@ func (p *Peer) announceTransactions() { size common.StorageSize ) for count = 0; count < len(queue) && size < maxTxPacketSize; count++ { - if p.txpool.Get(queue[count]) != nil { + tempTx := p.txpool.Get(queue[count]) + + // Skip EIP-4337 bundled transactions + if tempTx != nil && tempTx.GetOptions() == nil { pending = append(pending, queue[count]) size += common.HashLength } From 99cb89093fc012fbd0a9ae7fd898d876739f5571 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Wed, 9 Aug 2023 11:42:09 +0530 Subject: [PATCH 08/18] fixed after upstream merge --- accounts/abi/bind/backends/simulated.go | 3 +- core/state/state_test.go | 3 +- core/txpool/list.go | 2 +- core/txpool/list_test.go | 6 +- internal/ethapi/api.go | 2 +- mobile/ethclient.go | 321 ------------------------ 6 files changed, 9 insertions(+), 328 deletions(-) delete mode 100644 mobile/ethclient.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 8e7313fca3..d3254896c1 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -759,7 +759,8 @@ func (b *SimulatedBackend) SendTransactionConditional(ctx context.Context, tx *t for k, v := range knownAccounts["knownAccounts"] { // check if the value is hex string or an object if object, ok := v.(string); ok { - actualRootHash := currentState.StorageTrie(k).Hash() + trie, _ := currentState.StorageTrie(k) + actualRootHash := trie.Hash() if common.HexToHash(object) != actualRootHash { return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, common.HexToHash(object), actualRootHash) } diff --git a/core/state/state_test.go b/core/state/state_test.go index e2876e700d..268de63609 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -305,7 +305,8 @@ func TestValidateKnownAccounts(t *testing.T) { s.state.SetState(stateobjaddr2, storageaddr21, data21) s.state.SetState(stateobjaddr2, storageaddr22, data22) - fmt.Println("\nStorageTrie.Hash()", s.state.StorageTrie(stateobjaddr1).Hash()) + trieTemp, _ := s.state.StorageTrie(stateobjaddr1) + fmt.Println("\nStorageTrie.Hash()", trieTemp.Hash()) if err := s.state.ValidateKnownAccounts(knownAccounts); err != nil { t.Fatalf(err.Error()) diff --git a/core/txpool/list.go b/core/txpool/list.go index 2f79a4f09d..f9bd1e1c1e 100644 --- a/core/txpool/list.go +++ b/core/txpool/list.go @@ -566,7 +566,7 @@ func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactio // Returns the conditional transactions with invalid KnownAccounts // TODO - We will also have to check block range and time stamp range! -func (l *txList) FilterTxConditional(state *state.StateDB) types.Transactions { +func (l *list) FilterTxConditional(state *state.StateDB) types.Transactions { removed := l.txs.filter(func(tx *types.Transaction) bool { if options := tx.GetOptions(); options != nil { err := state.ValidateKnownAccounts(options.KnownAccounts) diff --git a/core/txpool/list_test.go b/core/txpool/list_test.go index 1e6b8f56e4..0553534f44 100644 --- a/core/txpool/list_test.go +++ b/core/txpool/list_test.go @@ -94,12 +94,12 @@ func TestFilterTxConditional(t *testing.T) { key, _ := crypto.GenerateKey() // Create a list. - list := newTxList(true) + list := newList(true) // Create a transaction with no defined tx options // and add to the list. tx := transaction(0, 1000, key) - list.Add(tx, DefaultTxPoolConfig.PriceBump) + list.Add(tx, DefaultConfig.PriceBump) // There should be no drops at this point. // No state has been modified. @@ -123,7 +123,7 @@ func TestFilterTxConditional(t *testing.T) { state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{30: 1}) tx2.PutOptions(&options) - list.Add(tx2, DefaultTxPoolConfig.PriceBump) + list.Add(tx2, DefaultConfig.PriceBump) // There should still be no drops as no state has been modified. drops = list.FilterTxConditional(state) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 639daad79e..3f007a0772 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2236,7 +2236,7 @@ func (s *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.B // SendRawTransactionConditional will add the signed transaction to the transaction pool. // The sender/bundler is responsible for signing the transaction -func (s *PublicTransactionPoolAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options types.OptionsAA4337) (common.Hash, error) { +func (s *TransactionAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options types.OptionsAA4337) (common.Hash, error) { tx := new(types.Transaction) if err := tx.UnmarshalBinary(input); err != nil { return common.Hash{}, err diff --git a/mobile/ethclient.go b/mobile/ethclient.go deleted file mode 100644 index 53202b254b..0000000000 --- a/mobile/ethclient.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains a wrapper for the Ethereum client. - -package geth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" -) - -// EthereumClient provides access to the Ethereum APIs. -type EthereumClient struct { - client *ethclient.Client -} - -// NewEthereumClient connects a client to the given URL. -func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) { - rawClient, err := ethclient.Dial(rawurl) - return &EthereumClient{rawClient}, err -} - -// GetBlockByHash returns the given full block. -func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (block *Block, _ error) { - rawBlock, err := ec.client.BlockByHash(ctx.context, hash.hash) - return &Block{rawBlock}, err -} - -// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the -// latest known block is returned. -func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (block *Block, _ error) { - if number < 0 { - rawBlock, err := ec.client.BlockByNumber(ctx.context, nil) - return &Block{rawBlock}, err - } - rawBlock, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number)) - return &Block{rawBlock}, err -} - -// GetHeaderByHash returns the block header with the given hash. -func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (header *Header, _ error) { - rawHeader, err := ec.client.HeaderByHash(ctx.context, hash.hash) - return &Header{rawHeader}, err -} - -// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0, -// the latest known header is returned. -func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (header *Header, _ error) { - if number < 0 { - rawHeader, err := ec.client.HeaderByNumber(ctx.context, nil) - return &Header{rawHeader}, err - } - rawHeader, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number)) - return &Header{rawHeader}, err -} - -// GetTransactionByHash returns the transaction with the given hash. -func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Transaction, _ error) { - // TODO(karalabe): handle isPending - rawTx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash) - return &Transaction{rawTx}, err -} - -// GetTransactionSender returns the sender address of a transaction. The transaction must -// be included in blockchain at the given block and index. -func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction, blockhash *Hash, index int) (sender *Address, _ error) { - addr, err := ec.client.TransactionSender(ctx.context, tx.tx, blockhash.hash, uint(index)) - return &Address{addr}, err -} - -// GetTransactionCount returns the total number of transactions in the given block. -func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) { - rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash) - return int(rawCount), err -} - -// GetTransactionInBlock returns a single transaction at index in the given block. -func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (tx *Transaction, _ error) { - rawTx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index)) - return &Transaction{rawTx}, err - -} - -// GetTransactionReceipt returns the receipt of a transaction by transaction hash. -// Note that the receipt is not available for pending transactions. -func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (receipt *Receipt, _ error) { - rawReceipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash) - return &Receipt{rawReceipt}, err -} - -// SyncProgress retrieves the current progress of the sync algorithm. If there's -// no sync currently running, it returns nil. -func (ec *EthereumClient) SyncProgress(ctx *Context) (progress *SyncProgress, _ error) { - rawProgress, err := ec.client.SyncProgress(ctx.context) - if rawProgress == nil { - return nil, err - } - return &SyncProgress{*rawProgress}, err -} - -// NewHeadHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type NewHeadHandler interface { - OnNewHead(header *Header) - OnError(failure string) -} - -// SubscribeNewHead subscribes to notifications about the current blockchain head -// on the given channel. -func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan *types.Header, buffer) - rawSub, err := ec.client.SubscribeNewHead(ctx.context, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case header := <-ch: - handler.OnNewHead(&Header{header}) - - case err := <-rawSub.Err(): - if err != nil { - handler.OnError(err.Error()) - } - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// State Access - -// GetBalanceAt returns the wei balance of the given account. -// The block number can be <0, in which case the balance is taken from the latest known block. -func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (balance *BigInt, _ error) { - if number < 0 { - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, nil) - return &BigInt{rawBalance}, err - } - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number)) - return &BigInt{rawBalance}, err -} - -// GetStorageAt returns the value of key in the contract storage of the given account. -// The block number can be <0, in which case the value is taken from the latest known block. -func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) (storage []byte, _ error) { - if number < 0 { - return ec.client.StorageAt(ctx.context, account.address, key.hash, nil) - } - return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number)) -} - -// GetCodeAt returns the contract code of the given account. -// The block number can be <0, in which case the code is taken from the latest known block. -func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) (code []byte, _ error) { - if number < 0 { - return ec.client.CodeAt(ctx.context, account.address, nil) - } - return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number)) -} - -// GetNonceAt returns the account nonce of the given account. -// The block number can be <0, in which case the nonce is taken from the latest known block. -func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (nonce int64, _ error) { - if number < 0 { - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, nil) - return int64(rawNonce), err - } - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number)) - return int64(rawNonce), err -} - -// Filters - -// FilterLogs executes a filter query. -func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (logs *Logs, _ error) { - rawLogs, err := ec.client.FilterLogs(ctx.context, query.query) - if err != nil { - return nil, err - } - // Temp hack due to vm.Logs being []*vm.Log - res := make([]*types.Log, len(rawLogs)) - for i := range rawLogs { - res[i] = &rawLogs[i] - } - return &Logs{res}, nil -} - -// FilterLogsHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type FilterLogsHandler interface { - OnFilterLogs(log *Log) - OnError(failure string) -} - -// SubscribeFilterLogs subscribes to the results of a streaming filter query. -func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan types.Log, buffer) - rawSub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case log := <-ch: - handler.OnFilterLogs(&Log{&log}) - - case err := <-rawSub.Err(): - if err != nil { - handler.OnError(err.Error()) - } - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// Pending State - -// GetPendingBalanceAt returns the wei balance of the given account in the pending state. -func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (balance *BigInt, _ error) { - rawBalance, err := ec.client.PendingBalanceAt(ctx.context, account.address) - return &BigInt{rawBalance}, err -} - -// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state. -func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) (storage []byte, _ error) { - return ec.client.PendingStorageAt(ctx.context, account.address, key.hash) -} - -// GetPendingCodeAt returns the contract code of the given account in the pending state. -func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) (code []byte, _ error) { - return ec.client.PendingCodeAt(ctx.context, account.address) -} - -// GetPendingNonceAt returns the account nonce of the given account in the pending state. -// This is the nonce that should be used for the next transaction. -func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (nonce int64, _ error) { - rawNonce, err := ec.client.PendingNonceAt(ctx.context, account.address) - return int64(rawNonce), err -} - -// GetPendingTransactionCount returns the total number of transactions in the pending state. -func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (count int, _ error) { - rawCount, err := ec.client.PendingTransactionCount(ctx.context) - return int(rawCount), err -} - -// Contract Calling - -// CallContract executes a message call transaction, which is directly executed in the VM -// of the node, but never mined into the blockchain. -// -// blockNumber selects the block height at which the call runs. It can be <0, in which -// case the code is taken from the latest known block. Note that state from very old -// blocks might not be available. -func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) (output []byte, _ error) { - if number < 0 { - return ec.client.CallContract(ctx.context, msg.msg, nil) - } - return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number)) -} - -// PendingCallContract executes a message call transaction using the EVM. -// The state seen by the contract call is the pending state. -func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) (output []byte, _ error) { - return ec.client.PendingCallContract(ctx.context, msg.msg) -} - -// SuggestGasPrice retrieves the currently suggested gas price to allow a timely -// execution of a transaction. -func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (price *BigInt, _ error) { - rawPrice, err := ec.client.SuggestGasPrice(ctx.context) - return &BigInt{rawPrice}, err -} - -// EstimateGas tries to estimate the gas needed to execute a specific transaction based on -// the current pending state of the backend blockchain. There is no guarantee that this is -// the true gas limit requirement as other transactions may be added or removed by miners, -// but it should provide a basis for setting a reasonable default. -func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _ error) { - rawGas, err := ec.client.EstimateGas(ctx.context, msg.msg) - return int64(rawGas), err -} - -// SendTransaction injects a signed transaction into the pending pool for execution. -// -// If the transaction was a contract creation use the TransactionReceipt method to get the -// contract address after the transaction has been mined. -func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { - return ec.client.SendTransaction(ctx.context, tx.tx) -} - -func (ec *EthereumClient) SendTransactionConditional(ctx *Context, tx *Transaction, knownAccounts map[string]map[common.Address]interface{}) error { - return ec.client.SendTransactionConditional(ctx.context, tx.tx, knownAccounts) -} From 7d837dcd672a1633afe0f7c37ddc41367082e008 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Wed, 9 Aug 2023 13:16:46 +0530 Subject: [PATCH 09/18] few fixes --- accounts/abi/bind/backends/simulated.go | 1 + eth/protocols/eth/broadcast.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index d3254896c1..f252ffebd0 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -761,6 +761,7 @@ func (b *SimulatedBackend) SendTransactionConditional(ctx context.Context, tx *t if object, ok := v.(string); ok { trie, _ := currentState.StorageTrie(k) actualRootHash := trie.Hash() + if common.HexToHash(object) != actualRootHash { return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, common.HexToHash(object), actualRootHash) } diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go index 94bd94045a..9d18869d35 100644 --- a/eth/protocols/eth/broadcast.go +++ b/eth/protocols/eth/broadcast.go @@ -161,7 +161,10 @@ func (p *Peer) announceTransactions() { ) for count = 0; count < len(queue) && size < maxTxPacketSize; count++ { - if tx := p.txpool.Get(queue[count]); tx != nil { + tx := p.txpool.Get(queue[count]) + + // Skip EIP-4337 bundled transactions + if tx != nil && tx.GetOptions() == nil { pending = append(pending, queue[count]) pendingTypes = append(pendingTypes, tx.Type()) pendingSizes = append(pendingSizes, uint32(tx.Size())) From c4e0a5c7152f917711e822718ccf9d205052e8ff Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Fri, 11 Aug 2023 14:47:48 +0530 Subject: [PATCH 10/18] sentry reject conditional transaction --- eth/api_backend.go | 4 ++++ miner/miner.go | 6 +++++- miner/test_backend.go | 8 ++++---- miner/worker.go | 17 +++++++++-------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index 564aab59c8..8242fb6034 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -313,6 +313,10 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri } func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { + if signedTx.GetOptions() != nil && !b.eth.Miner().GetWorker().IsRunning() { + return errors.New("bundled transactions are not broadcasted therefore they will not submitted to the transaction pool") + } + err := b.eth.txPool.AddLocal(signedTx) if err != nil { if unwrapped := errors.Unwrap(err); unwrapped != nil { diff --git a/miner/miner.go b/miner/miner.go index 349b08d583..6904d7c759 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -103,6 +103,10 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even return miner } +func (miner *Miner) GetWorker() *worker { + return miner.worker +} + // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks @@ -189,7 +193,7 @@ func (miner *Miner) Close() { } func (miner *Miner) Mining() bool { - return miner.worker.isRunning() + return miner.worker.IsRunning() } func (miner *Miner) Hashrate() uint64 { diff --git a/miner/test_backend.go b/miner/test_backend.go index e8acf42edf..1d28ebfa8a 100644 --- a/miner/test_backend.go +++ b/miner/test_backend.go @@ -392,7 +392,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay // If our sealing block contains less than 2 uncle blocks, // add the new uncle block if valid and regenerate a new // sealing block for higher profit. - if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 { + if w.IsRunning() && w.current != nil && len(w.current.uncles) < 2 { start := time.Now() if err := w.commitUncle(w.current, ev.Block.Header()); err == nil { commitErr := w.commit(ctx, w.current.copy(), nil, true, start) @@ -423,7 +423,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay // already included in the current sealing block. These transactions will // be automatically eliminated. // nolint : nestif - if !w.isRunning() && w.current != nil { + if !w.IsRunning() && w.current != nil { // If block is already full, abort if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas { continue @@ -481,7 +481,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) { // Set the coinbase if the worker is running or it's required var coinbase common.Address - if w.isRunning() { + if w.IsRunning() { if w.coinbase == (common.Address{}) { log.Error("Refusing to mine without etherbase") return @@ -868,7 +868,7 @@ mainloop: } } - if !w.isRunning() && len(coalescedLogs) > 0 { + if !w.IsRunning() && len(coalescedLogs) > 0 { // We don't push the pendingLogsEvent while we are sealing. The reason is that // when we are sealing, the worker will regenerate a sealing block every 3 seconds. // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. diff --git a/miner/worker.go b/miner/worker.go index adec615e4f..81378520be 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -487,7 +487,7 @@ func (w *worker) stop() { } // isRunning returns an indicator whether worker is running or not. -func (w *worker) isRunning() bool { +func (w *worker) IsRunning() bool { return w.running.Load() } @@ -589,7 +589,7 @@ func (w *worker) newWorkLoop(ctx context.Context, recommit time.Duration) { case <-timer.C: // If sealing is running resubmit a new work cycle periodically to pull in // higher priced transactions. Disable this overhead for pending blocks. - if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { + if w.IsRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { // Short circuit if no new transaction arrives. if w.newTxs.Load() == 0 { timer.Reset(recommit) @@ -684,7 +684,7 @@ func (w *worker) mainLoop(ctx context.Context) { } // If our mining block contains less than 2 uncle blocks, // add the new uncle block if valid and regenerate a mining block. - if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 { + if w.IsRunning() && w.current != nil && len(w.current.uncles) < 2 { start := time.Now() if err := w.commitUncle(w.current, ev.Block.Header()); err == nil { commitErr := w.commit(ctx, w.current.copy(), nil, true, start) @@ -714,7 +714,8 @@ func (w *worker) mainLoop(ctx context.Context) { // Note all transactions received may not be continuous with transactions // already included in the current sealing block. These transactions will // be automatically eliminated. - if !w.isRunning() && w.current != nil { + // nolint : nestif + if !w.IsRunning() && w.current != nil { // If block is already full, abort if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas { continue @@ -1235,7 +1236,7 @@ mainloop: } // nolint:nestif - if EnableMVHashMap && w.isRunning() { + if EnableMVHashMap && w.IsRunning() { close(chDeps) depsWg.Wait() @@ -1296,7 +1297,7 @@ mainloop: } - if !w.isRunning() && len(coalescedLogs) > 0 { + if !w.IsRunning() && len(coalescedLogs) > 0 { // We don't push the pendingLogsEvent while we are sealing. The reason is that // when we are sealing, the worker will regenerate a sealing block every 3 seconds. // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. @@ -1710,7 +1711,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *atomic.Int32, noempt tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) { // Set the coinbase if the worker is running or it's required var coinbase common.Address - if w.isRunning() { + if w.IsRunning() { coinbase = w.etherbase() if coinbase == (common.Address{}) { log.Error("Refusing to mine without etherbase") @@ -1825,7 +1826,7 @@ func getInterruptTimer(ctx context.Context, work *environment, current *types.Bl // Note the assumption is held that the mutation is allowed to the passed env, do // the deep copy first. func (w *worker) commit(ctx context.Context, env *environment, interval func(), update bool, start time.Time) error { - if w.isRunning() { + if w.IsRunning() { ctx, span := tracing.StartSpan(ctx, "commit") defer tracing.EndSpan(span) From 7deba1f13565a6de7f844fa443e36bcc596610a6 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Wed, 6 Sep 2023 16:22:23 +0530 Subject: [PATCH 11/18] Changed the namespace of conditional transaction API from `eth` to `bor` (#985) * added conditional transaction to bor namespace * test comit * test comit * added conditional transaction * namespapce changed to bor * cleanup * cleanup --- internal/ethapi/api.go | 37 ---------------------------- internal/ethapi/backend.go | 3 +++ internal/ethapi/bor_api.go | 49 +++++++++++++++++++++++++++++++++++++ internal/web3ext/bor_ext.go | 6 +++++ 4 files changed, 58 insertions(+), 37 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 3f007a0772..a393326dba 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2234,43 +2234,6 @@ func (s *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.B return SubmitTransaction(ctx, s.b, tx) } -// SendRawTransactionConditional will add the signed transaction to the transaction pool. -// The sender/bundler is responsible for signing the transaction -func (s *TransactionAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options types.OptionsAA4337) (common.Hash, error) { - tx := new(types.Transaction) - if err := tx.UnmarshalBinary(input); err != nil { - return common.Hash{}, err - } - - currentHeader := s.b.CurrentHeader() - currentState, _, _ := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(currentHeader.Number.Int64())) - - // check block number range - if err := currentHeader.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil { - return common.Hash{}, &rpc.OptionsValidateError{Message: "out of block range. err: " + err.Error()} - } - - // check timestamp range - if err := currentHeader.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil { - return common.Hash{}, &rpc.OptionsValidateError{Message: "out of time range. err: " + err.Error()} - } - - // check knownAccounts length (number of slots/accounts) should be less than 1000 - if err := options.KnownAccounts.ValidateLength(); err != nil { - return common.Hash{}, &rpc.KnownAccountsLimitExceededError{Message: "limit exceeded. err: " + err.Error()} - } - - // check knownAccounts - if err := currentState.ValidateKnownAccounts(options.KnownAccounts); err != nil { - return common.Hash{}, &rpc.OptionsValidateError{Message: "storage error. err: " + err.Error()} - } - - // put options data in Tx, to use it later while block building - tx.PutOptions(&options) - - return SubmitTransaction(ctx, s.b, tx) -} - // Sign calculates an ECDSA signature for: // keccak256("\x19Ethereum Signed Message:\n" + len(message) + message). // diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 57399dde8b..4a17887138 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -136,6 +136,9 @@ func GetAPIs(apiBackend Backend) []rpc.API { }, { Namespace: "personal", Service: NewPersonalAccountAPI(apiBackend, nonceLock), + }, { + Namespace: "bor", + Service: NewBorAPI(apiBackend), }, } } diff --git a/internal/ethapi/bor_api.go b/internal/ethapi/bor_api.go index aaf5733f61..c170faa6e8 100644 --- a/internal/ethapi/bor_api.go +++ b/internal/ethapi/bor_api.go @@ -4,7 +4,9 @@ import ( "context" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" ) // GetRootHash returns root hash for given start and end block @@ -47,3 +49,50 @@ func (s *BlockChainAPI) appendRPCMarshalBorTransaction(ctx context.Context, bloc return fields } + +// EthereumAPI provides an API to access Ethereum related information. +type BorAPI struct { + b Backend +} + +// NewEthereumAPI creates a new Ethereum protocol API. +func NewBorAPI(b Backend) *BorAPI { + return &BorAPI{b} +} + +// SendRawTransactionConditional will add the signed transaction to the transaction pool. +// The sender/bundler is responsible for signing the transaction +func (api *BorAPI) SendRawTransactionConditional(ctx context.Context, input hexutil.Bytes, options types.OptionsAA4337) (common.Hash, error) { + tx := new(types.Transaction) + if err := tx.UnmarshalBinary(input); err != nil { + return common.Hash{}, err + } + + currentHeader := api.b.CurrentHeader() + currentState, _, _ := api.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(currentHeader.Number.Int64())) + + // check block number range + if err := currentHeader.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil { + return common.Hash{}, &rpc.OptionsValidateError{Message: "out of block range. err: " + err.Error()} + } + + // check timestamp range + if err := currentHeader.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil { + return common.Hash{}, &rpc.OptionsValidateError{Message: "out of time range. err: " + err.Error()} + } + + // check knownAccounts length (number of slots/accounts) should be less than 1000 + if err := options.KnownAccounts.ValidateLength(); err != nil { + return common.Hash{}, &rpc.KnownAccountsLimitExceededError{Message: "limit exceeded. err: " + err.Error()} + } + + // check knownAccounts + if err := currentState.ValidateKnownAccounts(options.KnownAccounts); err != nil { + return common.Hash{}, &rpc.OptionsValidateError{Message: "storage error. err: " + err.Error()} + } + + // put options data in Tx, to use it later while block building + tx.PutOptions(&options) + + return SubmitTransaction(ctx, api.b, tx) +} diff --git a/internal/web3ext/bor_ext.go b/internal/web3ext/bor_ext.go index fe8b01da19..1bb1fd9bd4 100644 --- a/internal/web3ext/bor_ext.go +++ b/internal/web3ext/bor_ext.go @@ -60,6 +60,12 @@ web3._extend({ call: 'bor_getRootHash', params: 2, }), + new web3._extend.Method({ + name: 'sendRawTransactionConditional', + call: 'bor_sendRawTransactionConditional', + params: 2, + inputFormatter: [null] + }), ] }); ` From 6b106210a1014267ea71341a28c32e51666df4ae Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 12 Sep 2023 12:04:23 +0530 Subject: [PATCH 12/18] addressed comments --- accounts/abi/bind/backends/simulated.go | 25 ------------------------- core/state/statedb.go | 9 ++++----- miner/worker.go | 6 +++--- 3 files changed, 7 insertions(+), 33 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index f252ffebd0..916052f736 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "math/big" - "reflect" "sync" "time" @@ -753,30 +752,6 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa // a replica of above `SendTransaction` function func (b *SimulatedBackend) SendTransactionConditional(ctx context.Context, tx *types.Transaction, knownAccounts map[string]map[common.Address]interface{}) error { - // check knownAccounts - currentState, _ := b.blockchain.State() - - for k, v := range knownAccounts["knownAccounts"] { - // check if the value is hex string or an object - if object, ok := v.(string); ok { - trie, _ := currentState.StorageTrie(k) - actualRootHash := trie.Hash() - - if common.HexToHash(object) != actualRootHash { - return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, common.HexToHash(object), actualRootHash) - } - } else if object, ok := v.(map[string]interface{}); ok { - for slot, value := range object { - actualValue := currentState.GetState(k, common.HexToHash(slot)) - if common.HexToHash(value.(string)) != actualValue { - return fmt.Errorf("invalid slot value at address: %v slot: %v value: %v actual value: %v", k, slot, value, actualValue) - } - } - } else { - return fmt.Errorf("invalid type in knownAccounts %v", reflect.TypeOf(v)) - } - } - return b.SendTransaction(ctx, tx) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 7967bfa390..04b952c992 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1649,11 +1649,10 @@ func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error // check if the value is hex string or an object switch { case v.IsSingle(): - trie, _ := s.StorageTrie(k) - if trie != nil { - actualRootHash := trie.Hash() - if *v.Single != actualRootHash { - return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, actualRootHash) + acc, _ := s.trie.GetAccount(k) + if acc != nil { + if *v.Single != acc.Root { + return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, acc.Root) } } else { return fmt.Errorf("Storage Trie is nil for: %v", k) diff --git a/miner/worker.go b/miner/worker.go index 615ef59105..da27492847 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1157,21 +1157,21 @@ mainloop: //nolint:nestif if options := tx.GetOptions(); options != nil { if err := env.header.ValidateBlockNumberOptions4337(options.BlockNumberMin, options.BlockNumberMax); err != nil { - log.Trace("Dropping conditional transaction from", from, "with hash", tx.Hash(), "reason", err) + log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err) txs.Pop() continue } if err := env.header.ValidateTimestampOptions4337(options.TimestampMin, options.TimestampMax); err != nil { - log.Trace("Dropping conditional transaction from", from, "with hash", tx.Hash(), "reason", err) + log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err) txs.Pop() continue } if err := env.state.ValidateKnownAccounts(options.KnownAccounts); err != nil { - log.Trace("Dropping conditional transaction from", from, "with hash", tx.Hash(), "reason", err) + log.Trace("Dropping conditional transaction", "from", from, "hash", tx.Hash(), "reason", err) txs.Pop() continue From 566386e923aafd4787202edd5937dfdc936c5f98 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 12 Sep 2023 13:06:50 +0530 Subject: [PATCH 13/18] reverted changes in ValidateKnownAccounts --- core/state/statedb.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 04b952c992..7967bfa390 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1649,10 +1649,11 @@ func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error // check if the value is hex string or an object switch { case v.IsSingle(): - acc, _ := s.trie.GetAccount(k) - if acc != nil { - if *v.Single != acc.Root { - return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, acc.Root) + trie, _ := s.StorageTrie(k) + if trie != nil { + actualRootHash := trie.Hash() + if *v.Single != actualRootHash { + return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, actualRootHash) } } else { return fmt.Errorf("Storage Trie is nil for: %v", k) From abe87af0bb99b7995b621af5eb41d50b4a964e0f Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 12 Sep 2023 15:28:43 +0530 Subject: [PATCH 14/18] addressed comments and removed unwanted code --- accounts/abi/bind/backend.go | 3 --- accounts/abi/bind/backends/simulated.go | 5 ---- accounts/abi/bind/base_test.go | 4 --- core/txpool/list.go | 2 +- core/txpool/txpool.go | 2 +- core/types/block.go | 2 ++ core/types/transaction.go | 2 ++ core/types/transaction_conditional.go | 16 ----------- eth/backend.go | 1 + ethclient/ethclient.go | 9 ------- ethclient/ethclient_test.go | 36 ------------------------- internal/ethapi/bor_api.go | 4 +-- miner/fake_miner.go | 2 +- miner/test_backend.go | 2 +- 14 files changed, 11 insertions(+), 79 deletions(-) diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index a8e1abcdc2..c16990f395 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -96,9 +96,6 @@ type ContractTransactor interface { // SendTransaction injects the transaction into the pending pool for execution. SendTransaction(ctx context.Context, tx *types.Transaction) error - - // SendTransactionConditional injects the conditional transaction into the pending pool for execution after verification. - SendTransactionConditional(ctx context.Context, tx *types.Transaction, knownAccounts map[string]map[common.Address]interface{}) error } // ContractFilterer defines the methods needed to access log events using one-off diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 916052f736..441849c20f 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -750,11 +750,6 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa return nil } -// a replica of above `SendTransaction` function -func (b *SimulatedBackend) SendTransactionConditional(ctx context.Context, tx *types.Transaction, knownAccounts map[string]map[common.Address]interface{}) error { - return b.SendTransaction(ctx, tx) -} - // FilterLogs executes a log filter operation, blocking during execution and // returning all the results in one batch. // diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index 17b75b2b4f..450f430fcb 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -75,10 +75,6 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac return nil } -func (mt *mockTransactor) SendTransactionConditional(ctx context.Context, tx *types.Transaction, ownAccounts map[string]map[common.Address]interface{}) error { - return nil -} - type mockCaller struct { codeAtBlockNumber *big.Int callContractBlockNumber *big.Int diff --git a/core/txpool/list.go b/core/txpool/list.go index 32205ec2b1..2435de5c38 100644 --- a/core/txpool/list.go +++ b/core/txpool/list.go @@ -564,7 +564,7 @@ func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactio return removed, invalids } -// Returns the conditional transactions with invalid KnownAccounts +// FilterTxConditional returns the conditional transactions with invalid KnownAccounts // TODO - We will also have to check block range and time stamp range! func (l *list) FilterTxConditional(state *state.StateDB) types.Transactions { removed := l.txs.filter(func(tx *types.Transaction) bool { diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index beea263404..9da20cd763 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -2308,8 +2308,8 @@ func (pool *TxPool) demoteUnexecutables() { for _, tx := range txConditionalsRemoved { hash := tx.Hash() - log.Trace("Removed invalid conditional transaction", "hash", hash) pool.all.Remove(hash) + log.Trace("Removed invalid conditional transaction", "hash", hash) } pendingGauge.Dec(int64(oldsLen + dropsLen + invalidsLen + len(txConditionalsRemoved))) diff --git a/core/types/block.go b/core/types/block.go index c6a94b1bb4..e732e0a25f 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -188,6 +188,7 @@ func (h *Header) EmptyReceipts() bool { return h.ReceiptHash == EmptyReceiptsHash } +// ValidateBlockNumberOptions4337 validates the block range passed as in the options parameter in the conditional transaction (EIP-4337) func (h *Header) ValidateBlockNumberOptions4337(minBlockNumber *big.Int, maxBlockNumber *big.Int) error { currentBlockNumber := h.Number @@ -206,6 +207,7 @@ func (h *Header) ValidateBlockNumberOptions4337(minBlockNumber *big.Int, maxBloc return nil } +// ValidateBlockNumberOptions4337 validates the timestamp range passed as in the options parameter in the conditional transaction (EIP-4337) func (h *Header) ValidateTimestampOptions4337(minTimestamp *uint64, maxTimestamp *uint64) error { currentBlockTime := h.Time diff --git a/core/types/transaction.go b/core/types/transaction.go index 49514faf81..b16f6fc3ba 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -104,10 +104,12 @@ type TxData interface { effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int } +// PutOptions stores the optionsAA4337 field of the conditional transaction (EIP-4337) func (tx *Transaction) PutOptions(options *OptionsAA4337) { tx.optionsAA4337 = options } +// GetOptions returns the optionsAA4337 field of the conditional transaction (EIP-4337) func (tx *Transaction) GetOptions() *OptionsAA4337 { return tx.optionsAA4337 } diff --git a/core/types/transaction_conditional.go b/core/types/transaction_conditional.go index 42bd926abb..358303a0b5 100644 --- a/core/types/transaction_conditional.go +++ b/core/types/transaction_conditional.go @@ -1,19 +1,3 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - package types import ( diff --git a/eth/backend.go b/eth/backend.go index 93004cc196..de333afb5b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -351,6 +351,7 @@ func makeExtraData(extra []byte) []byte { return extra } +// PeerCount returns the number of connected peers. func (s *Ethereum) PeerCount() int { return s.p2pServer.PeerCount() } diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 71f2f31914..f20cc1662d 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -633,15 +633,6 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)) } -func (ec *Client) SendTransactionConditional(ctx context.Context, tx *types.Transaction, knownAccounts map[string]map[common.Address]interface{}) error { - data, err := tx.MarshalBinary() - if err != nil { - return err - } - - return ec.c.CallContext(ctx, nil, "eth_sendRawTransactionConditional", hexutil.Encode(data), knownAccounts) -} - func toBlockNumArg(number *big.Int) string { if number == nil { return "latest" diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 655f34e4b6..7e9f9f08df 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -612,10 +612,6 @@ func testCallContract(t *testing.T, client *rpc.Client) { func testAtFunctions(t *testing.T, client *rpc.Client) { ec := NewClient(client) - if err := sendTransactionConditional(ec); err != nil { - t.Fatalf("error: %v", err) - } - // send a transaction for some interesting pending status sendTransaction(ec) time.Sleep(100 * time.Millisecond) @@ -758,35 +754,3 @@ func sendTransaction(ec *Client) error { return ec.SendTransaction(context.Background(), tx) } - -func sendTransactionConditional(ec *Client) error { - chainID, err := ec.ChainID(context.Background()) - if err != nil { - return err - } - - nonce, err := ec.PendingNonceAt(context.Background(), testAddr) - if err != nil { - return err - } - - signer := types.LatestSignerForChainID(chainID) - - tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{ - Nonce: nonce, - To: &common.Address{2}, - Value: big.NewInt(1), - Gas: 22000, - GasPrice: big.NewInt(params.InitialBaseFee), - }) - if err != nil { - return err - } - - return ec.SendTransactionConditional(context.Background(), tx, map[string]map[common.Address]interface{}{ - "knownAccounts": { - common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add1"): "0x313AaDcA1750CaadC7BCB26FF08175c95DCf8E38", - common.HexToAddress("0xadd2add2add2add2add2add2add2add2add2add2"): "0x413AaDcA1750CaadC7BCB26FF08175c95DCf8E38", - }, - }) -} diff --git a/internal/ethapi/bor_api.go b/internal/ethapi/bor_api.go index 5361a2a8ba..722b524c7c 100644 --- a/internal/ethapi/bor_api.go +++ b/internal/ethapi/bor_api.go @@ -54,12 +54,12 @@ func (s *BlockChainAPI) appendRPCMarshalBorTransaction(ctx context.Context, bloc return fields } -// EthereumAPI provides an API to access Ethereum related information. +// BorAPI provides an API to access Bor related information. type BorAPI struct { b Backend } -// NewEthereumAPI creates a new Ethereum protocol API. +// NewBorAPI creates a new Bor protocol API. func NewBorAPI(b Backend) *BorAPI { return &BorAPI{b} } diff --git a/miner/fake_miner.go b/miner/fake_miner.go index b3b58c0dc2..ed7b3d9ac8 100644 --- a/miner/fake_miner.go +++ b/miner/fake_miner.go @@ -172,7 +172,7 @@ type mockBackend struct { txPool *txpool.TxPool } -// PeerCount implements Backend. +// PeerCount implements mockBackend. func (*mockBackend) PeerCount() int { panic("unimplemented") } diff --git a/miner/test_backend.go b/miner/test_backend.go index 3d3d72d0d9..bb63f1b562 100644 --- a/miner/test_backend.go +++ b/miner/test_backend.go @@ -95,7 +95,7 @@ type testWorkerBackend struct { uncleBlock *types.Block } -// PeerCount implements Backend. +// PeerCount implements testWorkerBackend. func (*testWorkerBackend) PeerCount() int { panic("unimplemented") } From bce561598fb19c756b861d73a2168e79e1409a6f Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 12 Sep 2023 18:14:59 +0530 Subject: [PATCH 15/18] addressed comments --- core/state/state_test.go | 20 +++++++------------- core/txpool/list_test.go | 20 ++++++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/core/state/state_test.go b/core/state/state_test.go index 268de63609..4703fc203e 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -18,7 +18,6 @@ package state import ( "bytes" - "fmt" "math/big" "testing" @@ -28,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" + "github.com/stretchr/testify/require" ) type stateTest struct { @@ -305,12 +305,7 @@ func TestValidateKnownAccounts(t *testing.T) { s.state.SetState(stateobjaddr2, storageaddr21, data21) s.state.SetState(stateobjaddr2, storageaddr22, data22) - trieTemp, _ := s.state.StorageTrie(stateobjaddr1) - fmt.Println("\nStorageTrie.Hash()", trieTemp.Hash()) - - if err := s.state.ValidateKnownAccounts(knownAccounts); err != nil { - t.Fatalf(err.Error()) - } + require.NoError(t, s.state.ValidateKnownAccounts(knownAccounts)) types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf")) @@ -321,9 +316,8 @@ func TestValidateKnownAccounts(t *testing.T) { s.state.SetState(stateobjaddr3, storageaddr3, data3) // expected error - if err := s.state.ValidateKnownAccounts(knownAccounts); err == nil { - t.Fatalf("should have been an error") - } + err := s.state.ValidateKnownAccounts(knownAccounts) + require.Error(t, err, "should have been an error") // correct the previous mistake "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1cf" -> "0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce" types.InsertKnownAccounts(knownAccounts, common.HexToAddress("0xadd1add1add1add1add1add1add1add1add1add2"), common.HexToHash("0x2d6f8a898e7dec0bb7a50e8c142be32d7c98c096ff68ed57b9b08280d9aca1ce")) @@ -341,7 +335,7 @@ func TestValidateKnownAccounts(t *testing.T) { s.state.SetState(stateobjaddr4, storageaddr42, data4) // expected error - if err := s.state.ValidateKnownAccounts(knownAccounts); err == nil { - t.Fatalf("should have been an error") - } + err = s.state.ValidateKnownAccounts(knownAccounts) + require.Error(t, err, "should have been an error") + } diff --git a/core/txpool/list_test.go b/core/txpool/list_test.go index 0553534f44..5f12b2fdb6 100644 --- a/core/txpool/list_test.go +++ b/core/txpool/list_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/holiman/uint256" + "github.com/stretchr/testify/require" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -105,9 +106,8 @@ func TestFilterTxConditional(t *testing.T) { // No state has been modified. drops := list.FilterTxConditional(state) - if count := len(drops); count != 0 { - t.Fatalf("got %d filtered by TxOptions when there should not be any", count) - } + count := len(drops) + require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count) // Create another transaction with a known account storage root tx option // and add to the list. @@ -128,9 +128,8 @@ func TestFilterTxConditional(t *testing.T) { // There should still be no drops as no state has been modified. drops = list.FilterTxConditional(state) - if count := len(drops); count != 0 { - t.Fatalf("got %d filtered by TxOptions when there should not be any", count) - } + count = len(drops) + require.Equal(t, 0, count, "got %d filtered by TxOptions when there should not be any", count) // Set state that conflicts with tx2's policy state.SetState(common.Address{19: 1}, common.Hash{}, common.Hash{31: 1}) @@ -138,11 +137,8 @@ func TestFilterTxConditional(t *testing.T) { // tx2 should be the single transaction filtered out drops = list.FilterTxConditional(state) - if count := len(drops); count != 1 { - t.Fatalf("got %d filtered by TxOptions when there should be a single one", count) - } + count = len(drops) + require.Equal(t, 1, count, "got %d filtered by TxOptions when there should be a single one", count) - if drops[0] != tx2 { - t.Fatalf("Got %x, expected %x", drops[0].Hash(), tx2.Hash()) - } + require.Equal(t, tx2, drops[0], "Got %x, expected %x", drops[0].Hash(), tx2.Hash()) } From 768308ac019062dfc85e2462eb1f47f4c72a212a Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 12 Sep 2023 18:36:08 +0530 Subject: [PATCH 16/18] bug fix --- core/state/statedb.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 7967bfa390..9e680fae61 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1666,8 +1666,7 @@ func (s *StateDB) ValidateKnownAccounts(knownAccounts types.KnownAccounts) error } } default: - trie, _ := s.StorageTrie(k) - return fmt.Errorf("invalid root hash for: %v root hash: %v actual root hash: %v", k, v.Single, trie.Hash()) + return fmt.Errorf("impossible to validate known accounts: %v", k) } } From 674997e381c781a0354eb7ef52aa99babcf34b58 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 12 Sep 2023 19:30:10 +0530 Subject: [PATCH 17/18] lint --- core/state/state_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/state/state_test.go b/core/state/state_test.go index 4703fc203e..3ccefdd1f9 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -21,13 +21,14 @@ import ( "math/big" "testing" + "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" - "github.com/stretchr/testify/require" ) type stateTest struct { @@ -337,5 +338,4 @@ func TestValidateKnownAccounts(t *testing.T) { // expected error err = s.state.ValidateKnownAccounts(knownAccounts) require.Error(t, err, "should have been an error") - } From b00bc5320a8f4d8fc01759556ee9c35b0fcc463b Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Tue, 12 Sep 2023 21:36:10 +0530 Subject: [PATCH 18/18] removed licence from core/types/transaction_conditional_test.go --- core/types/transaction_conditional_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/core/types/transaction_conditional_test.go b/core/types/transaction_conditional_test.go index 1789095ac9..03ce473d16 100644 --- a/core/types/transaction_conditional_test.go +++ b/core/types/transaction_conditional_test.go @@ -1,19 +1,3 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - package types import (