From 4ee0ae161067a6cc68d85e4032d80fcf91f77fdb Mon Sep 17 00:00:00 2001 From: leonardchinonso <36096513+leonardchinonso@users.noreply.github.com> Date: Mon, 14 Mar 2022 14:59:51 +0100 Subject: [PATCH] adding eth_getLogs functionality for contract events for the devnet tool (#3680) * adding eth_getLogs functionality for contract events for the devnet tool * Made changes to fix double hashing * Fixed lint errors * Changed strings.Replace to strings.ReplaceAll for replacing strings Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local> --- cmd/devnettest/commands/block.go | 111 +++++++++++++------ cmd/devnettest/requests/request_generator.go | 5 + cmd/devnettest/requests/requests.go | 15 ++- cmd/devnettest/requests/utils.go | 9 ++ cmd/rpctest/rpctest/type.go | 5 - 5 files changed, 104 insertions(+), 41 deletions(-) diff --git a/cmd/devnettest/commands/block.go b/cmd/devnettest/commands/block.go index 0ed04b51ed..9007ab8792 100644 --- a/cmd/devnettest/commands/block.go +++ b/cmd/devnettest/commands/block.go @@ -23,7 +23,10 @@ import ( "github.com/spf13/cobra" ) -var devnetSignPrivateKey, _ = crypto.HexToECDSA("26e86e45f6fc45ec6e2ecd128cec80fa1d1505e5507dcd2ae58c3130a7a97b48") +var ( + devnetSignPrivateKey, _ = crypto.HexToECDSA("26e86e45f6fc45ec6e2ecd128cec80fa1d1505e5507dcd2ae58c3130a7a97b48") + signer = types.LatestSigner(params.AllCliqueProtocolChanges) +) var ( sendAddr string @@ -74,7 +77,7 @@ var sendTxCmd = &cobra.Command{ } // subscriptionContract is the handler to the contract for further operations - signedTx, subscriptionContract, transactOpts, err := createTransaction(txType) + signedTx, address, subscriptionContract, transactOpts, err := createTransaction(txType) if err != nil { panic(err) } @@ -85,46 +88,44 @@ var sendTxCmd = &cobra.Command{ } if searchBlock { - searchBlockForTx(*hash) + _ = searchBlockForTx(*hash) } fmt.Printf("subscription contract, transactOps: %+v, %+v\n", subscriptionContract, transactOpts) - //_, err = subscriptionContract.Fallback(transactOpts, []byte{}) - //if err != nil { - // fmt.Printf("error 3: %+v\n", err) - // panic(err) - //} - // TODO: call eth_getLogs on address and this block number + if err := emitEventAndGetLogs(subscriptionContract, transactOpts, address); err != nil { + panic(err) + } }, } -func createTransaction(transactionType string) (*types.Transaction, *contracts.Subscription, *bind.TransactOpts, error) { - signer := types.LatestSigner(params.AllCliqueProtocolChanges) +func createTransaction(transactionType string) (*types.Transaction, common.Address, *contracts.Subscription, *bind.TransactOpts, error) { if transactionType == "regular" { - return createNonContractTx(signer) + tx, address, err := createNonContractTx(signer) + return tx, address, nil, nil, err } return createContractTx(signer) } -func createNonContractTx(signer *types.Signer) (*types.Transaction, *contracts.Subscription, *bind.TransactOpts, error) { +// createNonContractTx takes in a signer and returns the signed transaction and the address receiving the sent value +func createNonContractTx(signer *types.Signer) (*types.Transaction, common.Address, error) { toAddress := common.HexToAddress(sendAddr) signedTx, err := types.SignTx(types.NewTransaction(nonce, toAddress, uint256.NewInt(sendValue), params.TxGas, uint256.NewInt(gasPrice), nil), *signer, devnetSignPrivateKey) if err != nil { - return nil, nil, nil, err + return nil, toAddress, err } - return &signedTx, nil, nil, nil + return &signedTx, toAddress, nil } -func createContractTx(signer *types.Signer) (*types.Transaction, *contracts.Subscription, *bind.TransactOpts, error) { +func createContractTx(signer *types.Signer) (*types.Transaction, common.Address, *contracts.Subscription, *bind.TransactOpts, error) { const txGas uint64 = 200_000 gspec := core.DeveloperGenesisBlock(uint64(0), common.HexToAddress("67b1d87101671b127f5f8714789C7192f7ad340e")) contractBackend := backends.NewSimulatedBackendWithConfig(gspec.Alloc, gspec.Config, 1_000_000) transactOpts, err := bind.NewKeyedTransactorWithChainID(devnetSignPrivateKey, big.NewInt(1337)) if err != nil { - return nil, nil, nil, err + return nil, common.Address{}, nil, nil, err } transactOpts.GasLimit = txGas @@ -133,21 +134,21 @@ func createContractTx(signer *types.Signer) (*types.Transaction, *contracts.Subs transactOpts.Nonce = big.NewInt(int64(nonce)) // get transaction to sign and contract handler - _, txToSign, subscriptionContract, err := contracts.DeploySubscription(transactOpts, contractBackend) + address, txToSign, subscriptionContract, err := contracts.DeploySubscription(transactOpts, contractBackend) if err != nil { - return nil, nil, nil, err + return nil, common.Address{}, nil, nil, err } // sign the transaction with the private key signedTx, err := types.SignTx(txToSign, *signer, devnetSignPrivateKey) if err != nil { - return nil, nil, nil, err + return nil, common.Address{}, nil, nil, err } - return &signedTx, subscriptionContract, transactOpts, nil + return &signedTx, address, subscriptionContract, transactOpts, nil } -func searchBlockForTx(txnHash common.Hash) { +func searchBlockForTx(txnHash common.Hash) uint64 { url := "ws://127.0.0.1:8545" client, clientErr := rpc.DialWebsocket(context.Background(), url, "") if clientErr != nil { @@ -157,44 +158,51 @@ func searchBlockForTx(txnHash common.Hash) { fmt.Println() fmt.Println("Connected to web socket successfully") - if err := subscribe(client, "eth_newHeads", txnHash); err != nil { + blockN, err := subscribe(client, "eth_newHeads", txnHash) + if err != nil { fmt.Println("error occurred while subscribing", err) panic(err) } + + return blockN } -func subscribe(client *rpc.Client, method string, hash common.Hash) error { +func subscribe(client *rpc.Client, method string, hash common.Hash) (uint64, error) { parts := strings.SplitN(method, "_", 2) namespace := parts[0] method = parts[1] ch := make(chan interface{}) sub, err := client.Subscribe(context.Background(), namespace, ch, []interface{}{method}...) if err != nil { - return err + return uint64(0), err } defer sub.Unsubscribe() - blockCount := 0 + var ( + blockCount int + blockN uint64 + ) ForLoop: for { select { case v := <-ch: blockCount++ blockNumber := v.(map[string]interface{})["number"] - fmt.Printf("Searching for the transaction in block with number: %+v\n", blockNumber) - foundTx, err := blockHasHash(client, hash, blockNumber.(string)) + fmt.Printf("Searching for the transaction in block with number: %+v, type: %[1]T\n", blockNumber.(string)) + num, foundTx, err := blockHasHash(client, hash, blockNumber.(string)) if err != nil { - return err + return uint64(0), err } if foundTx || blockCount == 128 { + blockN = num break ForLoop } case err := <-sub.Err(): - return err + return uint64(0), err } } - return nil + return blockN, nil } @@ -203,7 +211,7 @@ type Block struct { Transactions []common.Hash } -func blockHasHash(client *rpc.Client, hash common.Hash, blockNumber string) (bool, error) { +func blockHasHash(client *rpc.Client, hash common.Hash, blockNumber string) (uint64, bool, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() @@ -211,7 +219,7 @@ func blockHasHash(client *rpc.Client, hash common.Hash, blockNumber string) (boo err := client.CallContext(ctx, ¤tBlock, "eth_getBlockByNumber", blockNumber, false) if err != nil { fmt.Println("can't get latest block:", err) - return false, err + return uint64(0), false, err } for _, txnHash := range currentBlock.Transactions { @@ -219,9 +227,42 @@ func blockHasHash(client *rpc.Client, hash common.Hash, blockNumber string) (boo fmt.Println() fmt.Printf("Block with number: %v was mined and included transaction with hash: %v ==> %+v\n", blockNumber, hash, currentBlock) fmt.Println() - return true, nil + return requests.HexToInt(blockNumber), true, nil } } - return false, nil + return uint64(0), false, nil +} + +func emitEventAndGetLogs(subContract *contracts.Subscription, opts *bind.TransactOpts, address common.Address) error { + if subContract == nil { + return nil + } + + opts.Nonce.Add(opts.Nonce, big.NewInt(1)) + + tx, err := subContract.Fallback(opts, []byte{}) + if err != nil { + fmt.Printf("error 3: %+v\n", err) + panic(err) + } + + signedTx, err := types.SignTx(tx, *signer, devnetSignPrivateKey) + if err != nil { + return err + } + + hash, err := requests.SendTx(reqId, &signedTx) + if err != nil { + panic(err) + } + + blockN := searchBlockForTx(*hash) + + if err = requests.GetLogs(reqId, blockN, blockN, address); err != nil { + fmt.Printf("error 4: %+v\n", err) + panic(err) + } + + return nil } diff --git a/cmd/devnettest/requests/request_generator.go b/cmd/devnettest/requests/request_generator.go index 9075eff2b0..1343316f9b 100644 --- a/cmd/devnettest/requests/request_generator.go +++ b/cmd/devnettest/requests/request_generator.go @@ -112,3 +112,8 @@ func (req *RequestGenerator) parityStorageKeyListContent(address common.Address, return fmt.Sprintf(template, address, quantity, offsetString, blockNum, req.reqID) } + +func (req *RequestGenerator) getLogs(fromBlock, toBlock uint64, address common.Address) string { + const template = `{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock": "0x%x", "toBlock": "0x%x", "address": "0x%x"}],"id":%d}` + return fmt.Sprintf(template, fromBlock, toBlock, address, req.reqID) +} diff --git a/cmd/devnettest/requests/requests.go b/cmd/devnettest/requests/requests.go index ba46234841..11444fc031 100644 --- a/cmd/devnettest/requests/requests.go +++ b/cmd/devnettest/requests/requests.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/ledgerwatch/erigon/cmd/rpctest/rpctest" "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/core/types" @@ -77,3 +76,17 @@ func ParityList(reqId int, account common.Address, quantity int, offset []byte, fmt.Printf("Storage keys: %v\n", parseResponse(b)) } + +func GetLogs(reqId int, fromBlock, toBlock uint64, address common.Address) error { + reqGen := initialiseRequestGenerator(reqId) + var b rpctest.Log + + reqq := reqGen.getLogs(fromBlock, toBlock, address) + res := reqGen.Erigon("eth_getLogs", reqq, &b) + if res.Err != nil { + return fmt.Errorf("Error fetching logs: %v\n", res.Err) + } + + fmt.Printf("Logs and events: %v\n", parseResponse(b)) + return nil +} diff --git a/cmd/devnettest/requests/utils.go b/cmd/devnettest/requests/utils.go index 46c5054578..f751ca602d 100644 --- a/cmd/devnettest/requests/utils.go +++ b/cmd/devnettest/requests/utils.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "strings" "time" @@ -27,3 +28,11 @@ func post(client *http.Client, url, request string, response interface{}) error log.Info("Got in", "time", time.Since(start).Seconds()) return err } + +func HexToInt(hexStr string) uint64 { + // Remove the 0x prefix + cleaned := strings.ReplaceAll(hexStr, "0x", "") + + result, _ := strconv.ParseUint(cleaned, 16, 64) + return result +} diff --git a/cmd/rpctest/rpctest/type.go b/cmd/rpctest/rpctest/type.go index a36469e65c..ad3df795f9 100644 --- a/cmd/rpctest/rpctest/type.go +++ b/cmd/rpctest/rpctest/type.go @@ -229,11 +229,6 @@ type EthReceipt struct { Result Receipt `json:"result"` } -type EthLogs struct { - CommonResponse - Result []*Log `json:"result"` -} - type EthGetProof struct { CommonResponse Result AccountResult `json:"result"` -- GitLab