package main import ( "bufio" "bytes" "encoding/base64" "encoding/json" "flag" "fmt" "io" "net/http" "os" "path" "strings" "time" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/common/hexutil" "github.com/ledgerwatch/turbo-geth/core/state" "github.com/ledgerwatch/turbo-geth/crypto" "github.com/ledgerwatch/turbo-geth/log" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" ) var action = flag.String("action", "", "action to execute") var url = flag.String("url", "", "URL to use for RPC requests") var block = flag.Int("block", 1, "specifies a block number for operation") var chaindata = flag.String("chaindata", "chaindata", "path to the chaindata database file") type EthError struct { Code int `json:"code"` Message string `json:"message"` } type CommonResponse struct { Version string `json:"jsonrpc"` RequestId int `json:"id"` Error *EthError `json:"error"` } type EthBlockNumber struct { CommonResponse Number hexutil.Uint64 `json:"result"` } type EthBalance struct { CommonResponse Balance hexutil.Big `json:"result"` } type EthTransaction struct { From common.Address `json:"from"` To *common.Address `json:"to"` // Pointer because it might be missing Hash string `json:"hash"` Gas hexutil.Big `json:"gas"` } type EthBlockByNumberResult struct { Difficulty hexutil.Big `json:"difficulty"` Miner common.Address `json:"miner"` Transactions []EthTransaction `json:"transactions"` TxRoot common.Hash `json:"transactionsRoot"` Hash common.Hash `json:"hash"` } type EthBlockByNumber struct { CommonResponse Result EthBlockByNumberResult `json:"result"` } type StructLog struct { Op string `json:"op"` Pc uint64 `json:"pc"` Depth uint64 `json:"depth"` Error *EthError `json:"error"` Gas uint64 `json:"gas"` GasCost uint64 `json:"gasCost"` Memory []string `json:"memory"` Stack []string `json:"stack"` Storage map[string]string `json:"storage"` } type EthTxTraceResult struct { Gas uint64 `json:"gas"` Failed bool `json:"failed"` ReturnValue string `json:"returnValue"` StructLogs []StructLog `json:"structLogs"` } type EthTxTrace struct { CommonResponse Result EthTxTraceResult `json:"result"` } type DebugModifiedAccounts struct { CommonResponse Result []common.Address `json:"result"` } // StorageRangeResult is the result of a debug_storageRangeAt API call. type StorageRangeResult struct { Storage storageMap `json:"storage"` NextKey *common.Hash `json:"nextKey"` // nil if Storage includes the last key in the trie. } type storageMap map[common.Hash]storageEntry type storageEntry struct { Key *common.Hash `json:"key"` Value common.Hash `json:"value"` } type DebugStorageRange struct { CommonResponse Result StorageRangeResult `json:"result"` } type DebugAccountRange struct { CommonResponse Result state.IteratorDump `json:"result"` } // Log represents a contract log event. These events are generated by the LOG opcode and // stored/indexed by the node. type Log struct { // Consensus fields: // address of the contract that generated the event Address common.Address `json:"address" gencodec:"required"` // list of topics provided by the contract. Topics []common.Hash `json:"topics" gencodec:"required"` // supplied by the contract, usually ABI-encoded Data hexutil.Bytes `json:"data" gencodec:"required"` // Derived fields. These fields are filled in by the node // but not secured by consensus. // block in which the transaction was included BlockNumber hexutil.Uint64 `json:"blockNumber"` // hash of the transaction TxHash common.Hash `json:"transactionHash" gencodec:"required"` // index of the transaction in the block TxIndex hexutil.Uint `json:"transactionIndex" gencodec:"required"` // hash of the block in which the transaction was included BlockHash common.Hash `json:"blockHash"` // index of the log in the receipt Index hexutil.Uint `json:"logIndex" gencodec:"required"` // The Removed field is true if this log was reverted due to a chain reorganisation. // You must pay attention to this field if you receive logs through a filter query. Removed bool `json:"removed"` } type Receipt struct { // Consensus fields PostState common.Hash `json:"root"` Status hexutil.Uint64 `json:"status"` CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` Bloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` Logs []*Log `json:"logs" gencodec:"required"` // Implementation fields (don't reorder!) TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress *common.Address `json:"contractAddress"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` } type EthReceipt struct { CommonResponse Result Receipt `json:"result"` } type EthLogs struct { CommonResponse Result []*Log `json:"result"` } func post(client *http.Client, url, request string, response interface{}) error { log.Info("Getting", "url", url, "request", request) start := time.Now() r, err := client.Post(url, "application/json", strings.NewReader(request)) if err != nil { return err } defer r.Body.Close() if r.StatusCode != 200 { return fmt.Errorf("status %s", r.Status) } decoder := json.NewDecoder(r.Body) err = decoder.Decode(response) log.Info("Got in", "time", time.Since(start).Seconds()) return err } func print(client *http.Client, url, request string) { r, err := client.Post(url, "application/json", strings.NewReader(request)) if err != nil { fmt.Printf("Could not print: %v\n", err) return } if r.StatusCode != 200 { fmt.Printf("Status %s", r.Status) return } fmt.Printf("ContentLength: %d\n", r.ContentLength) buf := make([]byte, 2000000) l, err := r.Body.Read(buf) if err != nil && err != io.EOF { fmt.Printf("Could not read response: %v\n", err) return } if l < len(buf) { fmt.Printf("Could not read response: %d out of %d\n", l, len(buf)) //return } fmt.Printf("%s\n", buf[:l]) } func compareBlocks(b, bg *EthBlockByNumber) bool { r := b.Result rg := bg.Result if r.Difficulty.ToInt().Cmp(rg.Difficulty.ToInt()) != 0 { fmt.Printf("Difficulty difference %d %d\n", r.Difficulty.ToInt(), rg.Difficulty.ToInt()) return false } if r.Miner != rg.Miner { fmt.Printf("Miner different %x %x\n", r.Miner, rg.Miner) return false } if len(r.Transactions) != len(rg.Transactions) { fmt.Printf("Num of txs different: %d %d\n", len(r.Transactions), len(rg.Transactions)) return false } for i, tx := range r.Transactions { txg := rg.Transactions[i] if tx.From != txg.From { fmt.Printf("Tx %d different From: %x %x\n", i, tx.From, txg.From) return false } if (tx.To == nil && txg.To != nil) || (tx.To != nil && txg.To == nil) { fmt.Printf("Tx %d different To nilness: %t %t\n", i, (tx.To == nil), (txg.To == nil)) return false } if tx.To != nil && txg.To != nil && *tx.To != *txg.To { fmt.Printf("Tx %d different To: %x %x\n", i, *tx.To, *txg.To) return false } if tx.Hash != txg.Hash { fmt.Printf("Tx %x different Hash: %s %s\n", i, tx.Hash, txg.Hash) return false } } return true } func compareTraces(trace, traceg *EthTxTrace) bool { r := trace.Result rg := traceg.Result if r.Gas != rg.Gas { fmt.Printf("Trace different Gas: %d %d\n", r.Gas, rg.Gas) return false } if r.Failed != rg.Failed { fmt.Printf("Trace different Failed: %t %t\n", r.Failed, rg.Failed) return false } if r.ReturnValue != rg.ReturnValue { fmt.Printf("Trace different ReturnValue: %s %s\n", r.ReturnValue, rg.ReturnValue) return false } if len(r.StructLogs) != len(rg.StructLogs) { fmt.Printf("Trace different length: %d %d\n", len(r.StructLogs), len(rg.StructLogs)) return false } for i, l := range r.StructLogs { lg := rg.StructLogs[i] if l.Op != lg.Op { fmt.Printf("Trace different Op: %d %s %s\n", i, l.Op, lg.Op) return false } if l.Pc != lg.Pc { fmt.Printf("Trace different Pc: %d %d %d\n", i, l.Pc, lg.Pc) return false } } return true } func compareBalances(balance, balanceg *EthBalance) bool { if balance.Balance.ToInt().Cmp(balanceg.Balance.ToInt()) != 0 { fmt.Printf("Different balance: %d %d\n", balance.Balance.ToInt(), balanceg.Balance.ToInt()) return false } return true } func (ma *DebugModifiedAccounts) Print() { r := ma.Result rset := make(map[common.Address]struct{}) for _, a := range r { rset[a] = struct{}{} } for a := range rset { fmt.Printf("%x\n", a) } } func extractAccountMap(ma *DebugModifiedAccounts) map[common.Address]struct{} { r := ma.Result rset := make(map[common.Address]struct{}) for _, a := range r { rset[a] = struct{}{} } return rset } func printStorageRange(sm map[common.Hash]storageEntry) { for k := range sm { fmt.Printf("%x\n", k) } } func compareStorageRanges(sm, smg map[common.Hash]storageEntry) bool { for k, v := range sm { if vg, ok := smg[k]; !ok { fmt.Printf("%x not present in smg\n", k) return false } else { if v.Key == nil { fmt.Printf("v.Key == nil for %x\n", k) return false } if k != crypto.Keccak256Hash(v.Key[:]) { fmt.Printf("Sec key %x does not match key %x\n", k, *v.Key) return false } if v.Value != vg.Value { fmt.Printf("Different values for %x: %x %x [%x]\n", k, v.Value, vg.Value, *v.Key) return false } } } for k, v := range smg { if _, ok := sm[k]; !ok { fmt.Printf("%x not present in sm\n", k) return false } if k != crypto.Keccak256Hash(v.Key[:]) { fmt.Printf("Sec key (g) %x does not match key %x\n", k, *v.Key) return false } } return true } /* // Derived fields. These fields are filled in by the node // but not secured by consensus. // block in which the transaction was included BlockNumber hexutil.Uint64 `json:"blockNumber"` // hash of the transaction TxHash common.Hash `json:"transactionHash" gencodec:"required"` // index of the transaction in the block TxIndex hexutil.Uint `json:"transactionIndex" gencodec:"required"` // hash of the block in which the transaction was included BlockHash common.Hash `json:"blockHash"` // index of the log in the receipt Index hexutil.Uint `json:"logIndex" gencodec:"required"` // The Removed field is true if this log was reverted due to a chain reorganisation. // You must pay attention to this field if you receive logs through a filter query. Removed bool `json:"removed"` */ func compareReceipts(receipt, receiptg *EthReceipt) bool { r := receipt.Result rg := receiptg.Result if r.TxHash != rg.TxHash { fmt.Printf("Different tx hashes: %x %x\n", r.TxHash, rg.TxHash) return false } if r.Status != rg.Status { //fmt.Printf("Different status: %d %d\n", r.Status, rg.Status) //return false } if r.CumulativeGasUsed != rg.CumulativeGasUsed { fmt.Printf("Different cumulativeGasUsed: %d %d\n", r.CumulativeGasUsed, rg.CumulativeGasUsed) return false } if !bytes.Equal(r.Bloom, rg.Bloom) { fmt.Printf("Different blooms: %x %x\n", r.Bloom, rg.Bloom) return false } if r.ContractAddress == nil && rg.ContractAddress != nil { fmt.Printf("Different contract addresses: nil %x\n", rg.ContractAddress) return false } if r.ContractAddress != nil && rg.ContractAddress == nil { fmt.Printf("Different contract addresses: %x nil\n", r.ContractAddress) return false } if r.ContractAddress != nil && rg.ContractAddress != nil && *r.ContractAddress != *rg.ContractAddress { fmt.Printf("Different contract addresses: %x %x\n", r.ContractAddress, rg.ContractAddress) return false } if r.GasUsed != rg.GasUsed { fmt.Printf("Different gasUsed: %d %d\n", r.GasUsed, rg.GasUsed) return false } if len(r.Logs) != len(rg.Logs) { fmt.Printf("Different log lenths: %d %d\n", len(r.Logs), len(rg.Logs)) return false } for i, l := range r.Logs { lg := rg.Logs[i] if l.Address != lg.Address { fmt.Printf("Different log %d addresses: %x %x\n", i, l.Address, lg.Address) return false } if len(l.Topics) != len(lg.Topics) { fmt.Printf("Different log %d topic lengths: %d %d\n", i, len(l.Topics), len(lg.Topics)) return false } for j, t := range l.Topics { tg := lg.Topics[j] if t != tg { fmt.Printf("Different log %d topics %d: %x %x\n", i, j, t, tg) return false } } if !bytes.Equal(l.Data, lg.Data) { fmt.Printf("Different log %d data: %x %x\n", i, l.Data, lg.Data) return false } } return true } func compareLogs(logs, logsg *EthLogs) bool { r := logs.Result rg := logsg.Result if len(r) != len(rg) { fmt.Printf("Different log lenths: %d %d\n", len(r), len(rg)) return false } for i, l := range r { lg := rg[i] if l.Address != lg.Address { fmt.Printf("Different log %d addresses: %x %x\n", i, l.Address, lg.Address) return false } if len(l.Topics) != len(lg.Topics) { fmt.Printf("Different log %d topic lengths: %d %d\n", i, len(l.Topics), len(lg.Topics)) return false } for j, t := range l.Topics { tg := lg.Topics[j] if t != tg { fmt.Printf("Different log %d topics %d: %x %x\n", i, j, t, tg) return false } } if !bytes.Equal(l.Data, lg.Data) { fmt.Printf("Different log %d data: %x %x\n", i, l.Data, lg.Data) return false } } return true } const Geth = "geth" const TurboGeth = "turbo_geth" var routes = map[string]string{ Geth: "http://192.168.1.238:8545", TurboGeth: "http://192.168.1.126:8545", } type CallResult struct { Target string Took time.Duration RequestID int Method string RequestBody string Err error } type RequestGenerator struct { reqID int client *http.Client } func (g *RequestGenerator) blockNumber() string { const template = `{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":%d}` return fmt.Sprintf(template, g.reqID) } func (g *RequestGenerator) getBlockByNumber(blockNum int) string { const template = `{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x%x",true],"id":%d}` return fmt.Sprintf(template, blockNum, g.reqID) } func (g *RequestGenerator) storageRangeAt(hash common.Hash, i int, to *common.Address, nextKey common.Hash) string { const template = `{"jsonrpc":"2.0","method":"debug_storageRangeAt","params":["0x%x", %d,"0x%x","0x%x",%d],"id":%d}` return fmt.Sprintf(template, hash, i, to, nextKey, 1024, g.reqID) } func (g *RequestGenerator) traceTransaction(hash string) string { const template = `{"jsonrpc":"2.0","method":"debug_traceTransaction","params":["%s"],"id":%d}` return fmt.Sprintf(template, hash, g.reqID) } func (g *RequestGenerator) getTransactionReceipt(hash string) string { const template = `{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["%s"],"id":%d}` return fmt.Sprintf(template, hash, g.reqID) } func (g *RequestGenerator) getBalance(miner common.Address, bn int) string { const template = `{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x%x", "0x%x"],"id":%d}` return fmt.Sprintf(template, miner, bn, g.reqID) } func (g *RequestGenerator) getModifiedAccountsByNumber(prevBn int, bn int) string { const template = `{"jsonrpc":"2.0","method":"debug_getModifiedAccountsByNumber","params":[%d, %d],"id":%d}` return fmt.Sprintf(template, prevBn, bn, g.reqID) } func (g *RequestGenerator) getLogs(prevBn int, bn int, account 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, prevBn, bn, account, g.reqID) } func (g *RequestGenerator) accountRange(bn int, page []byte) string { const template = `{ "jsonrpc": "2.0", "method": "debug_accountRange", "params": ["0x%x", "%s", %d, true, true, true], "id":%d}` encodedKey := base64.StdEncoding.EncodeToString(page) return fmt.Sprintf(template, bn, encodedKey, 256, g.reqID) } func (g *RequestGenerator) call(target string, method, body string, response interface{}) CallResult { start := time.Now() err := post(g.client, routes[target], body, response) return CallResult{ RequestBody: body, Target: target, Took: time.Since(start), RequestID: g.reqID, Method: method, Err: err, } } func (g *RequestGenerator) Geth(method, body string, response interface{}) CallResult { return g.call(Geth, method, body, response) } func (g *RequestGenerator) TurboGeth(method, body string, response interface{}) CallResult { return g.call(TurboGeth, method, body, response) } // vegetaWrite (to be run as a goroutine) writing results of server calls into several files: // results to /$tmp$/turbo_geth_stress_test/results_*.csv // vegeta format going to files /$tmp$/turbo_geth_stress_test/vegeta_*.txt func vegetaWrite(enabled bool, resultsCh chan CallResult) { var err error var files map[string]map[string]*os.File var vegetaFiles map[string]map[string]*os.File if enabled { files = map[string]map[string]*os.File{ Geth: make(map[string]*os.File), TurboGeth: make(map[string]*os.File), } vegetaFiles = map[string]map[string]*os.File{ Geth: make(map[string]*os.File), TurboGeth: make(map[string]*os.File), } tmpDir := os.TempDir() fmt.Printf("tmp dir is: %s\n", tmpDir) dir := path.Join(tmpDir, "turbo_geth_stress_test") if err = os.MkdirAll(dir, 0770); err != nil { panic(err) } for _, route := range []string{Geth, TurboGeth} { for _, method := range []string{"eth_getBlockByNumber", "debug_storageRangeAt"} { file := path.Join(dir, "results_"+route+"_"+method+".csv") files[route][method], err = os.OpenFile(file, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) if err != nil { panic(err) } } } for _, route := range []string{Geth, TurboGeth} { for _, method := range []string{"eth_getBlockByNumber", "debug_storageRangeAt"} { file := path.Join(dir, "vegeta_"+route+"_"+method+".txt") vegetaFiles[route][method], err = os.OpenFile(file, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) if err != nil { panic(err) } } } } for res := range resultsCh { // If not enabled, simply keep draining the results channel if enabled { if res.Err != nil { fmt.Printf("error response. target: %s, err: %s\n", res.Target, res.Err) } // files with call stats if f, ok := files[res.Target][res.Method]; ok { row := fmt.Sprintf("%d, %s, %d\n", res.RequestID, res.Method, res.Took.Microseconds()) if _, err := fmt.Fprint(f, row); err != nil { panic(err) } } // vegeta files, write into all target files // because if "needCompare" is false - then we don't have responses from TurboGeth // but we still have enough information to build vegeta file for TurboGeth for _, target := range []string{Geth, TurboGeth} { if f, ok := vegetaFiles[target][res.Method]; ok { template := `{"method": "POST", "url": "%s", "body": "%s", "header": {"Content-Type": ["application/json"]}}` row := fmt.Sprintf(template, routes[target], base64.StdEncoding.EncodeToString([]byte(res.RequestBody))) if _, err := fmt.Fprint(f, row+"\n"); err != nil { panic(err) } } } } } } // bench1 compares response of TurboGeth with Geth // but also can be used for comparing RPCDaemon with Geth // parameters: // needCompare - if false - doesn't call TurboGeth and doesn't compare responses // use false value - to generate vegeta files, it's faster but we can generate vegeta files for Geth and Turbogeth // fullTest - if false - then call only methods which RPCDaemon currently supports func bench1(needCompare bool, fullTest bool) { var client = &http.Client{ Timeout: time.Second * 600, } resultsCh := make(chan CallResult, 1000) defer close(resultsCh) go vegetaWrite(false, resultsCh) var res CallResult reqGen := &RequestGenerator{ client: client, } reqGen.reqID++ var blockNumber EthBlockNumber res = reqGen.TurboGeth("eth_blockNumber", reqGen.blockNumber(), &blockNumber) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get block number: %v\n", res.Err) return } if blockNumber.Error != nil { fmt.Printf("Error getting block number: %d %s\n", blockNumber.Error.Code, blockNumber.Error.Message) return } lastBlock := blockNumber.Number if lastBlock > 5000000 { lastBlock = 5000000 } fmt.Printf("Last block: %d\n", lastBlock) accounts := make(map[common.Address]struct{}) firstBn := 49000 prevBn := firstBn storageCounter := 0 for bn := firstBn; bn <= int(lastBlock); bn++ { reqGen.reqID++ var b EthBlockByNumber res = reqGen.Geth("eth_getBlockByNumber", reqGen.getBlockByNumber(bn), &b) resultsCh <- res if res.Err != nil { fmt.Printf("Could not retrieve block %d: %v\n", bn, res.Err) return } if b.Error != nil { fmt.Printf("Error retrieving block: %d %s\n", b.Error.Code, b.Error.Message) } if needCompare { var bg EthBlockByNumber res = reqGen.TurboGeth("eth_getBlockByNumber", reqGen.getBlockByNumber(bn), &bg) if res.Err != nil { fmt.Printf("Could not retrieve block g %d: %v\n", bn, res.Err) return } if bg.Error != nil { fmt.Printf("Error retrieving block g: %d %s\n", bg.Error.Code, bg.Error.Message) return } if !compareBlocks(&b, &bg) { fmt.Printf("Block difference for %d\n", bn) return } } accounts[b.Result.Miner] = struct{}{} for i, tx := range b.Result.Transactions { accounts[tx.From] = struct{}{} if tx.To != nil { accounts[*tx.To] = struct{}{} } if tx.To != nil && tx.Gas.ToInt().Uint64() > 21000 { storageCounter++ if storageCounter == 100 { storageCounter = 0 var sm map[common.Hash]storageEntry var smGeth map[common.Hash]storageEntry nextKey := &common.Hash{} nextKeyGeth := &common.Hash{} var sr DebugStorageRange var srGeth DebugStorageRange counter := 16 for nextKey != nil && counter > 0 { sm = make(map[common.Hash]storageEntry) smGeth = make(map[common.Hash]storageEntry) reqGen.reqID++ res = reqGen.TurboGeth("debug_storageRangeAt", reqGen.storageRangeAt(b.Result.Hash, i, tx.To, *nextKey), &sr) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get storageRange: %s: %v\n", tx.Hash, res.Err) break } if sr.Error != nil { fmt.Printf("Error getting storageRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { nextKey = sr.Result.NextKey for k, v := range sr.Result.Storage { sm[k] = v } } res = reqGen.Geth("debug_storageRangeAt", reqGen.storageRangeAt(b.Result.Hash, i, tx.To, *nextKeyGeth), &srGeth) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get storageRange geth: %s: %v\n", tx.Hash, res.Err) break } if srGeth.Error != nil { fmt.Printf("Error getting storageRange geth: %d %s\n", srGeth.Error.Code, srGeth.Error.Message) break } else { nextKeyGeth = srGeth.Result.NextKey for k, v := range srGeth.Result.Storage { smGeth[k] = v } } if nextKey != nil && nextKeyGeth != nil && *nextKey != *nextKeyGeth { fmt.Printf("Non matching nextKey %x %x\n", *nextKey, *nextKeyGeth) fmt.Printf("len(sm) %d, len(smg) %d\n", len(sm), len(smGeth)) fmt.Printf("================sm\n") printStorageRange(sm) fmt.Printf("================smg\n") printStorageRange(smGeth) return } if !compareStorageRanges(sm, smGeth) { fmt.Printf("len(sm) %d, len(smGeth) %d\n", len(sm), len(smGeth)) fmt.Printf("================sm\n") printStorageRange(sm) fmt.Printf("================smg\n") printStorageRange(smGeth) return } counter-- } } } if !fullTest { continue // TODO: remove me } reqGen.reqID++ var trace EthTxTrace res = reqGen.Geth("debug_traceTransaction", reqGen.traceTransaction(tx.Hash), &trace) resultsCh <- res if res.Err != nil { fmt.Printf("Could not trace transaction %s: %v\n", tx.Hash, res.Err) print(client, routes[Geth], reqGen.traceTransaction(tx.Hash)) } if trace.Error != nil { fmt.Printf("Error tracing transaction: %d %s\n", trace.Error.Code, trace.Error.Message) } if needCompare { var traceg EthTxTrace res = reqGen.TurboGeth("debug_traceTransaction", reqGen.traceTransaction(tx.Hash), &traceg) resultsCh <- res if res.Err != nil { fmt.Printf("Could not trace transaction g %s: %v\n", tx.Hash, res.Err) print(client, routes[TurboGeth], reqGen.traceTransaction(tx.Hash)) return } if traceg.Error != nil { fmt.Printf("Error tracing transaction g: %d %s\n", traceg.Error.Code, traceg.Error.Message) return } if res.Err == nil && trace.Error == nil { if !compareTraces(&trace, &traceg) { fmt.Printf("Different traces block %d, tx %s\n", bn, tx.Hash) return } } } reqGen.reqID++ var receipt EthReceipt res = reqGen.Geth("eth_getTransactionReceipt", reqGen.getTransactionReceipt(tx.Hash), &receipt) resultsCh <- res if res.Err != nil { fmt.Printf("Count not get receipt: %s: %v\n", tx.Hash, res.Err) print(client, routes[Geth], reqGen.getTransactionReceipt(tx.Hash)) return } if receipt.Error != nil { fmt.Printf("Error getting receipt: %d %s\n", receipt.Error.Code, receipt.Error.Message) return } if needCompare { var receiptg EthReceipt res = reqGen.TurboGeth("eth_getTransactionReceipt", reqGen.getTransactionReceipt(tx.Hash), &receiptg) resultsCh <- res if res.Err != nil { fmt.Printf("Count not get receipt g: %s: %v\n", tx.Hash, res.Err) print(client, routes[TurboGeth], reqGen.getTransactionReceipt(tx.Hash)) return } if receiptg.Error != nil { fmt.Printf("Error getting receipt g: %d %s\n", receiptg.Error.Code, receiptg.Error.Message) return } if !compareReceipts(&receipt, &receiptg) { fmt.Printf("Different receipts block %d, tx %s\n", bn, tx.Hash) print(client, routes[Geth], reqGen.getTransactionReceipt(tx.Hash)) print(client, routes[TurboGeth], reqGen.getTransactionReceipt(tx.Hash)) return } } } reqGen.reqID++ var balance EthBalance res = reqGen.Geth("eth_getBalance", reqGen.getBalance(b.Result.Miner, bn), &balance) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get account balance: %v\n", res.Err) return } if balance.Error != nil { fmt.Printf("Error getting account balance: %d %s", balance.Error.Code, balance.Error.Message) return } if needCompare { var balanceg EthBalance res = reqGen.TurboGeth("eth_getBalance", reqGen.getBalance(b.Result.Miner, bn), &balanceg) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get account balance g: %v\n", res.Err) return } if balanceg.Error != nil { fmt.Printf("Error getting account balance g: %d %s\n", balanceg.Error.Code, balanceg.Error.Message) return } if !compareBalances(&balance, &balanceg) { fmt.Printf("Miner %x balance difference for block %d\n", b.Result.Miner, bn) return } } if prevBn < bn && bn%100 == 0 { // Checking modified accounts reqGen.reqID++ var mag DebugModifiedAccounts res = reqGen.TurboGeth("debug_getModifiedAccountsByNumber", reqGen.getModifiedAccountsByNumber(prevBn, bn), &mag) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get modified accounts g: %v\n", res.Err) return } if mag.Error != nil { fmt.Printf("Error getting modified accounts g: %d %s\n", mag.Error.Code, mag.Error.Message) return } if res.Err == nil && mag.Error == nil { accountSet := extractAccountMap(&mag) for account := range accountSet { reqGen.reqID++ var logs EthLogs res = reqGen.Geth("eth_getLogs", reqGen.getLogs(prevBn, bn, account), &logs) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get logs for account %x: %v\n", account, res.Err) return } if logs.Error != nil { fmt.Printf("Error getting logs for account %x: %d %s\n", account, logs.Error.Code, logs.Error.Message) return } var logsg EthLogs res = reqGen.TurboGeth("eth_getLogs", reqGen.getLogs(prevBn, bn, account), &logsg) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get logs for account g %x: %v\n", account, res.Err) return } if logsg.Error != nil { fmt.Printf("Error getting logs for account g %x: %d %s\n", account, logsg.Error.Code, logsg.Error.Message) return } if !compareLogs(&logs, &logsg) { fmt.Printf("Different logs for account %x and block %d-%d\n", account, prevBn, bn) return } } } fmt.Printf("Done blocks %d-%d, modified accounts: %d\n", prevBn, bn, len(mag.Result)) page := common.Hash{}.Bytes() pageGeth := common.Hash{}.Bytes() var accRangeTG map[common.Address]state.DumpAccount var accRangeGeth map[common.Address]state.DumpAccount for len(page) > 0 { accRangeTG = make(map[common.Address]state.DumpAccount) accRangeGeth = make(map[common.Address]state.DumpAccount) var sr DebugAccountRange reqGen.reqID++ res = reqGen.TurboGeth("debug_accountRange", reqGen.accountRange(bn, page), &sr) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get accountRange: %v\n", res.Err) return } if sr.Error != nil { fmt.Printf("Error getting accountRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { page = sr.Result.Next for k, v := range sr.Result.Accounts { accRangeTG[k] = v } } var srGeth DebugAccountRange res = reqGen.Geth("debug_accountRange", reqGen.accountRange(bn, pageGeth), &srGeth) resultsCh <- res if res.Err != nil { fmt.Printf("Could not get accountRange geth: %v\n", res.Err) return } if srGeth.Error != nil { fmt.Printf("Error getting accountRange geth: %d %s\n", srGeth.Error.Code, srGeth.Error.Message) break } else { pageGeth = srGeth.Result.Next for k, v := range srGeth.Result.Accounts { accRangeGeth[k] = v } } if !bytes.Equal(page, pageGeth) { fmt.Printf("Different next page keys: %x geth %x", page, pageGeth) } if !compareAccountRanges(accRangeTG, accRangeGeth) { fmt.Printf("Different in account ranges tx\n") return } } prevBn = bn } } } func bench2() { var client = &http.Client{ Timeout: time.Second * 600, } req_id := 0 turbogeth_url := "http://localhost:8545" req_id++ template := ` {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":%d} ` var blockNumber EthBlockNumber if err := post(client, turbogeth_url, fmt.Sprintf(template, req_id), &blockNumber); err != nil { fmt.Printf("Could not get block number: %v\n", err) return } if blockNumber.Error != nil { fmt.Printf("Error getting block number: %d %s\n", blockNumber.Error.Code, blockNumber.Error.Message) return } lastBlock := blockNumber.Number fmt.Printf("Last block: %d\n", lastBlock) firstBn := 1720000 - 2 prevBn := firstBn for bn := firstBn; bn <= int(lastBlock); bn++ { req_id++ template := ` {"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x%x",true],"id":%d} ` var b EthBlockByNumber if err := post(client, turbogeth_url, fmt.Sprintf(template, bn, req_id), &b); err != nil { fmt.Printf("Could not retrieve block %d: %v\n", bn, err) return } if b.Error != nil { fmt.Printf("Error retrieving block: %d %s\n", b.Error.Code, b.Error.Message) } for i, tx := range b.Result.Transactions { if tx.To != nil && tx.Gas.ToInt().Uint64() > 21000 { // Request storage range // blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int req_id++ template = ` {"jsonrpc":"2.0","method":"debug_storageRangeAt","params":["0x%x", %d,"0x%x","0x%x",%d],"id":%d} ` sm := make(map[common.Hash]storageEntry) nextKey := &common.Hash{} for nextKey != nil { var sr DebugStorageRange if err := post(client, turbogeth_url, fmt.Sprintf(template, b.Result.Hash, i, tx.To, *nextKey, 1024, req_id), &sr); err != nil { fmt.Printf("Could not get storageRange: %x: %v\n", tx.Hash, err) return } if sr.Error != nil { fmt.Printf("Error getting storageRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { nextKey = sr.Result.NextKey for k, v := range sr.Result.Storage { sm[k] = v if v.Key == nil { fmt.Printf("No key for sec key: %x\n", k) } else if k != crypto.Keccak256Hash(v.Key[:]) { fmt.Printf("Different sec key: %x %x (%x), value %x\n", k, crypto.Keccak256Hash(v.Key[:]), *(v.Key), v.Value) } else { fmt.Printf("Keys: %x %x, value %x\n", *(v.Key), k, v.Value) } } } } fmt.Printf("storageRange: %d\n", len(sm)) } } if prevBn < bn && bn%1000 == 0 { // Checking modified accounts req_id++ template = ` {"jsonrpc":"2.0","method":"debug_getModifiedAccountsByNumber","params":[%d, %d],"id":%d} ` var ma DebugModifiedAccounts if err := post(client, turbogeth_url, fmt.Sprintf(template, prevBn, bn, req_id), &ma); err != nil { fmt.Printf("Could not get modified accounts: %v\n", err) return } if ma.Error != nil { fmt.Printf("Error getting modified accounts: %d %s\n", ma.Error.Code, ma.Error.Message) return } fmt.Printf("Done blocks %d-%d, modified accounts: %d\n", prevBn, bn, len(ma.Result)) prevBn = bn } } } func bench3() { var client = &http.Client{ Timeout: time.Second * 600, } geth_url := "http://localhost:8545" turbogeth_url := "http://localhost:9545" blockhash := common.HexToHash("0xdf15213766f00680c6a20ba76ba2cc9534435e19bc490039f3a7ef42095c8d13") req_id := 1 pageSize := 256 req_id++ template := `{ "jsonrpc": "2.0", "method": "debug_accountRange", "params": ["0x1", "%s", %d, true, true, true], "id":%d}` page := common.Hash{}.Bytes() accRangeTG := make(map[common.Address]state.DumpAccount) for len(page) > 0 { encodedKey := base64.StdEncoding.EncodeToString(page) var sr DebugAccountRange if err := post(client, turbogeth_url, fmt.Sprintf(template, encodedKey, pageSize, req_id), &sr); err != nil { fmt.Printf("Could not get accountRange: %v\n", err) return } if sr.Error != nil { fmt.Printf("Error getting accountRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { page = sr.Result.Next for k, v := range sr.Result.Accounts { accRangeTG[k] = v } } } accRangeGeth := make(map[common.Address]state.DumpAccount) page = common.Hash{}.Bytes() for len(page) > 0 { encodedKey := base64.StdEncoding.EncodeToString(page) var sr DebugAccountRange if err := post(client, geth_url, fmt.Sprintf(template, encodedKey, pageSize, req_id), &sr); err != nil { fmt.Printf("Could not get accountRange: %v\n", err) return } if sr.Error != nil { fmt.Printf("Error getting accountRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { page = sr.Result.Next for k, v := range sr.Result.Accounts { accRangeTG[k] = v } } } if !compareAccountRanges(accRangeTG, accRangeGeth) { fmt.Printf("Different in account ranges tx\n") return } fmt.Println("debug_accountRanges... OK!") template = `{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x%x",true],"id":%d}` var b EthBlockByNumber if err := post(client, turbogeth_url, fmt.Sprintf(template, 1720000, req_id), &b); err != nil { fmt.Printf("Could not retrieve block %d: %v\n", 1720000, err) return } if b.Error != nil { fmt.Printf("Error retrieving block: %d %s\n", b.Error.Code, b.Error.Message) } for txindex := 0; txindex < 18; txindex++ { txhash := b.Result.Transactions[txindex].Hash req_id++ template = ` {"jsonrpc":"2.0","method":"debug_traceTransaction","params":["%s"],"id":%d} ` var trace EthTxTrace if err := post(client, turbogeth_url, fmt.Sprintf(template, txhash, req_id), &trace); err != nil { fmt.Printf("Could not trace transaction %s: %v\n", txhash, err) print(client, turbogeth_url, fmt.Sprintf(template, txhash, req_id)) return } if trace.Error != nil { fmt.Printf("Error tracing transaction: %d %s\n", trace.Error.Code, trace.Error.Message) } var traceg EthTxTrace if err := post(client, geth_url, fmt.Sprintf(template, txhash, req_id), &traceg); err != nil { fmt.Printf("Could not trace transaction g %s: %v\n", txhash, err) print(client, geth_url, fmt.Sprintf(template, txhash, req_id)) return } if traceg.Error != nil { fmt.Printf("Error tracing transaction g: %d %s\n", traceg.Error.Code, traceg.Error.Message) return } //print(client, turbogeth_url, fmt.Sprintf(template, txhash, req_id)) if !compareTraces(&trace, &traceg) { fmt.Printf("Different traces block %d, tx %s\n", 1720000, txhash) return } } to := common.HexToAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413") sm := make(map[common.Hash]storageEntry) start := common.HexToHash("0x5aa12c260b07325d83f0c9170a2c667948d0247cad4ad999cd00148658b0552d") req_id++ template = ` {"jsonrpc":"2.0","method":"debug_storageRangeAt","params":["0x%x", %d,"0x%x","0x%x",%d],"id":%d} ` i := 18 nextKey := &start for nextKey != nil { var sr DebugStorageRange if err := post(client, turbogeth_url, fmt.Sprintf(template, blockhash, i, to, *nextKey, 1024, req_id), &sr); err != nil { fmt.Printf("Could not get storageRange: %v\n", err) return } if sr.Error != nil { fmt.Printf("Error getting storageRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { nextKey = sr.Result.NextKey for k, v := range sr.Result.Storage { sm[k] = v } } } fmt.Printf("storageRange: %d\n", len(sm)) smg := make(map[common.Hash]storageEntry) nextKey = &start for nextKey != nil { var srg DebugStorageRange if err := post(client, geth_url, fmt.Sprintf(template, blockhash, i, to, *nextKey, 1024, req_id), &srg); err != nil { fmt.Printf("Could not get storageRange g: %v\n", err) return } if srg.Error != nil { fmt.Printf("Error getting storageRange g: %d %s\n", srg.Error.Code, srg.Error.Message) break } else { nextKey = srg.Result.NextKey for k, v := range srg.Result.Storage { smg[k] = v } } } fmt.Printf("storageRange g: %d\n", len(smg)) if !compareStorageRanges(sm, smg) { fmt.Printf("Different in storage ranges tx\n") return } } func compareAccountRanges(tg, geth map[common.Address]state.DumpAccount) bool { allAddresses := make(map[common.Address]struct{}) for k := range tg { allAddresses[k] = struct{}{} } for k := range geth { allAddresses[k] = struct{}{} } for addr := range allAddresses { tgAcc, tgOk := tg[addr] if !tgOk { fmt.Printf("missing account in TurboGeth %x\n", addr) return false } gethAcc, gethOk := geth[addr] if !gethOk { fmt.Printf("missing account in Geth %x\n", addr) return false } different := false if tgAcc.Balance != gethAcc.Balance { fmt.Printf("Different balance for %x: turbo %s, geth %s\n", addr, tgAcc.Balance, gethAcc.Balance) different = true } if tgAcc.Nonce != gethAcc.Nonce { fmt.Printf("Different nonce for %x: turbo %d, geth %d\n", addr, tgAcc.Nonce, gethAcc.Nonce) different = true } // We do not compare Root, because Turbo-geth does not compute it if tgAcc.CodeHash != gethAcc.CodeHash { fmt.Printf("Different codehash for %x: turbo %s, geth %s\n", addr, tgAcc.CodeHash, gethAcc.CodeHash) different = true } if tgAcc.Code != gethAcc.Code { fmt.Printf("Different codehash for %x: turbo %s, geth %s\n", addr, tgAcc.Code, gethAcc.Code) different = true } if different { return false } } return true } func bench4() { var client = &http.Client{ Timeout: time.Second * 600, } turbogeth_url := "http://localhost:9545" blockhash := common.HexToHash("0xdf15213766f00680c6a20ba76ba2cc9534435e19bc490039f3a7ef42095c8d13") req_id := 1 template := ` {"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x%x",true],"id":%d} ` var b EthBlockByNumber if err := post(client, turbogeth_url, fmt.Sprintf(template, 1720000, req_id), &b); err != nil { fmt.Printf("Could not retrieve block %d: %v\n", 1720000, err) return } if b.Error != nil { fmt.Printf("Error retrieving block: %d %s\n", b.Error.Code, b.Error.Message) } for txindex := 0; txindex < 6; txindex++ { txhash := b.Result.Transactions[txindex].Hash req_id++ template = ` {"jsonrpc":"2.0","method":"debug_traceTransaction","params":["%s"],"id":%d} ` var trace EthTxTrace if err := post(client, turbogeth_url, fmt.Sprintf(template, txhash, req_id), &trace); err != nil { fmt.Printf("Could not trace transaction %s: %v\n", txhash, err) print(client, turbogeth_url, fmt.Sprintf(template, txhash, req_id)) return } if trace.Error != nil { fmt.Printf("Error tracing transaction: %d %s\n", trace.Error.Code, trace.Error.Message) } print(client, turbogeth_url, fmt.Sprintf(template, txhash, req_id)) } to := common.HexToAddress("0x8b3b3b624c3c0397d3da8fd861512393d51dcbac") sm := make(map[common.Hash]storageEntry) start := common.HexToHash("0xa283ff49a55f86420a4acd5835658d8f45180db430c7b0d7ae98da5c64f620dc") req_id++ template = ` {"jsonrpc":"2.0","method":"debug_storageRangeAt","params":["0x%x", %d,"0x%x","0x%x",%d],"id":%d} ` i := 6 nextKey := &start for nextKey != nil { var sr DebugStorageRange if err := post(client, turbogeth_url, fmt.Sprintf(template, blockhash, i, to, *nextKey, 1024, req_id), &sr); err != nil { fmt.Printf("Could not get storageRange: %v\n", err) return } if sr.Error != nil { fmt.Printf("Error getting storageRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { nextKey = sr.Result.NextKey for k, v := range sr.Result.Storage { sm[k] = v } } } fmt.Printf("storageRange: %d\n", len(sm)) } func bench5() { var client = &http.Client{ Timeout: time.Second * 600, } turbogethURL := routes[TurboGeth] file, err := os.Open("txs.txt") if err != nil { panic(err) } req_id := 0 template := `{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x%s"],"id":%d}` var receipt EthReceipt scanner := bufio.NewScanner(file) for scanner.Scan() { req_id++ if err = post(client, turbogethURL, fmt.Sprintf(template, scanner.Text(), req_id), &receipt); err != nil { fmt.Printf("Count not get receipt: %s: %v\n", scanner.Text(), err) return } if receipt.Error != nil { fmt.Printf("Error getting receipt: %d %s\n", receipt.Error.Code, receipt.Error.Message) return } } err = scanner.Err() if err != nil { panic(err) } } func bench6() { var client = &http.Client{ Timeout: time.Second * 600, } req_id := 0 turbogeth_url := "http://localhost:8545" req_id++ template := ` {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":%d} ` var blockNumber EthBlockNumber if err := post(client, turbogeth_url, fmt.Sprintf(template, req_id), &blockNumber); err != nil { fmt.Printf("Could not get block number: %v\n", err) return } if blockNumber.Error != nil { fmt.Printf("Error getting block number: %d %s\n", blockNumber.Error.Code, blockNumber.Error.Message) return } lastBlock := blockNumber.Number fmt.Printf("Last block: %d\n", lastBlock) accounts := make(map[common.Address]struct{}) firstBn := 100000 for bn := firstBn; bn <= int(lastBlock); bn++ { req_id++ template := ` {"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x%x",true],"id":%d} ` var b EthBlockByNumber if err := post(client, turbogeth_url, fmt.Sprintf(template, bn, req_id), &b); err != nil { fmt.Printf("Could not retrieve block %d: %v\n", bn, err) return } if b.Error != nil { fmt.Printf("Error retrieving block: %d %s\n", b.Error.Code, b.Error.Message) } accounts[b.Result.Miner] = struct{}{} for _, tx := range b.Result.Transactions { accounts[tx.From] = struct{}{} if tx.To != nil { accounts[*tx.To] = struct{}{} } req_id++ template = ` {"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["%s"],"id":%d} ` var receipt EthReceipt if err := post(client, turbogeth_url, fmt.Sprintf(template, tx.Hash, req_id), &receipt); err != nil { fmt.Printf("Count not get receipt: %s: %v\n", tx.Hash, err) print(client, turbogeth_url, fmt.Sprintf(template, tx.Hash, req_id)) return } if receipt.Error != nil { fmt.Printf("Error getting receipt: %d %s\n", receipt.Error.Code, receipt.Error.Message) return } } } } func bench7() { var client = &http.Client{ Timeout: time.Second * 600, } turbogethURL := routes[TurboGeth] gethURL := routes[Geth] blockhash := common.HexToHash("0xdd3eb495312b11621669be45a2d50f8a66f2616bc72a610e2cbf1aebf9e4a9aa") reqID := 1 to := common.HexToAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413") var sm map[common.Hash]storageEntry var smg map[common.Hash]storageEntry start := common.HexToHash("0x4a17477338cba00d8a94336ef62ea15f68e77ad0ca738fa405daa13bf0874134") reqID++ template := ` {"jsonrpc":"2.0","method":"debug_storageRangeAt","params":["0x%x", %d,"0x%x","0x%x",%d],"id":%d} ` i := 0 nextKey := &start nextKeyG := &start for nextKey != nil { sm = make(map[common.Hash]storageEntry) smg = make(map[common.Hash]storageEntry) var sr DebugStorageRange if err := post(client, turbogethURL, fmt.Sprintf(template, blockhash, i, to, *nextKey, 2, reqID), &sr); err != nil { fmt.Printf("Could not get storageRange: %v\n", err) return } if sr.Error != nil { fmt.Printf("Error getting storageRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { nextKey = sr.Result.NextKey for k, v := range sr.Result.Storage { sm[k] = v if v.Key == nil { fmt.Printf("%x: %x", k, v) } } } var srg DebugStorageRange if err := post(client, gethURL, fmt.Sprintf(template, blockhash, i, to, *nextKeyG, 2, reqID), &srg); err != nil { fmt.Printf("Could not get storageRange: %v\n", err) return } if srg.Error != nil { fmt.Printf("Error getting storageRange: %d %s\n", sr.Error.Code, sr.Error.Message) break } else { for k, v := range srg.Result.Storage { smg[k] = v if v.Key == nil { fmt.Printf("%x: %x", k, v) } } nextKeyG = srg.Result.NextKey if *nextKey != *nextKeyG { fmt.Printf("Non matching nextKey %x %x\n", *nextKey, *nextKeyG) fmt.Printf("len(sm) %d, len(smg) %d\n", len(sm), len(smg)) fmt.Printf("================sm\n") printStorageRange(sm) fmt.Printf("================smg\n") printStorageRange(smg) return } } if !compareStorageRanges(sm, smg) { fmt.Printf("len(sm) %d, len(smg) %d\n", len(sm), len(smg)) fmt.Printf("================sm\n") printStorageRange(sm) fmt.Printf("================smg\n") printStorageRange(smg) return } } fmt.Printf("storageRange: %d\n", len(sm)) } func bench8() { var client = &http.Client{ Timeout: time.Second * 600, } turbogethURL := "http://localhost:8545" reqID := 1 to := common.HexToAddress("0x9653c9859b18f8777fe4eec9a67c9f64f3d6f62a") reqID++ template := `{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock": "0x%x", "toBlock": "0x%x", "address": "0x%x"}],"id":%d}` var logs EthLogs if err := post(client, turbogethURL, fmt.Sprintf(template, 49000, 49100, to, reqID), &logs); err != nil { fmt.Printf("Could not get eth_getLogs: %v\n", err) return } if logs.Error != nil { fmt.Printf("Error getting eth_getLogs: %d %s\n", logs.Error.Code, logs.Error.Message) } } func main() { var ( ostream log.Handler glogger *log.GlogHandler ) usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" output := io.Writer(os.Stderr) if usecolor { output = colorable.NewColorableStderr() } ostream = log.StreamHandler(output, log.TerminalFormat(usecolor)) glogger = log.NewGlogHandler(ostream) log.Root().SetHandler(glogger) glogger.Verbosity(log.Lvl(3)) // 3 == verbosity INFO flag.Parse() switch *action { case "proofs": proofs(*chaindata, *url, *block) case "fixState": fixState(*chaindata, *url) case "bench1": bench1(true, true) case "bench3": bench3() case "bench7": bench7() case "bench8": bench8() } }