diff --git a/core/plugeth_injection_test.go b/core/plugeth_injection_test.go new file mode 100644 index 000000000..a2dc60d54 --- /dev/null +++ b/core/plugeth_injection_test.go @@ -0,0 +1,150 @@ +package core + +import ( + "hash" + "fmt" + "testing" + "math/big" + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "golang.org/x/crypto/sha3" +) + +var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + } + signer = types.LatestSigner(config) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("0202020202020202020202020202020202020202020202020202002020202020") +) + +var makeTx = func(key *ecdsa.PrivateKey, nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { + tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, key) + return tx +} + +var ( + db = rawdb.NewMemoryDatabase() + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + common.HexToAddress("0xfd0810DD14796680f72adf1a371963d0745BCc64"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: math.MaxUint64, + }, + }, + } +) + +type testHasher struct { + hasher hash.Hash +} + +func newHasher() *testHasher { + return &testHasher{hasher: sha3.NewLegacyKeccak256()} +} + +func (h *testHasher) Reset() { + h.hasher.Reset() +} + +func (h *testHasher) Update(key, val []byte) { + h.hasher.Write(key) + h.hasher.Write(val) +} + +func (h *testHasher) Hash() common.Hash { + return common.BytesToHash(h.hasher.Sum(nil)) +} + + +func TestPlugethInjections(t *testing.T) { + + + blockchain, _ := NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) + + engine := ethash.NewFaker() + + sp := NewStateProcessor(config, blockchain, engine) + + txns := []*types.Transaction{ + makeTx(key1, 0, common.Address{}, big.NewInt(1000), params.TxGas-1000, big.NewInt(875000000), nil), + } + + block := GenerateBadBlock(gspec.ToBlock(), engine, txns, gspec.Config) + + statedb, _ := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache, nil) + + t.Run(fmt.Sprintf("test BlockProcessingError"), func(t *testing.T) { + called := false + injectionCalled = &called + metaInjectionCalled = &called + + _, _, _, _ = sp.Process(block, statedb, vm.Config{}) + + if *injectionCalled != true { + t.Fatalf("pluginBlockProcessingError injection in stateProcessor.Process() not called") + } + if *metaInjectionCalled != true { + t.Fatalf("metaTracer.BlockProcessingError injection in stateProcessor.Process() not called") + } + }) + + t.Run(fmt.Sprintf("test Reorg"), func(t *testing.T) { + called := false + injectionCalled = &called + + // the transaction has to be initialized with a different gas price than the previous tx in order to trigger a reorg + txns2 := []*types.Transaction{ + makeTx(key1, 0, common.Address{}, big.NewInt(1000), params.TxGas-1000, big.NewInt(875000001), nil), + } + block2 := GenerateBadBlock(gspec.ToBlock(), engine, txns2, gspec.Config) + + + _, _ = blockchain.writeBlockAndSetHead(block, []*types.Receipt{}, []*types.Log{}, statedb, false) + + _ = blockchain.reorg(block.Header(), block2) + + if *injectionCalled != true { + t.Fatalf("pluginReorg injection in blockChain.Reorg() not called") + } + }) + + t.Run(fmt.Sprintf("test NewSideBlock"), func(t *testing.T) { + called := false + injectionCalled = &called + + TestReorgToShorterRemovesCanonMapping(t) + + if *injectionCalled != true { + t.Fatalf("pluginNewSideBlock injection in blockChain.writeBlockAndSetHead() not called") + } + }) + +} \ No newline at end of file diff --git a/core/plugin_hooks.go b/core/plugin_hooks.go index f00730bdd..3052b1b44 100644 --- a/core/plugin_hooks.go +++ b/core/plugin_hooks.go @@ -18,6 +18,9 @@ import ( "github.com/openrelayxyz/plugeth-utils/core" ) +var injectionCalled *bool +var metaInjectionCalled *bool + func PluginPreProcessBlock(pl *plugins.PluginLoader, block *types.Block) { fnList := pl.Lookup("PreProcessBlock", func(item interface{}) bool { _, ok := item.(func(core.Hash, uint64, []byte)) @@ -68,6 +71,12 @@ func PluginBlockProcessingError(pl *plugins.PluginLoader, tx *types.Transaction, } } func pluginBlockProcessingError(tx *types.Transaction, block *types.Block, err error) { + + if injectionCalled != nil { + called := true + injectionCalled = &called + } + if plugins.DefaultPluginLoader == nil { log.Warn("Attempting BlockProcessingError, but default PluginLoader has not been initialized") return @@ -160,6 +169,7 @@ func pluginNewSideBlock(block *types.Block, hash common.Hash, logs []*types.Log) } func PluginReorg(pl *plugins.PluginLoader, commonBlock *types.Block, oldChain, newChain types.Blocks) { + fnList := pl.Lookup("Reorg", func(item interface{}) bool { _, ok := item.(func(core.Hash, []core.Hash, []core.Hash)) return ok @@ -179,6 +189,12 @@ func PluginReorg(pl *plugins.PluginLoader, commonBlock *types.Block, oldChain, n } } func pluginReorg(commonBlock *types.Block, oldChain, newChain types.Blocks) { + + if injectionCalled != nil { + called := true + injectionCalled = &called + } + if plugins.DefaultPluginLoader == nil { log.Warn("Attempting Reorg, but default PluginLoader has not been initialized") return @@ -213,6 +229,12 @@ func (mt *metaTracer) PreProcessTransaction(tx *types.Transaction, block *types. } } func (mt *metaTracer) BlockProcessingError(tx *types.Transaction, block *types.Block, err error) { + + if metaInjectionCalled != nil { + called := true + metaInjectionCalled = &called + } + if len(mt.tracers) == 0 { return } blockHash := core.Hash(block.Hash()) transactionHash := core.Hash(tx.Hash()) diff --git a/core/rawdb/plugeth_injection_test.go b/core/rawdb/plugeth_injection_test.go new file mode 100644 index 000000000..7ab47f9eb --- /dev/null +++ b/core/rawdb/plugeth_injection_test.go @@ -0,0 +1,50 @@ +package rawdb + + +import ( + "fmt" + "testing" + "math/big" + + "github.com/ethereum/go-ethereum/ethdb" +) + + +func TestPlugethInjections(t *testing.T) { + var valuesRaw [][]byte + var valuesRLP []*big.Int + for x := 0; x < 100; x++ { + v := getChunk(256, x) + valuesRaw = append(valuesRaw, v) + iv := big.NewInt(int64(x)) + iv = iv.Exp(iv, iv, nil) + valuesRLP = append(valuesRLP, iv) + } + tables := map[string]bool{"raw": true, "rlp": false} + f, _ := newFreezerForTesting(t, tables) + + t.Run(fmt.Sprintf("test plugeth injections"), func(t *testing.T) { + called := false + modifyAncientsInjection = &called + + _, _ = f.ModifyAncients(func(op ethdb.AncientWriteOp) error { + + appendRawInjection = &called + _ = op.AppendRaw("raw", uint64(0), valuesRaw[0]) + if *appendRawInjection != true { + t.Fatalf("pluginTrackUpdate injection in AppendRaw not called") + } + + appendInjection = &called + _ = op.Append("rlp", uint64(0), valuesRaw[0]) + if *appendInjection != true { + t.Fatalf("pluginTrackUpdate injection in Append not called") + } + + return nil + }) + if *modifyAncientsInjection != true { + t.Fatalf("pluginCommitUpdate injection in ModifyAncients not called") + } + }) +} \ No newline at end of file diff --git a/core/rawdb/plugin_hooks.go b/core/rawdb/plugin_hooks.go index 98fd27a2e..e4635e4a6 100644 --- a/core/rawdb/plugin_hooks.go +++ b/core/rawdb/plugin_hooks.go @@ -2,18 +2,33 @@ package rawdb import ( + "sync" + "github.com/ethereum/go-ethereum/plugins" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" - "sync" ) var ( freezerUpdates map[uint64]map[string]interface{} lock sync.Mutex + modifyAncientsInjection *bool + appendRawInjection *bool + appendInjection *bool ) func PluginTrackUpdate(num uint64, kind string, value interface{}) { + + if appendRawInjection != nil { + called := true + appendRawInjection = &called + } + + if appendInjection != nil { + called := true + appendInjection = &called + } + lock.Lock() defer lock.Unlock() if freezerUpdates == nil { freezerUpdates = make(map[uint64]map[string]interface{}) } @@ -26,6 +41,12 @@ func PluginTrackUpdate(num uint64, kind string, value interface{}) { } func pluginCommitUpdate(num uint64) { + + if modifyAncientsInjection != nil { + called := true + modifyAncientsInjection = &called + } + if plugins.DefaultPluginLoader == nil { log.Warn("Attempting CommitUpdate, but default PluginLoader has not been initialized") return @@ -34,6 +55,7 @@ func pluginCommitUpdate(num uint64) { } func PluginCommitUpdate(pl *plugins.PluginLoader, num uint64) { + lock.Lock() defer lock.Unlock() if freezerUpdates == nil { freezerUpdates = make(map[uint64]map[string]interface{}) } diff --git a/plugins/wrappers/engine/enginewrapper.go b/plugins/wrappers/engine/enginewrapper.go index 7d004dda9..92304e730 100644 --- a/plugins/wrappers/engine/enginewrapper.go +++ b/plugins/wrappers/engine/enginewrapper.go @@ -151,7 +151,6 @@ func utilsToGethTransactions(transactions []*ptypes.Transaction) []*types.Transa } return txs } - func utilsToGethWithdrawals(withdrawals []*ptypes.Withdrawal) []*types.Withdrawal { if withdrawals == nil { return nil } pwithdrawals := make([]*types.Withdrawal, len(withdrawals)) diff --git a/rpc/plugin_subscriptions.go b/rpc/plugin_subscriptions.go index 66f2f5027..d6cc540e3 100644 --- a/rpc/plugin_subscriptions.go +++ b/rpc/plugin_subscriptions.go @@ -4,6 +4,7 @@ import ( "context" "reflect" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/plugins" ) // Is t context.Context or *context.Context? @@ -128,7 +129,24 @@ func callbackifyChanPubSub(receiver, fn reflect.Value) *callback { return c } +func RPCSubscription(pl *plugins.PluginLoader) { + fnList := pl.Lookup("RPCSubscriptionTest", func(item interface{}) bool { + _, ok := item.(func()) + return ok + }) + for _, fni := range fnList { + if fn, ok := fni.(func()); ok { + fn() + } + } +} + func pluginExtendedCallbacks(callbacks map[string]*callback, receiver reflect.Value) { + if plugins.DefaultPluginLoader == nil { + log.Warn("Attempting RPCSubscriptionTest, but default PluginLoader has not been initialized") + return + } + RPCSubscription(plugins.DefaultPluginLoader) typ := receiver.Type() for m := 0; m < typ.NumMethod(); m++ { method := typ.Method(m)