Compare commits

...

6 Commits

Author SHA1 Message Date
i-norden
1d578b3bf9 debug_traceBlock test
Some checks failed
Test the stack. / Run unit tests (pull_request) Failing after 2m17s
Test the stack. / Run integration tests (pull_request) Failing after 6m3s
2023-11-02 13:03:01 -05:00
i-norden
5c387aaf1e TraceBlockByHash and TraceBlockByNumber methods 2023-11-02 13:03:01 -05:00
i-norden
289bf6dde4 debug_traceCall test 2023-11-02 13:03:01 -05:00
i-norden
2883dcb132 go mod 2023-11-02 13:03:01 -05:00
i-norden
cfb88d9f09 add ability to fall through to another RPC provider 2023-11-02 13:03:01 -05:00
i-norden
20894b7482 go fmt; temporary go mod using replace directive 2023-11-02 13:02:54 -05:00
7 changed files with 580 additions and 21 deletions

4
go.mod
View File

@ -295,9 +295,9 @@ require (
replace ( replace (
github.com/cerc-io/eth-ipfs-state-validator/v5 => git.vdb.to/cerc-io/eth-ipfs-state-validator/v5 v5.1.1-alpha github.com/cerc-io/eth-ipfs-state-validator/v5 => git.vdb.to/cerc-io/eth-ipfs-state-validator/v5 v5.1.1-alpha
github.com/cerc-io/eth-iterator-utils => git.vdb.to/cerc-io/eth-iterator-utils v0.1.2 github.com/cerc-io/eth-iterator-utils => git.vdb.to/cerc-io/eth-iterator-utils v0.1.2-beta
github.com/cerc-io/eth-testing => git.vdb.to/cerc-io/eth-testing v0.3.1 github.com/cerc-io/eth-testing => git.vdb.to/cerc-io/eth-testing v0.3.1
github.com/cerc-io/ipld-eth-statedb => git.vdb.to/cerc-io/ipld-eth-statedb v0.0.6-alpha github.com/cerc-io/ipld-eth-statedb => /Users/iannorden/go/src/github.com/cerc-io/ipld-eth-statedb // git.vdb.to/cerc-io/ipld-eth-statedb v0.0.6-alpha
github.com/cerc-io/plugeth-statediff => git.vdb.to/cerc-io/plugeth-statediff v0.1.4 github.com/cerc-io/plugeth-statediff => git.vdb.to/cerc-io/plugeth-statediff v0.1.4
github.com/ethereum/go-ethereum => git.vdb.to/cerc-io/plugeth v0.0.0-20230808125822-691dc334fab1 github.com/ethereum/go-ethereum => git.vdb.to/cerc-io/plugeth v0.0.0-20230808125822-691dc334fab1
github.com/openrelayxyz/plugeth-utils => git.vdb.to/cerc-io/plugeth-utils v0.0.0-20230706160122-cd41de354c46 github.com/openrelayxyz/plugeth-utils => git.vdb.to/cerc-io/plugeth-utils v0.0.0-20230706160122-cd41de354c46

6
go.sum
View File

@ -47,11 +47,9 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
git.vdb.to/cerc-io/eth-ipfs-state-validator/v5 v5.1.1-alpha h1:tophBXLyhMKmQILsxjfUTwQW+TGHtEJNajVULJJLhe0= git.vdb.to/cerc-io/eth-ipfs-state-validator/v5 v5.1.1-alpha h1:tophBXLyhMKmQILsxjfUTwQW+TGHtEJNajVULJJLhe0=
git.vdb.to/cerc-io/eth-ipfs-state-validator/v5 v5.1.1-alpha/go.mod h1:e/9QV7BeaAvR+E6G3fab5+OxKGOEwCmJzs8WReuqKZM= git.vdb.to/cerc-io/eth-ipfs-state-validator/v5 v5.1.1-alpha/go.mod h1:e/9QV7BeaAvR+E6G3fab5+OxKGOEwCmJzs8WReuqKZM=
git.vdb.to/cerc-io/eth-iterator-utils v0.1.2 h1:+3+T+J21J/VkhlCFujl8HT4XuwebavIuKj0+qfE+0QM= git.vdb.to/cerc-io/eth-iterator-utils v0.1.2-beta h1:pv1HCRlD7/1X7i35MWylwGhji0aWI4QujsrJoYOW55U=
git.vdb.to/cerc-io/eth-iterator-utils v0.1.2/go.mod h1:OvXbdWbZ5viBXC/Ui1EkhsSmGB+AUX+TjGa3UDAfjfg= git.vdb.to/cerc-io/eth-iterator-utils v0.1.2-beta/go.mod h1:OvXbdWbZ5viBXC/Ui1EkhsSmGB+AUX+TjGa3UDAfjfg=
git.vdb.to/cerc-io/eth-testing v0.3.1 h1:sPnlMev6oEgTjsW7GtUkSsjKNG/+X6P9q0izSejLGpM= git.vdb.to/cerc-io/eth-testing v0.3.1 h1:sPnlMev6oEgTjsW7GtUkSsjKNG/+X6P9q0izSejLGpM=
git.vdb.to/cerc-io/ipld-eth-statedb v0.0.6-alpha h1:0YnoohjuK7w2JhIqLDDyVUNnu1RjyeDqqyhm6MojD74=
git.vdb.to/cerc-io/ipld-eth-statedb v0.0.6-alpha/go.mod h1:cCQCfIUX5vTZBHeAfLa8wLUeLKO8kygDPm7Afc+MMI8=
git.vdb.to/cerc-io/plugeth v0.0.0-20230808125822-691dc334fab1 h1:KLjxHwp9Zp7xhECccmJS00RiL+VwTuUGLU7qeIctg8g= git.vdb.to/cerc-io/plugeth v0.0.0-20230808125822-691dc334fab1 h1:KLjxHwp9Zp7xhECccmJS00RiL+VwTuUGLU7qeIctg8g=
git.vdb.to/cerc-io/plugeth v0.0.0-20230808125822-691dc334fab1/go.mod h1:cYXZu70+6xmDgIgrTD81GPasv16piiAFJnKyAbwVPMU= git.vdb.to/cerc-io/plugeth v0.0.0-20230808125822-691dc334fab1/go.mod h1:cYXZu70+6xmDgIgrTD81GPasv16piiAFJnKyAbwVPMU=
git.vdb.to/cerc-io/plugeth-statediff v0.1.4 h1:swDJDAk1/yu6MOHAvxeyZz+MS1H9FCmSWGQRswFxFEw= git.vdb.to/cerc-io/plugeth-statediff v0.1.4 h1:swDJDAk1/yu6MOHAvxeyZz+MS1H9FCmSWGQRswFxFEw=

View File

@ -0,0 +1,471 @@
// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program 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 Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package eth_debug_test
import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
"math/big"
"sort"
"time"
statediff "github.com/cerc-io/plugeth-statediff"
"github.com/cerc-io/plugeth-statediff/adapt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"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/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/jmoiron/sqlx"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
)
var (
db *sqlx.DB
chainConfig = &*params.TestChainConfig
mockTD = big.NewInt(1337)
ctx = context.Background()
tb *testBackend
accounts Accounts
genBlocks int
)
var _ = BeforeSuite(func() {
chainConfig.LondonBlock = big.NewInt(100)
// db and type initializations
var err error
db = shared.SetupDB()
// Initialize test accounts
accounts = newAccounts(3)
genesis := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
},
}
genBlocks = 10
signer := types.HomesteadSigner{}
tb, blocks, receipts := newTestBackend(genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
})
transformer := shared.SetupTestStateDiffIndexer(ctx, chainConfig, blocks[0].Hash())
params := statediff.Params{}
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
builder := statediff.NewBuilder(adapt.GethStateView(tb.chain.StateCache()))
for i, block := range blocks {
var args statediff.Args
var rcts types.Receipts
if i == 0 {
args = statediff.Args{
OldStateRoot: common.Hash{},
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
} else {
args = statediff.Args{
OldStateRoot: blocks[i-1].Root(),
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
rcts = receipts[i-1]
}
diff, err := builder.BuildStateDiffObject(args, params)
Expect(err).ToNot(HaveOccurred())
tx, err := transformer.PushBlock(block, rcts, mockTD)
Expect(err).ToNot(HaveOccurred())
defer tx.RollbackOnFailure(err)
for _, node := range diff.Nodes {
err = transformer.PushStateNode(tx, node, block.Hash().String())
Expect(err).ToNot(HaveOccurred())
}
for _, ipld := range diff.IPLDs {
err = transformer.PushIPLD(tx, ipld)
Expect(err).ToNot(HaveOccurred())
}
err = tx.Submit()
Expect(err).ToNot(HaveOccurred())
}
backend, err := eth.NewEthBackend(db, &eth.Config{
ChainConfig: chainConfig,
VMConfig: vm.Config{},
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
GroupCacheConfig: &shared.GroupCacheConfig{
StateDB: shared.GroupConfig{
Name: "eth_state_test",
CacheSizeInMB: 8,
CacheExpiryInMins: 60,
LogStatsIntervalInSecs: 0,
},
},
})
Expect(err).ToNot(HaveOccurred())
tracingAPI, _ = eth.NewTracingAPI(backend, nil, eth.APIConfig{StateDiffTimeout: shared.DefaultStateDiffTimeout})
})
var _ = AfterSuite(func() {
shared.TearDownDB(db)
tb.teardown()
})
var (
tracingAPI *eth.TracingAPI
)
var _ = Describe("eth state reading tests", func() {
Describe("debug_traceCall", func() {
It("Works", func() {
var testSuite = []struct {
blockNumber rpc.BlockNumber
call eth.TransactionArgs
config *eth.TraceCallConfig
expectErr error
expect string
}{
// Standard JSON trace upon the genesis, plain transfer.
{
blockNumber: rpc.BlockNumber(0),
call: eth.TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: nil,
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
// Standard JSON trace upon the head, plain transfer.
{
blockNumber: rpc.BlockNumber(genBlocks),
call: eth.TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: nil,
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
// Standard JSON trace upon the non-existent block, error expects
{
blockNumber: rpc.BlockNumber(genBlocks + 1),
call: eth.TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: fmt.Errorf("block #%d not found", genBlocks+1),
//expect: nil,
},
// Standard JSON trace upon the latest block
{
blockNumber: rpc.LatestBlockNumber,
call: eth.TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: nil,
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
// Tracing on 'pending' should fail:
{
blockNumber: rpc.PendingBlockNumber,
call: eth.TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: fmt.Errorf("tracing on top of pending is not supported"),
},
{
blockNumber: rpc.LatestBlockNumber,
call: eth.TransactionArgs{
From: &accounts[0].addr,
Input: &hexutil.Bytes{0x43}, // blocknumber
},
config: &eth.TraceCallConfig{
BlockOverrides: &eth.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
expectErr: nil,
expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[
{"pc":0,"op":"NUMBER","gas":24946984,"gasCost":2,"depth":1,"stack":[]},
{"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`,
},
}
for _, testspec := range testSuite {
result, err := tracingAPI.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config)
if testspec.expectErr != nil {
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(testspec.expectErr))
} else {
Expect(err).ToNot(HaveOccurred())
var have *logger.ExecutionResult
err := json.Unmarshal(result.(json.RawMessage), &have)
Expect(err).ToNot(HaveOccurred())
var want *logger.ExecutionResult
err = json.Unmarshal([]byte(testspec.expect), &want)
Expect(err).ToNot(HaveOccurred())
Expect(have).To(Equal(want))
}
}
})
})
Describe("debug_traceBlock", func() {
It("Works", func() {
var testSuite = []struct {
blockNumber rpc.BlockNumber
config *eth.TraceConfig
want string
expectErr error
}{
// Trace genesis block, expect error
{
blockNumber: rpc.BlockNumber(0),
expectErr: errors.New("genesis is not traceable"),
},
// Trace head block
{
blockNumber: rpc.BlockNumber(genBlocks),
want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
},
// Trace non-existent block
{
blockNumber: rpc.BlockNumber(genBlocks + 1),
expectErr: fmt.Errorf("block #%d not found", genBlocks+1),
},
// Trace latest block
{
blockNumber: rpc.LatestBlockNumber,
want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
},
// Trace pending block
{
blockNumber: rpc.PendingBlockNumber,
want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
},
}
for _, tc := range testSuite {
result, err := tracingAPI.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config)
if tc.expectErr != nil {
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(tc.expectErr))
} else {
Expect(err).ToNot(HaveOccurred())
have, _ := json.Marshal(result)
want := tc.want
Expect(have).To(Equal(want))
}
}
})
})
})
type testBackend struct {
chainConfig *params.ChainConfig
engine consensus.Engine
chaindb ethdb.Database
chain *core.BlockChain
refHook func() // Hook is invoked when the requested state is referenced
relHook func() // Hook is invoked when the requested state is released
}
func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
return b.chain.GetHeaderByHash(hash), nil
}
func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber {
return b.chain.CurrentHeader(), nil
}
return b.chain.GetHeaderByNumber(uint64(number)), nil
}
func (b *testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
return b.chain.GetBlockByHash(hash), nil
}
func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
if number == rpc.PendingBlockNumber || number == rpc.LatestBlockNumber {
return b.chain.GetBlockByNumber(b.chain.CurrentBlock().Number.Uint64()), nil
}
return b.chain.GetBlockByNumber(uint64(number)), nil
}
func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
return tx, hash, blockNumber, index, nil
}
func (b *testBackend) RPCGasCap() uint64 {
return 25000000
}
func (b *testBackend) ChainConfig() *params.ChainConfig {
return b.chainConfig
}
func (b *testBackend) Engine() consensus.Engine {
return b.engine
}
func (b *testBackend) ChainDb() ethdb.Database {
return b.chaindb
}
// teardown releases the associated resources.
func (b *testBackend) teardown() {
b.chain.Stop()
}
var (
errStateNotFound = errors.New("state not found")
errBlockNotFound = errors.New("block not found")
)
func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
statedb, err := b.chain.StateAt(block.Root())
if err != nil {
return nil, nil, errStateNotFound
}
if b.refHook != nil {
b.refHook()
}
release := func() {
if b.relHook != nil {
b.relHook()
}
}
return statedb, release, nil
}
func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, vm.BlockContext{}, nil, nil, errBlockNotFound
}
statedb, release, err := b.StateAtBlock(ctx, parent, reexec, nil, true, false)
if err != nil {
return nil, vm.BlockContext{}, nil, nil, errStateNotFound
}
if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.BlockContext{}, statedb, release, nil
}
// Recompute transactions up to the target index.
signer := types.MakeSigner(b.chainConfig, block.Number())
for idx, tx := range block.Transactions() {
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), b.chain, nil)
if idx == txIndex {
return msg, context, statedb, release, nil
}
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
}
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}
// testBackend creates a new test backend. OBS: After test is done, teardown must be
// invoked in order to release associated resources.
func newTestBackend(n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) (*testBackend, types.Blocks, []types.Receipts) {
backend := &testBackend{
chainConfig: gspec.Config,
engine: ethash.NewFaker(),
chaindb: rawdb.NewMemoryDatabase(),
}
// Generate blocks for testing
_, blocks, receipts := core.GenerateChainWithGenesis(gspec, backend.engine, n, generator)
// Import the canonical chain
cacheConfig := &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0,
TrieDirtyDisabled: true, // Archive mode
}
chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, gspec, nil, backend.engine, vm.Config{}, nil, nil)
Expect(err).ToNot(HaveOccurred())
n, err = chain.InsertChain(blocks)
Expect(err).ToNot(HaveOccurred())
backend.chain = chain
return backend, blocks, receipts
}
type Account struct {
key *ecdsa.PrivateKey
addr common.Address
}
type Accounts []Account
func (a Accounts) Len() int { return len(a) }
func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 }
func newAccounts(n int) (accounts Accounts) {
for i := 0; i < n; i++ {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
accounts = append(accounts, Account{key: key, addr: addr})
}
sort.Sort(accounts)
return accounts
}

View File

@ -0,0 +1,29 @@
// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program 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 Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package eth_debug_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestETHSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "ipld-eth-server/pkg/eth/state_test")
}

View File

@ -23,7 +23,7 @@ import (
"math/big" "math/big"
"os" "os"
"github.com/cerc-io/plugeth-statediff" statediff "github.com/cerc-io/plugeth-statediff"
"github.com/cerc-io/plugeth-statediff/adapt" "github.com/cerc-io/plugeth-statediff/adapt"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"math/big" "math/big"
"github.com/cerc-io/plugeth-statediff" statediff "github.com/cerc-io/plugeth-statediff"
"github.com/cerc-io/plugeth-statediff/adapt" "github.com/cerc-io/plugeth-statediff/adapt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"

View File

@ -19,7 +19,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
@ -32,10 +31,9 @@ const (
// TracingAPI is the collection of tracing APIs exposed over the private debugging endpoint. // TracingAPI is the collection of tracing APIs exposed over the private debugging endpoint.
type TracingAPI struct { type TracingAPI struct {
backend *Backend backend *Backend
rpc *rpc.Client rpc *rpc.Client
ethClient *ethclient.Client config APIConfig
config APIConfig
} }
// NewTracingAPI creates a new TracingAPI with the provided underlying Backend // NewTracingAPI creates a new TracingAPI with the provided underlying Backend
@ -52,15 +50,10 @@ func NewTracingAPI(b *Backend, client *rpc.Client, config APIConfig) (*TracingAP
if config.ProxyOnError && client == nil { if config.ProxyOnError && client == nil {
return nil, errors.New("ipld-eth-server is configured to forward all calls to proxy node on errors but no proxy node is configured") return nil, errors.New("ipld-eth-server is configured to forward all calls to proxy node on errors but no proxy node is configured")
} }
var ethClient *ethclient.Client
if client != nil {
ethClient = ethclient.NewClient(client)
}
return &TracingAPI{ return &TracingAPI{
backend: b, backend: b,
rpc: client, rpc: client,
ethClient: ethClient, config: config,
config: config,
}, nil }, nil
} }
@ -87,6 +80,20 @@ type TraceCallConfig struct {
// created during the execution of EVM if the given transaction was added on // created during the execution of EVM if the given transaction was added on
// top of the provided block and returns them as a JSON object. // top of the provided block and returns them as a JSON object.
func (api *TracingAPI) TraceCall(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { func (api *TracingAPI) TraceCall(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
trace, err := api.localTraceCall(ctx, args, blockNrOrHash, config)
if trace != nil && err == nil {
return trace, nil
}
if api.config.ProxyOnError {
var res interface{}
if err := api.rpc.CallContext(ctx, &res, "debug_traceCall", args, blockNrOrHash, config); res != nil && err == nil {
return res, nil
}
}
return nil, err
}
func (api *TracingAPI) localTraceCall(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
// Try to retrieve the specified block // Try to retrieve the specified block
var ( var (
err error err error
@ -229,9 +236,63 @@ type txTraceResult struct {
Error string `json:"error,omitempty"` // Trace failure produced by the tracer Error string `json:"error,omitempty"` // Trace failure produced by the tracer
} }
// TraceBlockByNumber returns the structured logs created during the execution of
// EVM and returns them as a JSON object.
func (api *TracingAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
block, err := api.blockByNumber(ctx, number)
if err != nil {
return nil, err
}
trace, err := api.traceBlock(ctx, block, config)
if trace != nil && err == nil {
return trace, nil
}
if api.config.ProxyOnError {
var res []*txTraceResult
if err := api.rpc.CallContext(ctx, &res, "debug_traceBlockByNumber", number, config); res != nil && err == nil {
return res, nil
}
}
return nil, err
}
// TraceBlockByHash returns the structured logs created during the execution of
// EVM and returns them as a JSON object.
func (api *TracingAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
block, err := api.blockByHash(ctx, hash)
if err != nil {
return nil, err
}
trace, err := api.traceBlock(ctx, block, config)
if trace != nil && err == nil {
return trace, nil
}
if api.config.ProxyOnError {
var res []*txTraceResult
if err := api.rpc.CallContext(ctx, &res, "debug_traceBlockByHash", hash, config); res != nil && err == nil {
return res, nil
}
}
return nil, err
}
// TraceBlock returns the structured logs created during the execution of EVM // TraceBlock returns the structured logs created during the execution of EVM
// and returns them as a JSON object. // and returns them as a JSON object.
func (api *TracingAPI) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) { func (api *TracingAPI) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) {
trace, err := api.localTraceBlock(ctx, blob, config)
if trace != nil && err == nil {
return trace, nil
}
if api.config.ProxyOnError {
var res []*txTraceResult
if err := api.rpc.CallContext(ctx, &res, "debug_traceBlock", blob, config); res != nil && err == nil {
return res, nil
}
}
return nil, err
}
func (api *TracingAPI) localTraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) {
block := new(types.Block) block := new(types.Block)
if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { if err := rlp.Decode(bytes.NewReader(blob), block); err != nil {
return nil, fmt.Errorf("could not decode block: %v", err) return nil, fmt.Errorf("could not decode block: %v", err)