diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 6854c9624..27d40f1d6 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -462,6 +462,9 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// chain doesn't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
if b.pendingBlock.Header().BaseFee != nil {
return b.pendingBlock.Header().BaseFee, nil
}
diff --git a/cmd/devp2p/dns_cloudflare.go b/cmd/devp2p/dns_cloudflare.go
index 596254df9..d67aaea1a 100644
--- a/cmd/devp2p/dns_cloudflare.go
+++ b/cmd/devp2p/dns_cloudflare.go
@@ -133,7 +133,8 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string)
log.Info(fmt.Sprintf("Creating %s = %q", path, val))
ttl := rootTTL
if path != name {
- ttl = treeNodeTTL // Max TTL permitted by Cloudflare
+ ttl = treeNodeTTLCloudflare // Max TTL permitted by Cloudflare
+
}
record := cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl}
_, err = c.CreateDNSRecord(context.Background(), c.zoneID, record)
diff --git a/cmd/devp2p/dnscmd.go b/cmd/devp2p/dnscmd.go
index 66deef56e..85f28b8cb 100644
--- a/cmd/devp2p/dnscmd.go
+++ b/cmd/devp2p/dnscmd.go
@@ -115,8 +115,9 @@ var (
)
const (
- rootTTL = 30 * 60 // 30 min
- treeNodeTTL = 4 * 7 * 24 * 60 * 60 // 4 weeks
+ rootTTL = 30 * 60 // 30 min
+ treeNodeTTL = 4 * 7 * 24 * 60 * 60 // 4 weeks
+ treeNodeTTLCloudflare = 24 * 60 * 60 // 1 day
)
// dnsSync performs dnsSyncCommand.
diff --git a/cmd/devp2p/internal/ethtest/helpers.go b/cmd/devp2p/internal/ethtest/helpers.go
index 88d8e143c..e695cd42d 100644
--- a/cmd/devp2p/internal/ethtest/helpers.go
+++ b/cmd/devp2p/internal/ethtest/helpers.go
@@ -131,7 +131,7 @@ func (c *Conn) handshake() error {
}
c.negotiateEthProtocol(msg.Caps)
if c.negotiatedProtoVersion == 0 {
- return fmt.Errorf("unexpected eth protocol version")
+ return fmt.Errorf("could not negotiate protocol (remote caps: %v, local eth version: %v)", msg.Caps, c.ourHighestProtoVersion)
}
return nil
default:
diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go
index bbc955cd7..28ba4aa76 100644
--- a/cmd/devp2p/internal/ethtest/suite.go
+++ b/cmd/devp2p/internal/ethtest/suite.go
@@ -52,35 +52,35 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) (*Suite, e
func (s *Suite) AllEthTests() []utesting.Test {
return []utesting.Test{
// status
- {Name: "TestStatus", Fn: s.TestStatus},
+ {Name: "TestStatus65", Fn: s.TestStatus65},
{Name: "TestStatus66", Fn: s.TestStatus66},
// get block headers
- {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders},
+ {Name: "TestGetBlockHeaders65", Fn: s.TestGetBlockHeaders65},
{Name: "TestGetBlockHeaders66", Fn: s.TestGetBlockHeaders66},
{Name: "TestSimultaneousRequests66", Fn: s.TestSimultaneousRequests66},
{Name: "TestSameRequestID66", Fn: s.TestSameRequestID66},
{Name: "TestZeroRequestID66", Fn: s.TestZeroRequestID66},
// get block bodies
- {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies},
+ {Name: "TestGetBlockBodies65", Fn: s.TestGetBlockBodies65},
{Name: "TestGetBlockBodies66", Fn: s.TestGetBlockBodies66},
// broadcast
- {Name: "TestBroadcast", Fn: s.TestBroadcast},
+ {Name: "TestBroadcast65", Fn: s.TestBroadcast65},
{Name: "TestBroadcast66", Fn: s.TestBroadcast66},
- {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
+ {Name: "TestLargeAnnounce65", Fn: s.TestLargeAnnounce65},
{Name: "TestLargeAnnounce66", Fn: s.TestLargeAnnounce66},
- {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce},
+ {Name: "TestOldAnnounce65", Fn: s.TestOldAnnounce65},
{Name: "TestOldAnnounce66", Fn: s.TestOldAnnounce66},
- {Name: "TestBlockHashAnnounce", Fn: s.TestBlockHashAnnounce},
+ {Name: "TestBlockHashAnnounce65", Fn: s.TestBlockHashAnnounce65},
{Name: "TestBlockHashAnnounce66", Fn: s.TestBlockHashAnnounce66},
// malicious handshakes + status
- {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
- {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
+ {Name: "TestMaliciousHandshake65", Fn: s.TestMaliciousHandshake65},
+ {Name: "TestMaliciousStatus65", Fn: s.TestMaliciousStatus65},
{Name: "TestMaliciousHandshake66", Fn: s.TestMaliciousHandshake66},
{Name: "TestMaliciousStatus66", Fn: s.TestMaliciousStatus66},
// test transactions
- {Name: "TestTransaction", Fn: s.TestTransaction},
+ {Name: "TestTransaction65", Fn: s.TestTransaction65},
{Name: "TestTransaction66", Fn: s.TestTransaction66},
- {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx},
+ {Name: "TestMaliciousTx65", Fn: s.TestMaliciousTx65},
{Name: "TestMaliciousTx66", Fn: s.TestMaliciousTx66},
{Name: "TestLargeTxRequest66", Fn: s.TestLargeTxRequest66},
{Name: "TestNewPooledTxs66", Fn: s.TestNewPooledTxs66},
@@ -89,17 +89,17 @@ func (s *Suite) AllEthTests() []utesting.Test {
func (s *Suite) EthTests() []utesting.Test {
return []utesting.Test{
- {Name: "TestStatus", Fn: s.TestStatus},
- {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders},
- {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies},
- {Name: "TestBroadcast", Fn: s.TestBroadcast},
- {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
- {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce},
- {Name: "TestBlockHashAnnounce", Fn: s.TestBlockHashAnnounce},
- {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
- {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
- {Name: "TestTransaction", Fn: s.TestTransaction},
- {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx},
+ {Name: "TestStatus65", Fn: s.TestStatus65},
+ {Name: "TestGetBlockHeaders65", Fn: s.TestGetBlockHeaders65},
+ {Name: "TestGetBlockBodies65", Fn: s.TestGetBlockBodies65},
+ {Name: "TestBroadcast65", Fn: s.TestBroadcast65},
+ {Name: "TestLargeAnnounce65", Fn: s.TestLargeAnnounce65},
+ {Name: "TestOldAnnounce65", Fn: s.TestOldAnnounce65},
+ {Name: "TestBlockHashAnnounce65", Fn: s.TestBlockHashAnnounce65},
+ {Name: "TestMaliciousHandshake65", Fn: s.TestMaliciousHandshake65},
+ {Name: "TestMaliciousStatus65", Fn: s.TestMaliciousStatus65},
+ {Name: "TestTransaction65", Fn: s.TestTransaction65},
+ {Name: "TestMaliciousTx65", Fn: s.TestMaliciousTx65},
}
}
@@ -130,9 +130,9 @@ var (
eth65 = false // indicates whether suite should negotiate eth65 connection or below.
)
-// TestStatus attempts to connect to the given node and exchange
+// TestStatus65 attempts to connect to the given node and exchange
// a status message with it.
-func (s *Suite) TestStatus(t *utesting.T) {
+func (s *Suite) TestStatus65(t *utesting.T) {
conn, err := s.dial()
if err != nil {
t.Fatalf("dial failed: %v", err)
@@ -156,9 +156,9 @@ func (s *Suite) TestStatus66(t *utesting.T) {
}
}
-// TestGetBlockHeaders tests whether the given node can respond to
+// TestGetBlockHeaders65 tests whether the given node can respond to
// a `GetBlockHeaders` request accurately.
-func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
+func (s *Suite) TestGetBlockHeaders65(t *utesting.T) {
conn, err := s.dial()
if err != nil {
t.Fatalf("dial failed: %v", err)
@@ -392,9 +392,9 @@ func (s *Suite) TestZeroRequestID66(t *utesting.T) {
}
}
-// TestGetBlockBodies tests whether the given node can respond to
+// TestGetBlockBodies65 tests whether the given node can respond to
// a `GetBlockBodies` request and that the response is accurate.
-func (s *Suite) TestGetBlockBodies(t *utesting.T) {
+func (s *Suite) TestGetBlockBodies65(t *utesting.T) {
conn, err := s.dial()
if err != nil {
t.Fatalf("dial failed: %v", err)
@@ -460,9 +460,9 @@ func (s *Suite) TestGetBlockBodies66(t *utesting.T) {
}
}
-// TestBroadcast tests whether a block announcement is correctly
+// TestBroadcast65 tests whether a block announcement is correctly
// propagated to the given node's peer(s).
-func (s *Suite) TestBroadcast(t *utesting.T) {
+func (s *Suite) TestBroadcast65(t *utesting.T) {
if err := s.sendNextBlock(eth65); err != nil {
t.Fatalf("block broadcast failed: %v", err)
}
@@ -476,8 +476,8 @@ func (s *Suite) TestBroadcast66(t *utesting.T) {
}
}
-// TestLargeAnnounce tests the announcement mechanism with a large block.
-func (s *Suite) TestLargeAnnounce(t *utesting.T) {
+// TestLargeAnnounce65 tests the announcement mechanism with a large block.
+func (s *Suite) TestLargeAnnounce65(t *utesting.T) {
nextBlock := len(s.chain.blocks)
blocks := []*NewBlock{
{
@@ -569,8 +569,8 @@ func (s *Suite) TestLargeAnnounce66(t *utesting.T) {
}
}
-// TestOldAnnounce tests the announcement mechanism with an old block.
-func (s *Suite) TestOldAnnounce(t *utesting.T) {
+// TestOldAnnounce65 tests the announcement mechanism with an old block.
+func (s *Suite) TestOldAnnounce65(t *utesting.T) {
if err := s.oldAnnounce(eth65); err != nil {
t.Fatal(err)
}
@@ -584,9 +584,9 @@ func (s *Suite) TestOldAnnounce66(t *utesting.T) {
}
}
-// TestBlockHashAnnounce sends a new block hash announcement and expects
+// TestBlockHashAnnounce65 sends a new block hash announcement and expects
// the node to perform a `GetBlockHeaders` request.
-func (s *Suite) TestBlockHashAnnounce(t *utesting.T) {
+func (s *Suite) TestBlockHashAnnounce65(t *utesting.T) {
if err := s.hashAnnounce(eth65); err != nil {
t.Fatalf("block hash announcement failed: %v", err)
}
@@ -600,8 +600,8 @@ func (s *Suite) TestBlockHashAnnounce66(t *utesting.T) {
}
}
-// TestMaliciousHandshake tries to send malicious data during the handshake.
-func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
+// TestMaliciousHandshake65 tries to send malicious data during the handshake.
+func (s *Suite) TestMaliciousHandshake65(t *utesting.T) {
if err := s.maliciousHandshakes(t, eth65); err != nil {
t.Fatal(err)
}
@@ -614,8 +614,8 @@ func (s *Suite) TestMaliciousHandshake66(t *utesting.T) {
}
}
-// TestMaliciousStatus sends a status package with a large total difficulty.
-func (s *Suite) TestMaliciousStatus(t *utesting.T) {
+// TestMaliciousStatus65 sends a status package with a large total difficulty.
+func (s *Suite) TestMaliciousStatus65(t *utesting.T) {
conn, err := s.dial()
if err != nil {
t.Fatalf("dial failed: %v", err)
@@ -641,9 +641,9 @@ func (s *Suite) TestMaliciousStatus66(t *utesting.T) {
}
}
-// TestTransaction sends a valid transaction to the node and
+// TestTransaction65 sends a valid transaction to the node and
// checks if the transaction gets propagated.
-func (s *Suite) TestTransaction(t *utesting.T) {
+func (s *Suite) TestTransaction65(t *utesting.T) {
if err := s.sendSuccessfulTxs(t, eth65); err != nil {
t.Fatal(err)
}
@@ -657,9 +657,9 @@ func (s *Suite) TestTransaction66(t *utesting.T) {
}
}
-// TestMaliciousTx sends several invalid transactions and tests whether
+// TestMaliciousTx65 sends several invalid transactions and tests whether
// the node will propagate them.
-func (s *Suite) TestMaliciousTx(t *utesting.T) {
+func (s *Suite) TestMaliciousTx65(t *utesting.T) {
if err := s.sendMaliciousTxs(t, eth65); err != nil {
t.Fatal(err)
}
diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go
new file mode 100644
index 000000000..d4edd33bd
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/block.go
@@ -0,0 +1,380 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package t8ntool
+
+import (
+ "crypto/ecdsa"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
+ "os"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/consensus/clique"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "gopkg.in/urfave/cli.v1"
+)
+
+//go:generate gencodec -type header -field-override headerMarshaling -out gen_header.go
+type header struct {
+ ParentHash common.Hash `json:"parentHash"`
+ OmmerHash *common.Hash `json:"sha3Uncles"`
+ Coinbase *common.Address `json:"miner"`
+ Root common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash *common.Hash `json:"transactionsRoot"`
+ ReceiptHash *common.Hash `json:"receiptsRoot"`
+ Bloom types.Bloom `json:"logsBloom"`
+ Difficulty *big.Int `json:"difficulty"`
+ Number *big.Int `json:"number" gencodec:"required"`
+ GasLimit uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed uint64 `json:"gasUsed"`
+ Time uint64 `json:"timestamp" gencodec:"required"`
+ Extra []byte `json:"extraData"`
+ MixDigest common.Hash `json:"mixHash"`
+ Nonce *types.BlockNonce `json:"nonce"`
+ BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
+}
+
+type headerMarshaling struct {
+ Difficulty *math.HexOrDecimal256
+ Number *math.HexOrDecimal256
+ GasLimit math.HexOrDecimal64
+ GasUsed math.HexOrDecimal64
+ Time math.HexOrDecimal64
+ Extra hexutil.Bytes
+ BaseFee *math.HexOrDecimal256
+}
+
+type bbInput struct {
+ Header *header `json:"header,omitempty"`
+ OmmersRlp []string `json:"ommers,omitempty"`
+ TxRlp string `json:"txs,omitempty"`
+ Clique *cliqueInput `json:"clique,omitempty"`
+
+ Ethash bool `json:"-"`
+ EthashDir string `json:"-"`
+ PowMode ethash.Mode `json:"-"`
+ Txs []*types.Transaction `json:"-"`
+ Ommers []*types.Header `json:"-"`
+}
+
+type cliqueInput struct {
+ Key *ecdsa.PrivateKey
+ Voted *common.Address
+ Authorize *bool
+ Vanity common.Hash
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface.
+func (c *cliqueInput) UnmarshalJSON(input []byte) error {
+ var x struct {
+ Key *common.Hash `json:"secretKey"`
+ Voted *common.Address `json:"voted"`
+ Authorize *bool `json:"authorize"`
+ Vanity common.Hash `json:"vanity"`
+ }
+ if err := json.Unmarshal(input, &x); err != nil {
+ return err
+ }
+ if x.Key == nil {
+ return errors.New("missing required field 'secretKey' for cliqueInput")
+ }
+ if ecdsaKey, err := crypto.ToECDSA(x.Key[:]); err != nil {
+ return err
+ } else {
+ c.Key = ecdsaKey
+ }
+ c.Voted = x.Voted
+ c.Authorize = x.Authorize
+ c.Vanity = x.Vanity
+ return nil
+}
+
+// ToBlock converts i into a *types.Block
+func (i *bbInput) ToBlock() *types.Block {
+ header := &types.Header{
+ ParentHash: i.Header.ParentHash,
+ UncleHash: types.EmptyUncleHash,
+ Coinbase: common.Address{},
+ Root: i.Header.Root,
+ TxHash: types.EmptyRootHash,
+ ReceiptHash: types.EmptyRootHash,
+ Bloom: i.Header.Bloom,
+ Difficulty: common.Big0,
+ Number: i.Header.Number,
+ GasLimit: i.Header.GasLimit,
+ GasUsed: i.Header.GasUsed,
+ Time: i.Header.Time,
+ Extra: i.Header.Extra,
+ MixDigest: i.Header.MixDigest,
+ BaseFee: i.Header.BaseFee,
+ }
+
+ // Fill optional values.
+ if i.Header.OmmerHash != nil {
+ header.UncleHash = *i.Header.OmmerHash
+ } else if len(i.Ommers) != 0 {
+ // Calculate the ommer hash if none is provided and there are ommers to hash
+ header.UncleHash = types.CalcUncleHash(i.Ommers)
+ }
+ if i.Header.Coinbase != nil {
+ header.Coinbase = *i.Header.Coinbase
+ }
+ if i.Header.TxHash != nil {
+ header.TxHash = *i.Header.TxHash
+ }
+ if i.Header.ReceiptHash != nil {
+ header.ReceiptHash = *i.Header.ReceiptHash
+ }
+ if i.Header.Nonce != nil {
+ header.Nonce = *i.Header.Nonce
+ }
+ if header.Difficulty != nil {
+ header.Difficulty = i.Header.Difficulty
+ }
+ return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers)
+}
+
+// SealBlock seals the given block using the configured engine.
+func (i *bbInput) SealBlock(block *types.Block) (*types.Block, error) {
+ switch {
+ case i.Ethash:
+ return i.sealEthash(block)
+ case i.Clique != nil:
+ return i.sealClique(block)
+ default:
+ return block, nil
+ }
+}
+
+// sealEthash seals the given block using ethash.
+func (i *bbInput) sealEthash(block *types.Block) (*types.Block, error) {
+ if i.Header.Nonce != nil {
+ return nil, NewError(ErrorConfig, fmt.Errorf("sealing with ethash will overwrite provided nonce"))
+ }
+ ethashConfig := ethash.Config{
+ PowMode: i.PowMode,
+ DatasetDir: i.EthashDir,
+ CacheDir: i.EthashDir,
+ DatasetsInMem: 1,
+ DatasetsOnDisk: 2,
+ CachesInMem: 2,
+ CachesOnDisk: 3,
+ }
+ engine := ethash.New(ethashConfig, nil, true)
+ defer engine.Close()
+ // Use a buffered chan for results.
+ // If the testmode is used, the sealer will return quickly, and complain
+ // "Sealing result is not read by miner" if it cannot write the result.
+ results := make(chan *types.Block, 1)
+ if err := engine.Seal(nil, block, results, nil); err != nil {
+ panic(fmt.Sprintf("failed to seal block: %v", err))
+ }
+ found := <-results
+ return block.WithSeal(found.Header()), nil
+}
+
+// sealClique seals the given block using clique.
+func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) {
+ // If any clique value overwrites an explicit header value, fail
+ // to avoid silently building a block with unexpected values.
+ if i.Header.Extra != nil {
+ return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique will overwrite provided extra data"))
+ }
+ header := block.Header()
+ if i.Clique.Voted != nil {
+ if i.Header.Coinbase != nil {
+ return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided coinbase"))
+ }
+ header.Coinbase = *i.Clique.Voted
+ }
+ if i.Clique.Authorize != nil {
+ if i.Header.Nonce != nil {
+ return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided nonce"))
+ }
+ if *i.Clique.Authorize {
+ header.Nonce = [8]byte{}
+ } else {
+ header.Nonce = [8]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ }
+ }
+ // Extra is fixed 32 byte vanity and 65 byte signature
+ header.Extra = make([]byte, 32+65)
+ copy(header.Extra[0:32], i.Clique.Vanity.Bytes()[:])
+
+ // Sign the seal hash and fill in the rest of the extra data
+ h := clique.SealHash(header)
+ sighash, err := crypto.Sign(h[:], i.Clique.Key)
+ if err != nil {
+ return nil, err
+ }
+ copy(header.Extra[32:], sighash)
+ block = block.WithSeal(header)
+ return block, nil
+}
+
+// BuildBlock constructs a block from the given inputs.
+func BuildBlock(ctx *cli.Context) error {
+ // Configure the go-ethereum logger
+ glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
+ glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
+ log.Root().SetHandler(glogger)
+
+ baseDir, err := createBasedir(ctx)
+ if err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
+ }
+ inputData, err := readInput(ctx)
+ if err != nil {
+ return err
+ }
+ block := inputData.ToBlock()
+ block, err = inputData.SealBlock(block)
+ if err != nil {
+ return err
+ }
+ return dispatchBlock(ctx, baseDir, block)
+}
+
+func readInput(ctx *cli.Context) (*bbInput, error) {
+ var (
+ headerStr = ctx.String(InputHeaderFlag.Name)
+ ommersStr = ctx.String(InputOmmersFlag.Name)
+ txsStr = ctx.String(InputTxsRlpFlag.Name)
+ cliqueStr = ctx.String(SealCliqueFlag.Name)
+ ethashOn = ctx.Bool(SealEthashFlag.Name)
+ ethashDir = ctx.String(SealEthashDirFlag.Name)
+ ethashMode = ctx.String(SealEthashModeFlag.Name)
+ inputData = &bbInput{}
+ )
+ if ethashOn && cliqueStr != "" {
+ return nil, NewError(ErrorConfig, fmt.Errorf("both ethash and clique sealing specified, only one may be chosen"))
+ }
+ if ethashOn {
+ inputData.Ethash = ethashOn
+ inputData.EthashDir = ethashDir
+ switch ethashMode {
+ case "normal":
+ inputData.PowMode = ethash.ModeNormal
+ case "test":
+ inputData.PowMode = ethash.ModeTest
+ case "fake":
+ inputData.PowMode = ethash.ModeFake
+ default:
+ return nil, NewError(ErrorConfig, fmt.Errorf("unknown pow mode: %s, supported modes: test, fake, normal", ethashMode))
+ }
+ }
+ if headerStr == stdinSelector || ommersStr == stdinSelector || txsStr == stdinSelector || cliqueStr == stdinSelector {
+ decoder := json.NewDecoder(os.Stdin)
+ if err := decoder.Decode(inputData); err != nil {
+ return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err))
+ }
+ }
+ if cliqueStr != stdinSelector && cliqueStr != "" {
+ var clique cliqueInput
+ if err := readFile(cliqueStr, "clique", &clique); err != nil {
+ return nil, err
+ }
+ inputData.Clique = &clique
+ }
+ if headerStr != stdinSelector {
+ var env header
+ if err := readFile(headerStr, "header", &env); err != nil {
+ return nil, err
+ }
+ inputData.Header = &env
+ }
+ if ommersStr != stdinSelector && ommersStr != "" {
+ var ommers []string
+ if err := readFile(ommersStr, "ommers", &ommers); err != nil {
+ return nil, err
+ }
+ inputData.OmmersRlp = ommers
+ }
+ if txsStr != stdinSelector {
+ var txs string
+ if err := readFile(txsStr, "txs", &txs); err != nil {
+ return nil, err
+ }
+ inputData.TxRlp = txs
+ }
+ // Deserialize rlp txs and ommers
+ var (
+ ommers = []*types.Header{}
+ txs = []*types.Transaction{}
+ )
+ if inputData.TxRlp != "" {
+ if err := rlp.DecodeBytes(common.FromHex(inputData.TxRlp), &txs); err != nil {
+ return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode transaction from rlp data: %v", err))
+ }
+ inputData.Txs = txs
+ }
+ for _, str := range inputData.OmmersRlp {
+ type extblock struct {
+ Header *types.Header
+ Txs []*types.Transaction
+ Ommers []*types.Header
+ }
+ var ommer *extblock
+ if err := rlp.DecodeBytes(common.FromHex(str), &ommer); err != nil {
+ return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode ommer from rlp data: %v", err))
+ }
+ ommers = append(ommers, ommer.Header)
+ }
+ inputData.Ommers = ommers
+
+ return inputData, nil
+}
+
+// dispatchOutput writes the output data to either stderr or stdout, or to the specified
+// files
+func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error {
+ raw, _ := rlp.EncodeToBytes(block)
+
+ type blockInfo struct {
+ Rlp hexutil.Bytes `json:"rlp"`
+ Hash common.Hash `json:"hash"`
+ }
+ var enc blockInfo
+ enc.Rlp = raw
+ enc.Hash = block.Hash()
+
+ b, err := json.MarshalIndent(enc, "", " ")
+ if err != nil {
+ return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
+ }
+ switch dest := ctx.String(OutputBlockFlag.Name); dest {
+ case "stdout":
+ os.Stdout.Write(b)
+ os.Stdout.WriteString("\n")
+ case "stderr":
+ os.Stderr.Write(b)
+ os.Stderr.WriteString("\n")
+ default:
+ if err := saveFile(baseDir, dest, enc); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index cedf96627..dfdde4217 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -49,12 +49,13 @@ type Prestate struct {
type ExecutionResult struct {
StateRoot common.Hash `json:"stateRoot"`
TxRoot common.Hash `json:"txRoot"`
- ReceiptRoot common.Hash `json:"receiptRoot"`
+ ReceiptRoot common.Hash `json:"receiptsRoot"`
LogsHash common.Hash `json:"logsHash"`
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
Receipts types.Receipts `json:"receipts"`
Rejected []*rejectedTx `json:"rejected,omitempty"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
+ GasUsed math.HexOrDecimal64 `json:"gasUsed"`
}
type ommer struct {
@@ -255,6 +256,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
Receipts: receipts,
Rejected: rejectedTxs,
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
+ GasUsed: (math.HexOrDecimal64)(gasUsed),
}
return statedb, execRs, nil
}
diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go
index 05b6ed164..b6054ea56 100644
--- a/cmd/evm/internal/t8ntool/flags.go
+++ b/cmd/evm/internal/t8ntool/flags.go
@@ -32,7 +32,11 @@ var (
}
TraceDisableMemoryFlag = cli.BoolTFlag{
Name: "trace.nomemory",
- Usage: "Disable full memory dump in traces",
+ Usage: "Disable full memory dump in traces (deprecated)",
+ }
+ TraceEnableMemoryFlag = cli.BoolFlag{
+ Name: "trace.memory",
+ Usage: "Enable full memory dump in traces",
}
TraceDisableStackFlag = cli.BoolFlag{
Name: "trace.nostack",
@@ -40,7 +44,11 @@ var (
}
TraceDisableReturnDataFlag = cli.BoolTFlag{
Name: "trace.noreturndata",
- Usage: "Disable return data output in traces",
+ Usage: "Disable return data output in traces (deprecated)",
+ }
+ TraceEnableReturnDataFlag = cli.BoolFlag{
+ Name: "trace.returndata",
+ Usage: "Enable return data output in traces",
}
OutputBasedir = cli.StringFlag{
Name: "output.basedir",
@@ -68,6 +76,14 @@ var (
"\t - into the file ",
Value: "result.json",
}
+ OutputBlockFlag = cli.StringFlag{
+ Name: "output.block",
+ Usage: "Determines where to put the `block` after building.\n" +
+ "\t`stdout` - into the stdout output\n" +
+ "\t`stderr` - into the stderr output\n" +
+ "\t - into the file ",
+ Value: "block.json",
+ }
InputAllocFlag = cli.StringFlag{
Name: "input.alloc",
Usage: "`stdin` or file name of where to find the prestate alloc to use.",
@@ -81,10 +97,41 @@ var (
InputTxsFlag = cli.StringFlag{
Name: "input.txs",
Usage: "`stdin` or file name of where to find the transactions to apply. " +
- "If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions." +
+ "If the file extension is '.rlp', then the data is interpreted as an RLP list of signed transactions." +
"The '.rlp' format is identical to the output.body format.",
Value: "txs.json",
}
+ InputHeaderFlag = cli.StringFlag{
+ Name: "input.header",
+ Usage: "`stdin` or file name of where to find the block header to use.",
+ Value: "header.json",
+ }
+ InputOmmersFlag = cli.StringFlag{
+ Name: "input.ommers",
+ Usage: "`stdin` or file name of where to find the list of ommer header RLPs to use.",
+ }
+ InputTxsRlpFlag = cli.StringFlag{
+ Name: "input.txs",
+ Usage: "`stdin` or file name of where to find the transactions list in RLP form.",
+ Value: "txs.rlp",
+ }
+ SealCliqueFlag = cli.StringFlag{
+ Name: "seal.clique",
+ Usage: "Seal block with Clique. `stdin` or file name of where to find the Clique sealing data.",
+ }
+ SealEthashFlag = cli.BoolFlag{
+ Name: "seal.ethash",
+ Usage: "Seal block with ethash.",
+ }
+ SealEthashDirFlag = cli.StringFlag{
+ Name: "seal.ethash.dir",
+ Usage: "Path to ethash DAG. If none exists, a new DAG will be generated.",
+ }
+ SealEthashModeFlag = cli.StringFlag{
+ Name: "seal.ethash.mode",
+ Usage: "Defines the type and amount of PoW verification an ethash engine makes.",
+ Value: "normal",
+ }
RewardFlag = cli.Int64Flag{
Name: "state.reward",
Usage: "Mining reward. Set to -1 to disable",
diff --git a/cmd/evm/internal/t8ntool/gen_header.go b/cmd/evm/internal/t8ntool/gen_header.go
new file mode 100644
index 000000000..196e49dd7
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/gen_header.go
@@ -0,0 +1,135 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package t8ntool
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+var _ = (*headerMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (h header) MarshalJSON() ([]byte, error) {
+ type header struct {
+ ParentHash common.Hash `json:"parentHash"`
+ OmmerHash *common.Hash `json:"sha3Uncles"`
+ Coinbase *common.Address `json:"miner"`
+ Root common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash *common.Hash `json:"transactionsRoot"`
+ ReceiptHash *common.Hash `json:"receiptsRoot"`
+ Bloom types.Bloom `json:"logsBloom"`
+ Difficulty *math.HexOrDecimal256 `json:"difficulty"`
+ Number *math.HexOrDecimal256 `json:"number" gencodec:"required"`
+ GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
+ GasUsed math.HexOrDecimal64 `json:"gasUsed"`
+ Time math.HexOrDecimal64 `json:"timestamp" gencodec:"required"`
+ Extra hexutil.Bytes `json:"extraData"`
+ MixDigest common.Hash `json:"mixHash"`
+ Nonce *types.BlockNonce `json:"nonce"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
+ }
+ var enc header
+ enc.ParentHash = h.ParentHash
+ enc.OmmerHash = h.OmmerHash
+ enc.Coinbase = h.Coinbase
+ enc.Root = h.Root
+ enc.TxHash = h.TxHash
+ enc.ReceiptHash = h.ReceiptHash
+ enc.Bloom = h.Bloom
+ enc.Difficulty = (*math.HexOrDecimal256)(h.Difficulty)
+ enc.Number = (*math.HexOrDecimal256)(h.Number)
+ enc.GasLimit = math.HexOrDecimal64(h.GasLimit)
+ enc.GasUsed = math.HexOrDecimal64(h.GasUsed)
+ enc.Time = math.HexOrDecimal64(h.Time)
+ enc.Extra = h.Extra
+ enc.MixDigest = h.MixDigest
+ enc.Nonce = h.Nonce
+ enc.BaseFee = (*math.HexOrDecimal256)(h.BaseFee)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (h *header) UnmarshalJSON(input []byte) error {
+ type header struct {
+ ParentHash *common.Hash `json:"parentHash"`
+ OmmerHash *common.Hash `json:"sha3Uncles"`
+ Coinbase *common.Address `json:"miner"`
+ Root *common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash *common.Hash `json:"transactionsRoot"`
+ ReceiptHash *common.Hash `json:"receiptsRoot"`
+ Bloom *types.Bloom `json:"logsBloom"`
+ Difficulty *math.HexOrDecimal256 `json:"difficulty"`
+ Number *math.HexOrDecimal256 `json:"number" gencodec:"required"`
+ GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
+ GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
+ Time *math.HexOrDecimal64 `json:"timestamp" gencodec:"required"`
+ Extra *hexutil.Bytes `json:"extraData"`
+ MixDigest *common.Hash `json:"mixHash"`
+ Nonce *types.BlockNonce `json:"nonce"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
+ }
+ var dec header
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.ParentHash != nil {
+ h.ParentHash = *dec.ParentHash
+ }
+ if dec.OmmerHash != nil {
+ h.OmmerHash = dec.OmmerHash
+ }
+ if dec.Coinbase != nil {
+ h.Coinbase = dec.Coinbase
+ }
+ if dec.Root == nil {
+ return errors.New("missing required field 'stateRoot' for header")
+ }
+ h.Root = *dec.Root
+ if dec.TxHash != nil {
+ h.TxHash = dec.TxHash
+ }
+ if dec.ReceiptHash != nil {
+ h.ReceiptHash = dec.ReceiptHash
+ }
+ if dec.Bloom != nil {
+ h.Bloom = *dec.Bloom
+ }
+ if dec.Difficulty != nil {
+ h.Difficulty = (*big.Int)(dec.Difficulty)
+ }
+ if dec.Number == nil {
+ return errors.New("missing required field 'number' for header")
+ }
+ h.Number = (*big.Int)(dec.Number)
+ if dec.GasLimit == nil {
+ return errors.New("missing required field 'gasLimit' for header")
+ }
+ h.GasLimit = uint64(*dec.GasLimit)
+ if dec.GasUsed != nil {
+ h.GasUsed = uint64(*dec.GasUsed)
+ }
+ if dec.Time == nil {
+ return errors.New("missing required field 'timestamp' for header")
+ }
+ h.Time = uint64(*dec.Time)
+ if dec.Extra != nil {
+ h.Extra = *dec.Extra
+ }
+ if dec.MixDigest != nil {
+ h.MixDigest = *dec.MixDigest
+ }
+ if dec.Nonce != nil {
+ h.Nonce = dec.Nonce
+ }
+ if dec.BaseFee != nil {
+ h.BaseFee = (*big.Int)(dec.BaseFee)
+ }
+ return nil
+}
diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go
index bc9bc42ed..6f1c964ad 100644
--- a/cmd/evm/internal/t8ntool/transaction.go
+++ b/cmd/evm/internal/t8ntool/transaction.go
@@ -48,7 +48,7 @@ func (r *result) MarshalJSON() ([]byte, error) {
Error string `json:"error,omitempty"`
Address *common.Address `json:"address,omitempty"`
Hash *common.Hash `json:"hash,omitempty"`
- IntrinsicGas uint64 `json:"intrinsicGas,omitempty"`
+ IntrinsicGas hexutil.Uint64 `json:"intrinsicGas,omitempty"`
}
var out xx
if r.Error != nil {
@@ -60,7 +60,7 @@ func (r *result) MarshalJSON() ([]byte, error) {
if r.Hash != (common.Hash{}) {
out.Hash = &r.Hash
}
- out.IntrinsicGas = r.IntrinsicGas
+ out.IntrinsicGas = hexutil.Uint64(r.IntrinsicGas)
return json.Marshal(out)
}
@@ -82,7 +82,7 @@ func Transaction(ctx *cli.Context) error {
)
// Construct the chainconfig
if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
- return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
+ return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
} else {
chainConfig = cConf
}
@@ -154,6 +154,8 @@ func Transaction(ctx *cli.Context) error {
}
// Validate <256bit fields
switch {
+ case tx.Nonce()+1 < tx.Nonce():
+ r.Error = errors.New("nonce exceeds 2^64-1")
case tx.Value().BitLen() > 256:
r.Error = errors.New("value exceeds 256 bits")
case tx.GasPrice().BitLen() > 256:
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index 0aff715eb..edb439425 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -43,11 +43,12 @@ import (
const (
ErrorEVM = 2
- ErrorVMConfig = 3
+ ErrorConfig = 3
ErrorMissingBlockhash = 4
ErrorJson = 10
ErrorIO = 11
+ ErrorRlp = 12
stdinSelector = "stdin"
)
@@ -88,28 +89,33 @@ func Transition(ctx *cli.Context) error {
log.Root().SetHandler(glogger)
var (
- err error
- tracer vm.EVMLogger
- baseDir = ""
+ err error
+ tracer vm.EVMLogger
)
var getTracer func(txIndex int, txHash common.Hash) (vm.EVMLogger, error)
- // If user specified a basedir, make sure it exists
- if ctx.IsSet(OutputBasedir.Name) {
- if base := ctx.String(OutputBasedir.Name); len(base) > 0 {
- err := os.MkdirAll(base, 0755) // //rw-r--r--
- if err != nil {
- return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
- }
- baseDir = base
- }
+ baseDir, err := createBasedir(ctx)
+ if err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
}
if ctx.Bool(TraceFlag.Name) {
+ if ctx.IsSet(TraceDisableMemoryFlag.Name) && ctx.IsSet(TraceEnableMemoryFlag.Name) {
+ return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name))
+ }
+ if ctx.IsSet(TraceDisableReturnDataFlag.Name) && ctx.IsSet(TraceEnableReturnDataFlag.Name) {
+ return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name))
+ }
+ if ctx.IsSet(TraceDisableMemoryFlag.Name) {
+ log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name))
+ }
+ if ctx.IsSet(TraceDisableReturnDataFlag.Name) {
+ log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name))
+ }
// Configure the EVM logger
logConfig := &vm.LogConfig{
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
- EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name),
- EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name),
+ EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name) || ctx.Bool(TraceEnableMemoryFlag.Name),
+ EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name) || ctx.Bool(TraceEnableReturnDataFlag.Name),
Debug: true,
}
var prevFile *os.File
@@ -155,29 +161,17 @@ func Transition(ctx *cli.Context) error {
}
}
if allocStr != stdinSelector {
- inFile, err := os.Open(allocStr)
- if err != nil {
- return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
- }
- defer inFile.Close()
- decoder := json.NewDecoder(inFile)
- if err := decoder.Decode(&inputData.Alloc); err != nil {
- return NewError(ErrorJson, fmt.Errorf("failed unmarshaling alloc-file: %v", err))
+ if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
+ return err
}
}
prestate.Pre = inputData.Alloc
// Set the block environment
if envStr != stdinSelector {
- inFile, err := os.Open(envStr)
- if err != nil {
- return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err))
- }
- defer inFile.Close()
- decoder := json.NewDecoder(inFile)
var env stEnv
- if err := decoder.Decode(&env); err != nil {
- return NewError(ErrorJson, fmt.Errorf("failed unmarshaling env-file: %v", err))
+ if err := readFile(envStr, "env", &env); err != nil {
+ return err
}
inputData.Env = &env
}
@@ -190,7 +184,7 @@ func Transition(ctx *cli.Context) error {
// Construct the chainconfig
var chainConfig *params.ChainConfig
if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
- return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
+ return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
} else {
chainConfig = cConf
vmConfig.ExtraEips = extraEips
@@ -254,18 +248,18 @@ func Transition(ctx *cli.Context) error {
// Sanity check, to not `panic` in state_transition
if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) {
if prestate.Env.BaseFee == nil {
- return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
+ return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
}
}
if env := prestate.Env; env.Difficulty == nil {
// If difficulty was not provided by caller, we need to calculate it.
switch {
case env.ParentDifficulty == nil:
- return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
+ return NewError(ErrorConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
case env.Number == 0:
- return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
+ return NewError(ErrorConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
case env.Timestamp <= env.ParentTimestamp:
- return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
+ return NewError(ErrorConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
env.Timestamp, env.ParentTimestamp))
}
prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
@@ -286,27 +280,34 @@ func Transition(ctx *cli.Context) error {
// txWithKey is a helper-struct, to allow us to use the types.Transaction along with
// a `secretKey`-field, for input
type txWithKey struct {
- key *ecdsa.PrivateKey
- tx *types.Transaction
+ key *ecdsa.PrivateKey
+ tx *types.Transaction
+ protected bool
}
func (t *txWithKey) UnmarshalJSON(input []byte) error {
- // Read the secretKey, if present
- type sKey struct {
- Key *common.Hash `json:"secretKey"`
+ // Read the metadata, if present
+ type txMetadata struct {
+ Key *common.Hash `json:"secretKey"`
+ Protected *bool `json:"protected"`
}
- var key sKey
- if err := json.Unmarshal(input, &key); err != nil {
+ var data txMetadata
+ if err := json.Unmarshal(input, &data); err != nil {
return err
}
- if key.Key != nil {
- k := key.Key.Hex()[2:]
+ if data.Key != nil {
+ k := data.Key.Hex()[2:]
if ecdsaKey, err := crypto.HexToECDSA(k); err != nil {
return err
} else {
t.key = ecdsaKey
}
}
+ if data.Protected != nil {
+ t.protected = *data.Protected
+ } else {
+ t.protected = true
+ }
// Now, read the transaction itself
var tx types.Transaction
if err := json.Unmarshal(input, &tx); err != nil {
@@ -335,7 +336,15 @@ func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Tran
v, r, s := tx.RawSignatureValues()
if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 {
// This transaction needs to be signed
- signed, err := types.SignTx(tx, signer, key)
+ var (
+ signed *types.Transaction
+ err error
+ )
+ if txWithKey.protected {
+ signed, err = types.SignTx(tx, signer, key)
+ } else {
+ signed, err = types.SignTx(tx, types.FrontierSigner{}, key)
+ }
if err != nil {
return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err))
}
diff --git a/cmd/evm/internal/t8ntool/utils.go b/cmd/evm/internal/t8ntool/utils.go
new file mode 100644
index 000000000..1c54f09bf
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/utils.go
@@ -0,0 +1,54 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package t8ntool
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+
+ "gopkg.in/urfave/cli.v1"
+)
+
+// readFile reads the json-data in the provided path and marshals into dest.
+func readFile(path, desc string, dest interface{}) error {
+ inFile, err := os.Open(path)
+ if err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed reading %s file: %v", desc, err))
+ }
+ defer inFile.Close()
+ decoder := json.NewDecoder(inFile)
+ if err := decoder.Decode(dest); err != nil {
+ return NewError(ErrorJson, fmt.Errorf("failed unmarshaling %s file: %v", desc, err))
+ }
+ return nil
+}
+
+// createBasedir makes sure the basedir exists, if user specified one.
+func createBasedir(ctx *cli.Context) (string, error) {
+ baseDir := ""
+ if ctx.IsSet(OutputBasedir.Name) {
+ if base := ctx.String(OutputBasedir.Name); len(base) > 0 {
+ err := os.MkdirAll(base, 0755) // //rw-r--r--
+ if err != nil {
+ return "", err
+ }
+ baseDir = base
+ }
+ }
+ return baseDir, nil
+}
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 26064efc3..2f404d48e 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -139,8 +139,10 @@ var stateTransitionCommand = cli.Command{
Flags: []cli.Flag{
t8ntool.TraceFlag,
t8ntool.TraceDisableMemoryFlag,
+ t8ntool.TraceEnableMemoryFlag,
t8ntool.TraceDisableStackFlag,
t8ntool.TraceDisableReturnDataFlag,
+ t8ntool.TraceEnableReturnDataFlag,
t8ntool.OutputBasedir,
t8ntool.OutputAllocFlag,
t8ntool.OutputResultFlag,
@@ -167,6 +169,25 @@ var transactionCommand = cli.Command{
},
}
+var blockBuilderCommand = cli.Command{
+ Name: "block-builder",
+ Aliases: []string{"b11r"},
+ Usage: "builds a block",
+ Action: t8ntool.BuildBlock,
+ Flags: []cli.Flag{
+ t8ntool.OutputBasedir,
+ t8ntool.OutputBlockFlag,
+ t8ntool.InputHeaderFlag,
+ t8ntool.InputOmmersFlag,
+ t8ntool.InputTxsRlpFlag,
+ t8ntool.SealCliqueFlag,
+ t8ntool.SealEthashFlag,
+ t8ntool.SealEthashDirFlag,
+ t8ntool.SealEthashModeFlag,
+ t8ntool.VerbosityFlag,
+ },
+}
+
func init() {
app.Flags = []cli.Flag{
BenchFlag,
@@ -200,6 +221,7 @@ func init() {
stateTestCommand,
stateTransitionCommand,
transactionCommand,
+ blockBuilderCommand,
}
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
}
diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go
index b4b816f57..3f0bd3185 100644
--- a/cmd/evm/t8n_test.go
+++ b/cmd/evm/t8n_test.go
@@ -131,7 +131,7 @@ func TestT8n(t *testing.T) {
output: t8nOutput{alloc: true, result: true},
expExitCode: 4,
},
- { // Ommer test
+ { // Uncle test
base: "./testdata/5",
input: t8nInput{
"alloc.json", "txs.json", "env.json", "Byzantium", "0x80",
@@ -171,7 +171,7 @@ func TestT8n(t *testing.T) {
output: t8nOutput{result: true},
expOut: "exp2.json",
},
- { // Difficulty calculation - with uncles + Berlin
+ { // Difficulty calculation - with ommers + Berlin
base: "./testdata/14",
input: t8nInput{
"alloc.json", "txs.json", "env.uncles.json", "Berlin", "",
@@ -195,6 +195,14 @@ func TestT8n(t *testing.T) {
output: t8nOutput{result: true},
expOut: "exp_arrowglacier.json",
},
+ { // Sign unprotected (pre-EIP155) transaction
+ base: "./testdata/23",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "Berlin", "",
+ },
+ output: t8nOutput{result: true},
+ expOut: "exp.json",
+ },
} {
args := []string{"t8n"}
@@ -336,6 +344,126 @@ func TestT9n(t *testing.T) {
}
}
+type b11rInput struct {
+ inEnv string
+ inOmmersRlp string
+ inTxsRlp string
+ inClique string
+ ethash bool
+ ethashMode string
+ ethashDir string
+}
+
+func (args *b11rInput) get(base string) []string {
+ var out []string
+ if opt := args.inEnv; opt != "" {
+ out = append(out, "--input.header")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.inOmmersRlp; opt != "" {
+ out = append(out, "--input.ommers")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.inTxsRlp; opt != "" {
+ out = append(out, "--input.txs")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.inClique; opt != "" {
+ out = append(out, "--seal.clique")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if args.ethash {
+ out = append(out, "--seal.ethash")
+ }
+ if opt := args.ethashMode; opt != "" {
+ out = append(out, "--seal.ethash.mode")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.ethashDir; opt != "" {
+ out = append(out, "--seal.ethash.dir")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ out = append(out, "--output.block")
+ out = append(out, "stdout")
+ return out
+}
+
+func TestB11r(t *testing.T) {
+ tt := new(testT8n)
+ tt.TestCmd = cmdtest.NewTestCmd(t, tt)
+ for i, tc := range []struct {
+ base string
+ input b11rInput
+ expExitCode int
+ expOut string
+ }{
+ { // unsealed block
+ base: "./testdata/20",
+ input: b11rInput{
+ inEnv: "header.json",
+ inOmmersRlp: "ommers.json",
+ inTxsRlp: "txs.rlp",
+ },
+ expOut: "exp.json",
+ },
+ { // ethash test seal
+ base: "./testdata/21",
+ input: b11rInput{
+ inEnv: "header.json",
+ inOmmersRlp: "ommers.json",
+ inTxsRlp: "txs.rlp",
+ },
+ expOut: "exp.json",
+ },
+ { // clique test seal
+ base: "./testdata/21",
+ input: b11rInput{
+ inEnv: "header.json",
+ inOmmersRlp: "ommers.json",
+ inTxsRlp: "txs.rlp",
+ inClique: "clique.json",
+ },
+ expOut: "exp-clique.json",
+ },
+ { // block with ommers
+ base: "./testdata/22",
+ input: b11rInput{
+ inEnv: "header.json",
+ inOmmersRlp: "ommers.json",
+ inTxsRlp: "txs.rlp",
+ },
+ expOut: "exp.json",
+ },
+ } {
+
+ args := []string{"b11r"}
+ args = append(args, tc.input.get(tc.base)...)
+
+ tt.Run("evm-test", args...)
+ tt.Logf("args:\n go run . %v\n", strings.Join(args, " "))
+ // Compare the expected output, if provided
+ if tc.expOut != "" {
+ want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
+ if err != nil {
+ t.Fatalf("test %d: could not read expected output: %v", i, err)
+ }
+ have := tt.Output()
+ ok, err := cmpJson(have, want)
+ switch {
+ case err != nil:
+ t.Logf(string(have))
+ t.Fatalf("test %d, json parsing failed: %v", i, err)
+ case !ok:
+ t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
+ }
+ }
+ tt.WaitExit()
+ if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
+ t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
+ }
+ }
+}
+
// cmpJson compares the JSON in two byte slices.
func cmpJson(a, b []byte) (bool, error) {
var j, j2 interface{}
diff --git a/cmd/evm/testdata/1/exp.json b/cmd/evm/testdata/1/exp.json
index 17d2f8267..7d3805012 100644
--- a/cmd/evm/testdata/1/exp.json
+++ b/cmd/evm/testdata/1/exp.json
@@ -15,7 +15,7 @@
"result": {
"stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13",
"txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d",
- "receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
+ "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [
@@ -38,6 +38,7 @@
"error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
}
],
- "currentDifficulty": "0x20000"
+ "currentDifficulty": "0x20000",
+ "gasUsed": "0x5208"
}
-}
\ No newline at end of file
+}
diff --git a/cmd/evm/testdata/13/exp2.json b/cmd/evm/testdata/13/exp2.json
index 01ab59e84..ba8c9f865 100644
--- a/cmd/evm/testdata/13/exp2.json
+++ b/cmd/evm/testdata/13/exp2.json
@@ -2,7 +2,7 @@
"result": {
"stateRoot": "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61",
"txRoot": "0x013509c8563d41c0ae4bf38f2d6d19fc6512a1d0d6be045079c8c9f68bf45f9d",
- "receiptRoot": "0xa532a08aa9f62431d6fe5d924951b8efb86ed3c54d06fee77788c3767dd13420",
+ "receiptsRoot": "0xa532a08aa9f62431d6fe5d924951b8efb86ed3c54d06fee77788c3767dd13420",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [
@@ -33,6 +33,7 @@
"transactionIndex": "0x1"
}
],
- "currentDifficulty": "0x20000"
+ "currentDifficulty": "0x20000",
+ "gasUsed": "0x109a0"
}
}
diff --git a/cmd/evm/testdata/14/exp.json b/cmd/evm/testdata/14/exp.json
index bbe6a1317..9bf5635f5 100644
--- a/cmd/evm/testdata/14/exp.json
+++ b/cmd/evm/testdata/14/exp.json
@@ -2,10 +2,11 @@
"result": {
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"currentDifficulty": "0x2000020000000",
- "receipts": []
+ "receipts": [],
+ "gasUsed": "0x0"
}
-}
\ No newline at end of file
+}
diff --git a/cmd/evm/testdata/14/exp2.json b/cmd/evm/testdata/14/exp2.json
index 195c738d9..9c9025381 100644
--- a/cmd/evm/testdata/14/exp2.json
+++ b/cmd/evm/testdata/14/exp2.json
@@ -2,10 +2,11 @@
"result": {
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [],
- "currentDifficulty": "0x1ff8020000000"
+ "currentDifficulty": "0x1ff8020000000",
+ "gasUsed": "0x0"
}
-}
\ No newline at end of file
+}
diff --git a/cmd/evm/testdata/14/exp_berlin.json b/cmd/evm/testdata/14/exp_berlin.json
index e56478831..c2bf95311 100644
--- a/cmd/evm/testdata/14/exp_berlin.json
+++ b/cmd/evm/testdata/14/exp_berlin.json
@@ -2,10 +2,11 @@
"result": {
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [],
- "currentDifficulty": "0x1ff9000000000"
+ "currentDifficulty": "0x1ff9000000000",
+ "gasUsed": "0x0"
}
-}
\ No newline at end of file
+}
diff --git a/cmd/evm/testdata/15/exp2.json b/cmd/evm/testdata/15/exp2.json
index 2c49326ce..dd5e8a358 100644
--- a/cmd/evm/testdata/15/exp2.json
+++ b/cmd/evm/testdata/15/exp2.json
@@ -2,11 +2,11 @@
{
"address": "0xd02d72e067e77158444ef2020ff2d325f929b363",
"hash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
- "intrinsicGas": 21000
+ "intrinsicGas": "0x5208"
},
{
"address": "0xd02d72e067e77158444ef2020ff2d325f929b363",
"hash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
- "intrinsicGas": 21000
+ "intrinsicGas": "0x5208"
}
]
diff --git a/cmd/evm/testdata/16/exp.json b/cmd/evm/testdata/16/exp.json
index 075c977f2..137ade651 100644
--- a/cmd/evm/testdata/16/exp.json
+++ b/cmd/evm/testdata/16/exp.json
@@ -2,12 +2,12 @@
{
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"hash": "0x7cc3d1a8540a44736750f03bb4d85c0113be4b3472a71bf82241a3b261b479e6",
- "intrinsicGas": 21000
+ "intrinsicGas": "0x5208"
},
{
"error": "intrinsic gas too low: have 82, want 21000",
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"hash": "0x3b2d2609e4361562edb9169314f4c05afc6dbf5d706bf9dda5abe242ab76a22b",
- "intrinsicGas": 21000
+ "intrinsicGas": "0x5208"
}
]
\ No newline at end of file
diff --git a/cmd/evm/testdata/17/exp.json b/cmd/evm/testdata/17/exp.json
index 1c6c54723..485906041 100644
--- a/cmd/evm/testdata/17/exp.json
+++ b/cmd/evm/testdata/17/exp.json
@@ -3,13 +3,13 @@
"error": "value exceeds 256 bits",
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"hash": "0xfbd91685dcbf8172f0e8c53e2ddbb4d26707840da6b51a74371f62a33868fd82",
- "intrinsicGas": 21000
+ "intrinsicGas": "0x5208"
},
{
"error": "gasPrice exceeds 256 bits",
"address": "0x1b57ccef1fe5fb73f1e64530fb4ebd9cf1655964",
"hash": "0x45dc05035cada83748e4c1fe617220106b331eca054f44c2304d5654a9fb29d5",
- "intrinsicGas": 21000
+ "intrinsicGas": "0x5208"
},
{
"error": "invalid transaction v, r, s values",
diff --git a/cmd/evm/testdata/19/exp_arrowglacier.json b/cmd/evm/testdata/19/exp_arrowglacier.json
index 4c5f8e0fb..9cf56ffaf 100644
--- a/cmd/evm/testdata/19/exp_arrowglacier.json
+++ b/cmd/evm/testdata/19/exp_arrowglacier.json
@@ -2,10 +2,11 @@
"result": {
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"currentDifficulty": "0x2000000200000",
- "receipts": []
+ "receipts": [],
+ "gasUsed": "0x0"
}
-}
\ No newline at end of file
+}
diff --git a/cmd/evm/testdata/19/exp_london.json b/cmd/evm/testdata/19/exp_london.json
index 9dc1b9d4f..a06bc8ca6 100644
--- a/cmd/evm/testdata/19/exp_london.json
+++ b/cmd/evm/testdata/19/exp_london.json
@@ -2,10 +2,11 @@
"result": {
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"currentDifficulty": "0x2000080000000",
- "receipts": []
+ "receipts": [],
+ "gasUsed": "0x0"
}
-}
\ No newline at end of file
+}
diff --git a/cmd/evm/testdata/20/exp.json b/cmd/evm/testdata/20/exp.json
new file mode 100644
index 000000000..7bec6cefd
--- /dev/null
+++ b/cmd/evm/testdata/20/exp.json
@@ -0,0 +1,4 @@
+{
+ "rlp": "0xf902d9f90211a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e997a23b159e2e2a5ce72333262972374b15425ca0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e99476574682f76312e302e312f6c696e75782f676f312e342e32a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf8897435673d874f7c8f8c2f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600c0",
+ "hash": "0xaba9a3b6a4e96e9ecffcadaa5a2ae0589359455617535cd86589fe1dd26fe899"
+}
diff --git a/cmd/evm/testdata/20/header.json b/cmd/evm/testdata/20/header.json
new file mode 100644
index 000000000..fb9b7fc56
--- /dev/null
+++ b/cmd/evm/testdata/20/header.json
@@ -0,0 +1,14 @@
+{
+ "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e",
+ "miner": "0xe997a23b159e2e2a5ce72333262972374b15425c",
+ "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "difficulty": "0x1000",
+ "number": "0xc3be",
+ "gasLimit": "0x50785",
+ "gasUsed": "0x0",
+ "timestamp": "0x55c5277e",
+ "extraData": "0x476574682f76312e302e312f6c696e75782f676f312e342e32",
+ "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf",
+ "nonce": "0x97435673d874f7c8"
+}
diff --git a/cmd/evm/testdata/20/ommers.json b/cmd/evm/testdata/20/ommers.json
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/cmd/evm/testdata/20/ommers.json
@@ -0,0 +1 @@
+[]
diff --git a/cmd/evm/testdata/20/readme.md b/cmd/evm/testdata/20/readme.md
new file mode 100644
index 000000000..2c448a96e
--- /dev/null
+++ b/cmd/evm/testdata/20/readme.md
@@ -0,0 +1,11 @@
+# Block building
+
+This test shows how `b11r` can be used to assemble an unsealed block.
+
+```console
+$ go run . b11r --input.header=testdata/20/header.json --input.txs=testdata/20/txs.rlp --input.ommers=testdata/20/ommers.json --output.block=stdout
+{
+ "rlp": "0xf90216f90211a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e997a23b159e2e2a5ce72333262972374b15425ca0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e99476574682f76312e302e312f6c696e75782f676f312e342e32a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf8897435673d874f7c8c0c0",
+ "hash": "0xaba9a3b6a4e96e9ecffcadaa5a2ae0589359455617535cd86589fe1dd26fe899"
+}
+```
diff --git a/cmd/evm/testdata/20/txs.rlp b/cmd/evm/testdata/20/txs.rlp
new file mode 100644
index 000000000..3599ff065
--- /dev/null
+++ b/cmd/evm/testdata/20/txs.rlp
@@ -0,0 +1 @@
+"0xf8c2f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600"
\ No newline at end of file
diff --git a/cmd/evm/testdata/21/clique.json b/cmd/evm/testdata/21/clique.json
new file mode 100644
index 000000000..84fa259a0
--- /dev/null
+++ b/cmd/evm/testdata/21/clique.json
@@ -0,0 +1,6 @@
+{
+ "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
+ "voted": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "authorize": false,
+ "vanity": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+}
diff --git a/cmd/evm/testdata/21/exp-clique.json b/cmd/evm/testdata/21/exp-clique.json
new file mode 100644
index 000000000..c990ba8aa
--- /dev/null
+++ b/cmd/evm/testdata/21/exp-clique.json
@@ -0,0 +1,4 @@
+{
+ "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0",
+ "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7"
+}
diff --git a/cmd/evm/testdata/21/exp.json b/cmd/evm/testdata/21/exp.json
new file mode 100644
index 000000000..b3e5e7a83
--- /dev/null
+++ b/cmd/evm/testdata/21/exp.json
@@ -0,0 +1,4 @@
+{
+ "rlp": "0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0",
+ "hash": "0x801411e9f6609a659825690d13e4f75a3cfe9143952fa2d9573f3b0a5eb9ebbb"
+}
diff --git a/cmd/evm/testdata/21/header.json b/cmd/evm/testdata/21/header.json
new file mode 100644
index 000000000..62abe3cc2
--- /dev/null
+++ b/cmd/evm/testdata/21/header.json
@@ -0,0 +1,11 @@
+{
+ "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e",
+ "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "difficulty": "0x1000",
+ "number": "0xc3be",
+ "gasLimit": "0x50785",
+ "gasUsed": "0x0",
+ "timestamp": "0x55c5277e",
+ "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf"
+}
diff --git a/cmd/evm/testdata/21/ommers.json b/cmd/evm/testdata/21/ommers.json
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/cmd/evm/testdata/21/ommers.json
@@ -0,0 +1 @@
+[]
diff --git a/cmd/evm/testdata/21/readme.md b/cmd/evm/testdata/21/readme.md
new file mode 100644
index 000000000..b70f106ff
--- /dev/null
+++ b/cmd/evm/testdata/21/readme.md
@@ -0,0 +1,23 @@
+# Sealed block building
+
+This test shows how `b11r` can be used to assemble a sealed block.
+
+## Ethash
+
+```console
+$ go run . b11r --input.header=testdata/21/header.json --input.txs=testdata/21/txs.rlp --input.ommers=testdata/21/ommers.json --seal.ethash --seal.ethash.mode=test --output.block=stdout
+{
+ "rlp": "0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0",
+ "hash": "0x801411e9f6609a659825690d13e4f75a3cfe9143952fa2d9573f3b0a5eb9ebbb"
+}
+```
+
+## Clique
+
+```console
+$ go run . b11r --input.header=testdata/21/header.json --input.txs=testdata/21/txs.rlp --input.ommers=testdata/21/ommers.json --seal.clique=testdata/21/clique.json --output.block=stdout
+{
+ "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0",
+ "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7"
+}
+```
diff --git a/cmd/evm/testdata/21/txs.rlp b/cmd/evm/testdata/21/txs.rlp
new file mode 100644
index 000000000..e815397b3
--- /dev/null
+++ b/cmd/evm/testdata/21/txs.rlp
@@ -0,0 +1 @@
+"c0"
diff --git a/cmd/evm/testdata/22/exp-clique.json b/cmd/evm/testdata/22/exp-clique.json
new file mode 100644
index 000000000..c990ba8aa
--- /dev/null
+++ b/cmd/evm/testdata/22/exp-clique.json
@@ -0,0 +1,4 @@
+{
+ "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0",
+ "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7"
+}
diff --git a/cmd/evm/testdata/22/exp.json b/cmd/evm/testdata/22/exp.json
new file mode 100644
index 000000000..14fd81997
--- /dev/null
+++ b/cmd/evm/testdata/22/exp.json
@@ -0,0 +1,4 @@
+{
+ "rlp": "0xf905f5f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea06eb9f0c3cd68c9e97134e6725d12b1f1d8f0644458da6870a37ff84c908fb1e7940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0f903f6f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000",
+ "hash": "0xd9a81c8fcd57a7f2a0d2c375eff6ad192c30c3729a271303f0a9a7e1b357e755"
+}
diff --git a/cmd/evm/testdata/22/header.json b/cmd/evm/testdata/22/header.json
new file mode 100644
index 000000000..62abe3cc2
--- /dev/null
+++ b/cmd/evm/testdata/22/header.json
@@ -0,0 +1,11 @@
+{
+ "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e",
+ "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "difficulty": "0x1000",
+ "number": "0xc3be",
+ "gasLimit": "0x50785",
+ "gasUsed": "0x0",
+ "timestamp": "0x55c5277e",
+ "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf"
+}
diff --git a/cmd/evm/testdata/22/ommers.json b/cmd/evm/testdata/22/ommers.json
new file mode 100644
index 000000000..997015b3c
--- /dev/null
+++ b/cmd/evm/testdata/22/ommers.json
@@ -0,0 +1 @@
+["0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0","0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0"]
diff --git a/cmd/evm/testdata/22/readme.md b/cmd/evm/testdata/22/readme.md
new file mode 100644
index 000000000..2cac8a243
--- /dev/null
+++ b/cmd/evm/testdata/22/readme.md
@@ -0,0 +1,11 @@
+# Building blocks with ommers
+
+This test shows how `b11r` can chain together ommer assembles into a canonical block.
+
+```console
+$ echo "{ \"ommers\": [`go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --output.block=stdout | jq '.[\"rlp\"]'`,`go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --output.block=stdout | jq '.[\"rlp\"]'`]}" | go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --input.ommers=stdin --output.block=stdout
+{
+ "rlp": "0xf905f5f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea06eb9f0c3cd68c9e97134e6725d12b1f1d8f0644458da6870a37ff84c908fb1e7940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0f903f6f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000",
+ "hash": "0xd9a81c8fcd57a7f2a0d2c375eff6ad192c30c3729a271303f0a9a7e1b357e755"
+}
+```
diff --git a/cmd/evm/testdata/22/txs.rlp b/cmd/evm/testdata/22/txs.rlp
new file mode 100644
index 000000000..e815397b3
--- /dev/null
+++ b/cmd/evm/testdata/22/txs.rlp
@@ -0,0 +1 @@
+"c0"
diff --git a/cmd/evm/testdata/23/alloc.json b/cmd/evm/testdata/23/alloc.json
new file mode 100644
index 000000000..239b3553f
--- /dev/null
+++ b/cmd/evm/testdata/23/alloc.json
@@ -0,0 +1,16 @@
+{
+ "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x6001",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+}
diff --git a/cmd/evm/testdata/23/env.json b/cmd/evm/testdata/23/env.json
new file mode 100644
index 000000000..1b4632151
--- /dev/null
+++ b/cmd/evm/testdata/23/env.json
@@ -0,0 +1,7 @@
+{
+ "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "currentDifficulty" : "0x020000",
+ "currentGasLimit" : "0x3b9aca00",
+ "currentNumber" : "0x05",
+ "currentTimestamp" : "0x03e8"
+}
diff --git a/cmd/evm/testdata/23/exp.json b/cmd/evm/testdata/23/exp.json
new file mode 100644
index 000000000..e51f37d9c
--- /dev/null
+++ b/cmd/evm/testdata/23/exp.json
@@ -0,0 +1,25 @@
+{
+ "result": {
+ "stateRoot": "0x65334305e4accfa18352deb24f007b837b5036425b0712cf0e65a43bfa95154d",
+ "txRoot": "0x75e61774a2ff58cbe32653420256c7f44bc715715a423b0b746d5c622979af6b",
+ "receiptsRoot": "0xf951f9396af203499cc7d379715a9110323de73967c5700e2f424725446a3c76",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [
+ {
+ "root": "0x",
+ "status": "0x1",
+ "cumulativeGasUsed": "0x520b",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logs": null,
+ "transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
+ "contractAddress": "0x0000000000000000000000000000000000000000",
+ "gasUsed": "0x520b",
+ "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionIndex": "0x0"
+ }
+ ],
+ "currentDifficulty": "0x20000",
+ "gasUsed": "0x520b"
+ }
+}
diff --git a/cmd/evm/testdata/23/readme.md b/cmd/evm/testdata/23/readme.md
new file mode 100644
index 000000000..85fe8db66
--- /dev/null
+++ b/cmd/evm/testdata/23/readme.md
@@ -0,0 +1 @@
+These files examplify how to sign a transaction using the pre-EIP155 scheme.
diff --git a/cmd/evm/testdata/23/txs.json b/cmd/evm/testdata/23/txs.json
new file mode 100644
index 000000000..22f3840f8
--- /dev/null
+++ b/cmd/evm/testdata/23/txs.json
@@ -0,0 +1,15 @@
+[
+ {
+ "input" : "0x",
+ "gas" : "0x5f5e100",
+ "gasPrice" : "0x1",
+ "nonce" : "0x0",
+ "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "value" : "0x186a0",
+ "v" : "0x0",
+ "r" : "0x0",
+ "s" : "0x0",
+ "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
+ "protected": false
+ }
+]
diff --git a/cmd/evm/testdata/3/exp.json b/cmd/evm/testdata/3/exp.json
index ade09e9ac..71b3d2f55 100644
--- a/cmd/evm/testdata/3/exp.json
+++ b/cmd/evm/testdata/3/exp.json
@@ -15,7 +15,7 @@
"result": {
"stateRoot": "0xb7341da3f9f762a6884eaa186c32942734c146b609efee11c4b0214c44857ea1",
"txRoot": "0x75e61774a2ff58cbe32653420256c7f44bc715715a423b0b746d5c622979af6b",
- "receiptRoot": "0xd0d26df80374a327c025d405ebadc752b1bbd089d864801ae78ab704bcad8086",
+ "receiptsRoot": "0xd0d26df80374a327c025d405ebadc752b1bbd089d864801ae78ab704bcad8086",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [
@@ -32,6 +32,7 @@
"transactionIndex": "0x0"
}
],
- "currentDifficulty": "0x20000"
+ "currentDifficulty": "0x20000",
+ "gasUsed": "0x521f"
}
}
diff --git a/cmd/evm/testdata/5/exp.json b/cmd/evm/testdata/5/exp.json
index 6340d4cc3..7d715672c 100644
--- a/cmd/evm/testdata/5/exp.json
+++ b/cmd/evm/testdata/5/exp.json
@@ -13,10 +13,11 @@
"result": {
"stateRoot": "0xa7312add33811645c6aa65d928a1a4f49d65d448801912c069a0aa8fe9c1f393",
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
- "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [],
- "currentDifficulty": "0x20000"
+ "currentDifficulty": "0x20000",
+ "gasUsed": "0x0"
}
}
diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go
index e1e0d77f0..c2c42276b 100644
--- a/cmd/geth/dbcmd.go
+++ b/cmd/geth/dbcmd.go
@@ -77,6 +77,7 @@ Remove blockchain and state databases`,
ArgsUsage: " ",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.AncientFlag,
utils.SyncModeFlag,
utils.MainnetFlag,
utils.RopstenFlag,
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 48cfec15b..a2d893ac7 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -43,7 +43,8 @@ import (
"github.com/ethereum/go-ethereum/plugins"
"github.com/ethereum/go-ethereum/plugins/wrappers"
- // Force-load the native, to trigger registration
+ // Force-load the tracer engines to trigger registration
+ _ "github.com/ethereum/go-ethereum/eth/tracers/js"
_ "github.com/ethereum/go-ethereum/eth/tracers/native"
"gopkg.in/urfave/cli.v1"
@@ -142,6 +143,7 @@ var (
utils.MainnetFlag,
utils.DeveloperFlag,
utils.DeveloperPeriodFlag,
+ utils.DeveloperGasLimitFlag,
utils.RopstenFlag,
utils.SepoliaFlag,
utils.RinkebyFlag,
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 38f690f17..c63c62fd3 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -75,6 +75,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
Flags: []cli.Flag{
utils.DeveloperFlag,
utils.DeveloperPeriodFlag,
+ utils.DeveloperGasLimitFlag,
},
},
{
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 52554fbe5..25453148c 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -167,6 +167,11 @@ var (
Name: "dev.period",
Usage: "Block period to use in developer mode (0 = mine only if transaction pending)",
}
+ DeveloperGasLimitFlag = cli.Uint64Flag{
+ Name: "dev.gaslimit",
+ Usage: "Initial block gas limit",
+ Value: 11500000,
+ }
IdentityFlag = cli.StringFlag{
Name: "identity",
Usage: "Custom node name",
@@ -1661,7 +1666,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
log.Info("Using developer account", "address", developer.Address)
// Create a new developer genesis block or reuse existing one
- cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
+ cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), ctx.GlobalUint64(DeveloperGasLimitFlag.Name), developer.Address)
if ctx.GlobalIsSet(DataDirFlag.Name) {
// Check if we have an already initialized chain and fall back to
// that if so. Otherwise we need to generate a new genesis spec.
diff --git a/console/console_test.go b/console/console_test.go
index f6ab78141..71c80c20f 100644
--- a/console/console_test.go
+++ b/console/console_test.go
@@ -99,7 +99,7 @@ func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester {
t.Fatalf("failed to create node: %v", err)
}
ethConf := ðconfig.Config{
- Genesis: core.DeveloperGenesisBlock(15, common.Address{}),
+ Genesis: core.DeveloperGenesisBlock(15, 11_500_000, common.Address{}),
Miner: miner.Config{
Etherbase: common.HexToAddress(testAddress),
},
diff --git a/core/blockchain.go b/core/blockchain.go
index 2aed06173..162d244f4 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -296,7 +296,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if diskRoot != (common.Hash{}) {
log.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash(), "snaproot", diskRoot)
- snapDisk, err := bc.SetHeadBeyondRoot(head.NumberU64(), diskRoot)
+ snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), diskRoot, true)
if err != nil {
return nil, err
}
@@ -306,7 +306,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
}
} else {
log.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash())
- if err := bc.SetHead(head.NumberU64()); err != nil {
+ if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true); err != nil {
return nil, err
}
}
@@ -482,11 +482,11 @@ func (bc *BlockChain) loadLastState() error {
// was fast synced or full synced and in which state, the method will try to
// delete minimal data from disk whilst retaining chain consistency.
func (bc *BlockChain) SetHead(head uint64) error {
- _, err := bc.SetHeadBeyondRoot(head, common.Hash{})
+ _, err := bc.setHeadBeyondRoot(head, common.Hash{}, false)
return err
}
-// SetHeadBeyondRoot rewinds the local chain to a new head with the extra condition
+// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
// that the rewind must pass the specified state root. This method is meant to be
// used when rewinding with snapshots enabled to ensure that we go back further than
// persistent disk layer. Depending on whether the node was fast synced or full, and
@@ -494,7 +494,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
// retaining chain consistency.
//
// The method returns the block number where the requested root cap was found.
-func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, error) {
+func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bool) (uint64, error) {
if !bc.chainmu.TryLock() {
return 0, errChainStopped
}
@@ -509,7 +509,7 @@ func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64,
frozen, _ := bc.db.Ancients()
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (uint64, bool) {
- // Rewind the block chain, ensuring we don't end up with a stateless head
+ // Rewind the blockchain, ensuring we don't end up with a stateless head
// block. Note, depth equality is permitted to allow using SetHead as a
// chain reparation mechanism without deleting any data!
if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() <= currentBlock.NumberU64() {
@@ -610,8 +610,8 @@ func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64,
}
// If SetHead was only called as a chain reparation method, try to skip
// touching the header chain altogether, unless the freezer is broken
- if block := bc.CurrentBlock(); block.NumberU64() == head {
- if target, force := updateFn(bc.db, block.Header()); force {
+ if repair {
+ if target, force := updateFn(bc.db, bc.CurrentBlock().Header()); force {
bc.hc.SetHead(target, updateFn, delFn)
}
} else {
diff --git a/core/error.go b/core/error.go
index 594f3a26e..51ebefc13 100644
--- a/core/error.go
+++ b/core/error.go
@@ -51,6 +51,10 @@ var (
// next one expected based on the local chain.
ErrNonceTooHigh = errors.New("nonce too high")
+ // ErrNonceMax is returned if the nonce of a transaction sender account has
+ // maximum allowed value and would become invalid if incremented.
+ ErrNonceMax = errors.New("nonce has max value")
+
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
// by a transaction is higher than what's left in the block.
ErrGasLimitReached = errors.New("gas limit reached")
diff --git a/core/genesis.go b/core/genesis.go
index 37cc96fe6..85d01ec87 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -416,7 +416,7 @@ func DefaultSepoliaGenesisBlock() *Genesis {
}
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
-func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
+func DeveloperGenesisBlock(period uint64, gasLimit uint64, faucet common.Address) *Genesis {
// Override the default period to the user requested one
config := *params.AllCliqueProtocolChanges
config.Clique = ¶ms.CliqueConfig{
@@ -428,7 +428,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
return &Genesis{
Config: &config,
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
- GasLimit: 11500000,
+ GasLimit: gasLimit,
BaseFee: big.NewInt(params.InitialBaseFee),
Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index 7f26c3a42..4028191b7 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -669,7 +669,7 @@ func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, t
// ReadLogs retrieves the logs for all transactions in a block. The log fields
// are populated with metadata. In case the receipts or the block body
// are not found, a nil is returned.
-func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
+func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) [][]*types.Log {
// Retrieve the flattened receipt slice
data := ReadReceiptsRLP(db, hash, number)
if len(data) == 0 {
@@ -677,7 +677,12 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
}
receipts := []*receiptLogs{}
if err := rlp.DecodeBytes(data, &receipts); err != nil {
- log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
+ // Receipts might be in the legacy format, try decoding that.
+ // TODO: to be removed after users migrated
+ if logs := readLegacyLogs(db, hash, number, config); logs != nil {
+ return logs
+ }
+ log.Error("Invalid receipt array RLP", "hash", "err", err)
return nil
}
@@ -697,6 +702,21 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
return logs
}
+// readLegacyLogs is a temporary workaround for when trying to read logs
+// from a block which has its receipt stored in the legacy format. It'll
+// be removed after users have migrated their freezer databases.
+func readLegacyLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) [][]*types.Log {
+ receipts := ReadReceipts(db, hash, number, config)
+ if receipts == nil {
+ return nil
+ }
+ logs := make([][]*types.Log, len(receipts))
+ for i, receipt := range receipts {
+ logs[i] = receipt.Logs
+ }
+ return logs
+}
+
// ReadBlock retrieves an entire block corresponding to the hash, assembling it
// back from the stored header and body. If either the header or body could not
// be retrieved nil is returned.
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
index 4b173c55e..50b0d5390 100644
--- a/core/rawdb/accessors_chain_test.go
+++ b/core/rawdb/accessors_chain_test.go
@@ -744,7 +744,7 @@ func TestReadLogs(t *testing.T) {
// Insert the receipt slice into the database and check presence
WriteReceipts(db, hash, 0, receipts)
- logs := ReadLogs(db, hash, 0)
+ logs := ReadLogs(db, hash, 0, params.TestChainConfig)
if len(logs) == 0 {
t.Fatalf("no logs returned")
}
diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go
index ad222005b..daee72159 100644
--- a/core/rawdb/chain_iterator.go
+++ b/core/rawdb/chain_iterator.go
@@ -44,24 +44,29 @@ func InitDatabaseFromFreezer(db ethdb.Database) {
logged = start.Add(-7 * time.Second) // Unindex during import is fast, don't double log
hash common.Hash
)
- for i := uint64(0); i < frozen; i++ {
- // Since the freezer has all data in sequential order on a file,
- // it would be 'neat' to read more data in one go, and let the
- // freezerdb return N items (e.g up to 1000 items per go)
- // That would require an API change in Ancients though
- if h, err := db.Ancient(freezerHashTable, i); err != nil {
+ for i := uint64(0); i < frozen; {
+ // We read 100K hashes at a time, for a total of 3.2M
+ count := uint64(100_000)
+ if i+count > frozen {
+ count = frozen - i
+ }
+ data, err := db.AncientRange(freezerHashTable, i, count, 32*count)
+ if err != nil {
log.Crit("Failed to init database from freezer", "err", err)
- } else {
+ }
+ for j, h := range data {
+ number := i + uint64(j)
hash = common.BytesToHash(h)
- }
- WriteHeaderNumber(batch, hash, i)
- // If enough data was accumulated in memory or we're at the last block, dump to disk
- if batch.ValueSize() > ethdb.IdealBatchSize {
- if err := batch.Write(); err != nil {
- log.Crit("Failed to write data to db", "err", err)
+ WriteHeaderNumber(batch, hash, number)
+ // If enough data was accumulated in memory or we're at the last block, dump to disk
+ if batch.ValueSize() > ethdb.IdealBatchSize {
+ if err := batch.Write(); err != nil {
+ log.Crit("Failed to write data to db", "err", err)
+ }
+ batch.Reset()
}
- batch.Reset()
}
+ i += uint64(len(data))
// If we've spent too much time already, notify the user of what we're doing
if time.Since(logged) > 8*time.Second {
log.Info("Initializing database from freezer", "total", frozen, "number", i, "hash", hash, "elapsed", common.PrettyDuration(time.Since(start)))
diff --git a/core/rawdb/freezer_batch.go b/core/rawdb/freezer_batch.go
index 066fba5bc..98c058d12 100644
--- a/core/rawdb/freezer_batch.go
+++ b/core/rawdb/freezer_batch.go
@@ -118,7 +118,7 @@ func (batch *freezerTableBatch) reset() {
// existing data.
func (batch *freezerTableBatch) Append(item uint64, data interface{}) error {
if item != batch.curItem {
- return errOutOrderInsertion
+ return fmt.Errorf("%w: have %d want %d", errOutOrderInsertion, item, batch.curItem)
}
// Encode the item.
@@ -138,7 +138,7 @@ func (batch *freezerTableBatch) Append(item uint64, data interface{}) error {
// existing data.
func (batch *freezerTableBatch) AppendRaw(item uint64, blob []byte) error {
if item != batch.curItem {
- return errOutOrderInsertion
+ return fmt.Errorf("%w: have %d want %d", errOutOrderInsertion, item, batch.curItem)
}
encItem := blob
diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go
index 7359131c8..fa84f8030 100644
--- a/core/rawdb/freezer_test.go
+++ b/core/rawdb/freezer_test.go
@@ -246,7 +246,7 @@ func TestFreezerConcurrentModifyTruncate(t *testing.T) {
if truncateErr != nil {
t.Fatal("concurrent truncate failed:", err)
}
- if !(modifyErr == nil || modifyErr == errOutOrderInsertion) {
+ if !(errors.Is(modifyErr, nil) || errors.Is(modifyErr, errOutOrderInsertion)) {
t.Fatal("wrong error from concurrent modify:", modifyErr)
}
checkAncientCount(t, f, "test", 10)
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index 13a9eb810..aa8e4bebf 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -17,10 +17,12 @@
package core
import (
+ "crypto/ecdsa"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc"
@@ -54,11 +56,12 @@ func TestStateProcessorErrors(t *testing.T) {
LondonBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
}
- signer = types.LatestSigner(config)
- testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ signer = types.LatestSigner(config)
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ key2, _ = crypto.HexToECDSA("0202020202020202020202020202020202020202020202020202002020202020")
)
- var makeTx = func(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, testKey)
+ 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 mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction {
@@ -69,7 +72,7 @@ func TestStateProcessorErrors(t *testing.T) {
Gas: gasLimit,
To: &to,
Value: big.NewInt(0),
- }), signer, testKey)
+ }), signer, key1)
return tx
}
{ // Tests against a 'recent' chain definition
@@ -82,6 +85,10 @@ func TestStateProcessorErrors(t *testing.T) {
Balance: big.NewInt(1000000000000000000), // 1 ether
Nonce: 0,
},
+ common.HexToAddress("0xfd0810DD14796680f72adf1a371963d0745BCc64"): GenesisAccount{
+ Balance: big.NewInt(1000000000000000000), // 1 ether
+ Nonce: math.MaxUint64,
+ },
},
}
genesis = gspec.MustCommit(db)
@@ -97,32 +104,38 @@ func TestStateProcessorErrors(t *testing.T) {
}{
{ // ErrNonceTooLow
txs: []*types.Transaction{
- makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
- makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
+ makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
+ makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
},
want: "could not apply tx 1 [0x0026256b3939ed97e2c4a6f3fce8ecf83bdcfa6d507c47838c308a1fb0436f62]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1",
},
{ // ErrNonceTooHigh
txs: []*types.Transaction{
- makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
+ makeTx(key1, 100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
},
want: "could not apply tx 0 [0xdebad714ca7f363bd0d8121c4518ad48fa469ca81b0a081be3d10c17460f751b]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0",
},
+ { // ErrNonceMax
+ txs: []*types.Transaction{
+ makeTx(key2, math.MaxUint64, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
+ },
+ want: "could not apply tx 0 [0x84ea18d60eb2bb3b040e3add0eb72f757727122cc257dd858c67cb6591a85986]: nonce has max value: address 0xfd0810DD14796680f72adf1a371963d0745BCc64, nonce: 18446744073709551615",
+ },
{ // ErrGasLimitReached
txs: []*types.Transaction{
- makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil),
+ makeTx(key1, 0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil),
},
want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached",
},
{ // ErrInsufficientFundsForTransfer
txs: []*types.Transaction{
- makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil),
+ makeTx(key1, 0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil),
},
want: "could not apply tx 0 [0x98c796b470f7fcab40aaef5c965a602b0238e1034cce6fb73823042dd0638d74]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000",
},
{ // ErrInsufficientFunds
txs: []*types.Transaction{
- makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil),
+ makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil),
},
want: "could not apply tx 0 [0x4a69690c4b0cd85e64d0d9ea06302455b01e10a83db964d60281739752003440]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000",
},
@@ -132,13 +145,13 @@ func TestStateProcessorErrors(t *testing.T) {
// multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment
{ // ErrIntrinsicGas
txs: []*types.Transaction{
- makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil),
+ makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil),
},
want: "could not apply tx 0 [0xcf3b049a0b516cb4f9274b3e2a264359e2ba53b2fb64b7bda2c634d5c9d01fca]: intrinsic gas too low: have 20000, want 21000",
},
{ // ErrGasLimitReached
txs: []*types.Transaction{
- makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil),
+ makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil),
},
want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached",
},
diff --git a/core/state_transition.go b/core/state_transition.go
index 6a09f6adc..135a9c6db 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -222,6 +222,9 @@ func (st *StateTransition) preCheck() error {
} else if stNonce > msgNonce {
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
st.msg.From().Hex(), msgNonce, stNonce)
+ } else if stNonce+1 < stNonce {
+ return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
+ st.msg.From().Hex(), stNonce)
}
// Make sure the sender is an EOA
if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
diff --git a/core/tx_pool.go b/core/tx_pool.go
index 3329d736a..0e3844bcb 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -621,8 +621,9 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if err != nil {
return ErrInvalidSender
}
- // Drop non-local transactions under our own minimal accepted gas price or tip
- if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 {
+ // Drop non-local transactions under our own minimal accepted gas price or tip.
+ pendingBaseFee := pool.priced.urgent.baseFee
+ if !local && tx.EffectiveGasTipIntCmp(pool.gasPrice, pendingBaseFee) < 0 {
return ErrUnderpriced
}
// Ensure the transaction adheres to nonce ordering
diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go
index 11b4e2942..1368e4c99 100644
--- a/core/vm/access_list_tracer.go
+++ b/core/vm/access_list_tracer.go
@@ -141,7 +141,7 @@ func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common
}
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
-func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
+func (a *AccessListTracer) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
stack := scope.Stack
if (op == SLOAD || op == SSTORE) && stack.len() >= 1 {
slot := common.Hash(stack.data[stack.len()-1].Bytes32())
@@ -161,7 +161,7 @@ func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cos
}
}
-func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
+func (*AccessListTracer) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
}
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
diff --git a/core/vm/errors.go b/core/vm/errors.go
index c7cfeae53..565eecdd7 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -35,6 +35,7 @@ var (
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
ErrGasUintOverflow = errors.New("gas uint64 overflow")
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
+ ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
)
// ErrStackUnderflow wraps an evm error when the items on the stack less
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 07e961915..618bbcf17 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -424,6 +424,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
return nil, common.Address{}, gas, ErrInsufficientBalance
}
nonce := evm.StateDB.GetNonce(caller.Address())
+ if nonce+1 < nonce {
+ return nil, common.Address{}, gas, ErrNonceUintOverflow
+ }
evm.StateDB.SetNonce(caller.Address(), nonce+1)
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
// the access-list change should not be rolled back
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 92d33388f..4315750ba 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -169,9 +169,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
defer func() {
if err != nil {
if !logged {
- in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
} else {
- in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
+ in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
}
}
}()
@@ -253,7 +253,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
if in.cfg.Debug {
- in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 048b84ff6..98ff967ee 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -105,10 +105,10 @@ func (s *StructLog) ErrorString() string {
// if you need to retain them beyond the current call.
type EVMLogger interface {
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
- CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
+ CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
CaptureExit(output []byte, gasUsed uint64, err error)
- CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
+ CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
}
@@ -119,6 +119,7 @@ type EVMLogger interface {
// contract their storage.
type StructLogger struct {
cfg LogConfig
+ env *EVM
storage map[common.Address]Storage
logs []StructLog
@@ -147,12 +148,13 @@ func (l *StructLogger) Reset() {
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ l.env = env
}
// CaptureState logs a new structured log message and pushes it out to the environment
//
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
-func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
+func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
memory := scope.Memory
stack := scope.Stack
contract := scope.Contract
@@ -186,7 +188,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
if op == SLOAD && stack.len() >= 1 {
var (
address = common.Hash(stack.data[stack.len()-1].Bytes32())
- value = env.StateDB.GetState(contract.Address(), address)
+ value = l.env.StateDB.GetState(contract.Address(), address)
)
l.storage[contract.Address()][address] = value
storage = l.storage[contract.Address()].Copy()
@@ -206,13 +208,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
copy(rdata, rData)
}
// create a new snapshot of the EVM.
- log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.StateDB.GetRefund(), err}
+ log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log)
}
// CaptureFault implements the EVMLogger interface to trace an execution fault
// while running an opcode.
-func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
+func (l *StructLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
}
// CaptureEnd is called after the call finishes to finalize the tracing.
@@ -291,12 +293,13 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
type mdLogger struct {
out io.Writer
cfg *LogConfig
+ env *EVM
}
// NewMarkdownLogger creates a logger which outputs information in a format adapted
// for human readability, and is also a valid markdown table
func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
- l := &mdLogger{writer, cfg}
+ l := &mdLogger{out: writer, cfg: cfg}
if l.cfg == nil {
l.cfg = &LogConfig{}
}
@@ -304,6 +307,7 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
}
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ t.env = env
if !create {
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
@@ -321,7 +325,7 @@ func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address
}
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
-func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
+func (t *mdLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
stack := scope.Stack
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
@@ -334,14 +338,14 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
fmt.Fprintf(t.out, "%10v |", b)
}
- fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund())
+ fmt.Fprintf(t.out, "%10v |", t.env.StateDB.GetRefund())
fmt.Fprintln(t.out, "")
if err != nil {
fmt.Fprintf(t.out, "Error: %v\n", err)
}
}
-func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
+func (t *mdLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 479a00c0a..364ce738a 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -29,12 +29,13 @@ import (
type JSONLogger struct {
encoder *json.Encoder
cfg *LogConfig
+ env *EVM
}
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
// into the provided stream.
func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
- l := &JSONLogger{json.NewEncoder(writer), cfg}
+ l := &JSONLogger{encoder: json.NewEncoder(writer), cfg: cfg}
if l.cfg == nil {
l.cfg = &LogConfig{}
}
@@ -42,12 +43,13 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
}
func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ l.env = env
}
-func (l *JSONLogger) CaptureFault(*EVM, uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {}
+func (l *JSONLogger) CaptureFault(uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {}
// CaptureState outputs state information on the logger.
-func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
+func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
memory := scope.Memory
stack := scope.Stack
@@ -58,7 +60,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
GasCost: cost,
MemorySize: memory.Len(),
Depth: depth,
- RefundCounter: env.StateDB.GetRefund(),
+ RefundCounter: l.env.StateDB.GetRefund(),
Err: err,
}
if l.cfg.EnableMemory {
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 730d8374b..7726c90bd 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -62,7 +62,8 @@ func TestStoreCapture(t *testing.T) {
scope.Stack.push(uint256.NewInt(1))
scope.Stack.push(new(uint256.Int))
var index common.Hash
- logger.CaptureState(env, 0, SSTORE, 0, 0, scope, nil, 0, nil)
+ logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil)
+ logger.CaptureState(0, SSTORE, 0, 0, scope, nil, 0, nil)
if len(logger.storage[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(),
len(logger.storage[contract.Address()]))
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 286307ae9..a6c89d833 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -39,68 +39,68 @@ func (op OpCode) IsStaticJump() bool {
// 0x0 range - arithmetic ops.
const (
- STOP OpCode = iota
- ADD
- MUL
- SUB
- DIV
- SDIV
- MOD
- SMOD
- ADDMOD
- MULMOD
- EXP
- SIGNEXTEND
+ STOP OpCode = 0x0
+ ADD OpCode = 0x1
+ MUL OpCode = 0x2
+ SUB OpCode = 0x3
+ DIV OpCode = 0x4
+ SDIV OpCode = 0x5
+ MOD OpCode = 0x6
+ SMOD OpCode = 0x7
+ ADDMOD OpCode = 0x8
+ MULMOD OpCode = 0x9
+ EXP OpCode = 0xa
+ SIGNEXTEND OpCode = 0xb
)
// 0x10 range - comparison ops.
const (
- LT OpCode = iota + 0x10
- GT
- SLT
- SGT
- EQ
- ISZERO
- AND
- OR
- XOR
- NOT
- BYTE
- SHL
- SHR
- SAR
+ LT OpCode = 0x10
+ GT OpCode = 0x11
+ SLT OpCode = 0x12
+ SGT OpCode = 0x13
+ EQ OpCode = 0x14
+ ISZERO OpCode = 0x15
+ AND OpCode = 0x16
+ OR OpCode = 0x17
+ XOR OpCode = 0x18
+ NOT OpCode = 0x19
+ BYTE OpCode = 0x1a
+ SHL OpCode = 0x1b
+ SHR OpCode = 0x1c
+ SAR OpCode = 0x1d
SHA3 OpCode = 0x20
)
// 0x30 range - closure state.
const (
- ADDRESS OpCode = 0x30 + iota
- BALANCE
- ORIGIN
- CALLER
- CALLVALUE
- CALLDATALOAD
- CALLDATASIZE
- CALLDATACOPY
- CODESIZE
- CODECOPY
- GASPRICE
- EXTCODESIZE
- EXTCODECOPY
- RETURNDATASIZE
- RETURNDATACOPY
- EXTCODEHASH
+ ADDRESS OpCode = 0x30
+ BALANCE OpCode = 0x31
+ ORIGIN OpCode = 0x32
+ CALLER OpCode = 0x33
+ CALLVALUE OpCode = 0x34
+ CALLDATALOAD OpCode = 0x35
+ CALLDATASIZE OpCode = 0x36
+ CALLDATACOPY OpCode = 0x37
+ CODESIZE OpCode = 0x38
+ CODECOPY OpCode = 0x39
+ GASPRICE OpCode = 0x3a
+ EXTCODESIZE OpCode = 0x3b
+ EXTCODECOPY OpCode = 0x3c
+ RETURNDATASIZE OpCode = 0x3d
+ RETURNDATACOPY OpCode = 0x3e
+ EXTCODEHASH OpCode = 0x3f
)
// 0x40 range - block operations.
const (
- BLOCKHASH OpCode = 0x40 + iota
- COINBASE
- TIMESTAMP
- NUMBER
- DIFFICULTY
- GASLIMIT
+ BLOCKHASH OpCode = 0x40
+ COINBASE OpCode = 0x41
+ TIMESTAMP OpCode = 0x42
+ NUMBER OpCode = 0x43
+ DIFFICULTY OpCode = 0x44
+ GASLIMIT OpCode = 0x45
CHAINID OpCode = 0x46
SELFBALANCE OpCode = 0x47
BASEFEE OpCode = 0x48
@@ -122,7 +122,7 @@ const (
JUMPDEST OpCode = 0x5b
)
-// 0x60 range.
+// 0x60 range - pushes.
const (
PUSH1 OpCode = 0x60 + iota
PUSH2
@@ -156,7 +156,11 @@ const (
PUSH30
PUSH31
PUSH32
- DUP1
+)
+
+// 0x80 range - dups.
+const (
+ DUP1 = 0x80 + iota
DUP2
DUP3
DUP4
@@ -172,7 +176,11 @@ const (
DUP14
DUP15
DUP16
- SWAP1
+)
+
+// 0x90 range - swaps.
+const (
+ SWAP1 = 0x90 + iota
SWAP2
SWAP3
SWAP4
@@ -208,12 +216,13 @@ const (
// 0xf0 range - closures.
const (
- CREATE OpCode = 0xf0 + iota
- CALL
- CALLCODE
- RETURN
- DELEGATECALL
- CREATE2
+ CREATE OpCode = 0xf0
+ CALL OpCode = 0xf1
+ CALLCODE OpCode = 0xf2
+ RETURN OpCode = 0xf3
+ DELEGATECALL OpCode = 0xf4
+ CREATE2 OpCode = 0xf5
+
STATICCALL OpCode = 0xfa
REVERT OpCode = 0xfd
SELFDESTRUCT OpCode = 0xff
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 9f4bafbc7..fea7817ff 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -35,6 +35,9 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params"
+
+ // force-load js tracers to trigger registration
+ _ "github.com/ethereum/go-ethereum/eth/tracers/js"
)
func TestDefaults(t *testing.T) {
@@ -330,12 +333,12 @@ type stepCounter struct {
func (s *stepCounter) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
-func (s *stepCounter) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+func (s *stepCounter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}
func (s *stepCounter) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
-func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+func (s *stepCounter) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
s.steps++
// Enable this for more output
//s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err)
@@ -511,7 +514,7 @@ func BenchmarkSimpleLoop(b *testing.B) {
// TestEip2929Cases contains various testcases that are used for
// EIP-2929 about gas repricings
func TestEip2929Cases(t *testing.T) {
-
+ t.Skip("Test only useful for generating documentation")
id := 1
prettyPrint := func(comment string, code []byte) {
diff --git a/eth/api.go b/eth/api.go
index 3ec72c58e..f81dfa922 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -170,7 +170,7 @@ func (api *PrivateAdminAPI) ExportChain(file string, first *uint64, last *uint64
last = &head
}
if _, err := os.Stat(file); err == nil {
- // File already exists. Allowing overwrite could be a DoS vecotor,
+ // File already exists. Allowing overwrite could be a DoS vector,
// since the 'file' may point to arbitrary paths on the drive
return false, errors.New("location would overwrite an existing file")
}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index a0704876a..6a19fb36a 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -187,7 +187,7 @@ func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*typ
if number == nil {
return nil, errors.New("failed to get block number from hash")
}
- logs := rawdb.ReadLogs(db, hash, *number)
+ logs := rawdb.ReadLogs(db, hash, *number, b.eth.blockchain.Config())
if logs == nil {
return nil, errors.New("failed to get logs for block")
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index a6bf87acb..4ca1b55bb 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -535,7 +535,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
}
// Rewind the ancient store and blockchain if reorg happens.
if origin+1 < frozen {
- if err := d.lightchain.SetHead(origin + 1); err != nil {
+ if err := d.lightchain.SetHead(origin); err != nil {
return err
}
}
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index 8feb5ef24..00128a5dc 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -87,8 +87,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
if percent < 0 {
percent = 0
log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent)
- }
- if percent > 100 {
+ } else if percent > 100 {
percent = 100
log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent)
}
@@ -104,6 +103,16 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
} else if ignorePrice.Int64() > 0 {
log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
}
+ maxHeaderHistory := params.MaxHeaderHistory
+ if maxHeaderHistory < 1 {
+ maxHeaderHistory = 1
+ log.Warn("Sanitizing invalid gasprice oracle max header history", "provided", params.MaxHeaderHistory, "updated", maxHeaderHistory)
+ }
+ maxBlockHistory := params.MaxBlockHistory
+ if maxBlockHistory < 1 {
+ maxBlockHistory = 1
+ log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
+ }
cache, _ := lru.New(2048)
headEvent := make(chan core.ChainHeadEvent, 1)
@@ -125,8 +134,8 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
ignorePrice: ignorePrice,
checkBlocks: blocks,
percentile: percent,
- maxHeaderHistory: params.MaxHeaderHistory,
- maxBlockHistory: params.MaxBlockHistory,
+ maxHeaderHistory: maxHeaderHistory,
+ maxBlockHistory: maxBlockHistory,
historyCache: cache,
}
}
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index ff5675e9e..a3c0a7249 100644
--- a/eth/tracers/api_test.go
+++ b/eth/tracers/api_test.go
@@ -306,147 +306,6 @@ func TestTraceCall(t *testing.T) {
}
}
-func TestOverriddenTraceCall(t *testing.T) {
- t.Parallel()
-
- // Initialize test accounts
- accounts := newAccounts(3)
- genesis := &core.Genesis{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{}
- api := NewAPI(newTestBackend(t, 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)
- }))
- randomAccounts, tracer := newAccounts(3), "callTracerJs"
-
- var testSuite = []struct {
- blockNumber rpc.BlockNumber
- call ethapi.TransactionArgs
- config *TraceCallConfig
- expectErr error
- expect *callTrace
- }{
- // Succcessful call with state overriding
- {
- blockNumber: rpc.PendingBlockNumber,
- call: ethapi.TransactionArgs{
- From: &randomAccounts[0].addr,
- To: &randomAccounts[1].addr,
- Value: (*hexutil.Big)(big.NewInt(1000)),
- },
- config: &TraceCallConfig{
- Tracer: &tracer,
- StateOverrides: ðapi.StateOverride{
- randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
- },
- },
- expectErr: nil,
- expect: &callTrace{
- Type: "CALL",
- From: randomAccounts[0].addr,
- To: randomAccounts[1].addr,
- Gas: newRPCUint64(24979000),
- GasUsed: newRPCUint64(0),
- Value: (*hexutil.Big)(big.NewInt(1000)),
- },
- },
- // Invalid call without state overriding
- {
- blockNumber: rpc.PendingBlockNumber,
- call: ethapi.TransactionArgs{
- From: &randomAccounts[0].addr,
- To: &randomAccounts[1].addr,
- Value: (*hexutil.Big)(big.NewInt(1000)),
- },
- config: &TraceCallConfig{
- Tracer: &tracer,
- },
- expectErr: core.ErrInsufficientFunds,
- expect: nil,
- },
- // Successful simple contract call
- //
- // // SPDX-License-Identifier: GPL-3.0
- //
- // pragma solidity >=0.7.0 <0.8.0;
- //
- // /**
- // * @title Storage
- // * @dev Store & retrieve value in a variable
- // */
- // contract Storage {
- // uint256 public number;
- // constructor() {
- // number = block.number;
- // }
- // }
- {
- blockNumber: rpc.PendingBlockNumber,
- call: ethapi.TransactionArgs{
- From: &randomAccounts[0].addr,
- To: &randomAccounts[2].addr,
- Data: newRPCBytes(common.Hex2Bytes("8381f58a")), // call number()
- },
- config: &TraceCallConfig{
- Tracer: &tracer,
- StateOverrides: ðapi.StateOverride{
- randomAccounts[2].addr: ethapi.OverrideAccount{
- Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")),
- StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}),
- },
- },
- },
- expectErr: nil,
- expect: &callTrace{
- Type: "CALL",
- From: randomAccounts[0].addr,
- To: randomAccounts[2].addr,
- Input: hexutil.Bytes(common.Hex2Bytes("8381f58a")),
- Output: hexutil.Bytes(common.BigToHash(big.NewInt(123)).Bytes()),
- Gas: newRPCUint64(24978936),
- GasUsed: newRPCUint64(2283),
- Value: (*hexutil.Big)(big.NewInt(0)),
- },
- },
- }
- for i, testspec := range testSuite {
- result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config)
- if testspec.expectErr != nil {
- if err == nil {
- t.Errorf("test %d: want error %v, have nothing", i, testspec.expectErr)
- continue
- }
- if !errors.Is(err, testspec.expectErr) {
- t.Errorf("test %d: error mismatch, want %v, have %v", i, testspec.expectErr, err)
- }
- } else {
- if err != nil {
- t.Errorf("test %d: want no error, have %v", i, err)
- continue
- }
- ret := new(callTrace)
- if err := json.Unmarshal(result.(json.RawMessage), ret); err != nil {
- t.Fatalf("test %d: failed to unmarshal trace result: %v", i, err)
- }
- if !jsonEqual(ret, testspec.expect) {
- // uncomment this for easier debugging
- //have, _ := json.MarshalIndent(ret, "", " ")
- //want, _ := json.MarshalIndent(testspec.expect, "", " ")
- //t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", string(have), string(want))
- t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, testspec.expect)
- }
- }
- }
-}
-
func TestTraceTransaction(t *testing.T) {
t.Parallel()
@@ -503,90 +362,177 @@ func TestTraceBlock(t *testing.T) {
var testSuite = []struct {
blockNumber rpc.BlockNumber
config *TraceConfig
- expect interface{}
+ want string
expectErr error
}{
// Trace genesis block, expect error
{
blockNumber: rpc.BlockNumber(0),
- config: nil,
- expect: nil,
expectErr: errors.New("genesis is not traceable"),
},
// Trace head block
{
blockNumber: rpc.BlockNumber(genBlocks),
- config: nil,
- expectErr: nil,
- expect: []*txTraceResult{
- {
- Result: ðapi.ExecutionResult{
- Gas: params.TxGas,
- Failed: false,
- ReturnValue: "",
- StructLogs: []ethapi.StructLogRes{},
- },
- },
- },
+ want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
},
// Trace non-existent block
{
blockNumber: rpc.BlockNumber(genBlocks + 1),
- config: nil,
expectErr: fmt.Errorf("block #%d not found", genBlocks+1),
- expect: nil,
},
// Trace latest block
{
blockNumber: rpc.LatestBlockNumber,
- config: nil,
- expectErr: nil,
- expect: []*txTraceResult{
- {
- Result: ðapi.ExecutionResult{
- Gas: params.TxGas,
- Failed: false,
- ReturnValue: "",
- StructLogs: []ethapi.StructLogRes{},
- },
- },
- },
+ want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
},
// Trace pending block
{
blockNumber: rpc.PendingBlockNumber,
- config: nil,
- expectErr: nil,
- expect: []*txTraceResult{
- {
- Result: ðapi.ExecutionResult{
- Gas: params.TxGas,
- Failed: false,
- ReturnValue: "",
- StructLogs: []ethapi.StructLogRes{},
+ want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
+ },
+ }
+ for i, tc := range testSuite {
+ result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config)
+ if tc.expectErr != nil {
+ if err == nil {
+ t.Errorf("test %d, want error %v", i, tc.expectErr)
+ continue
+ }
+ if !reflect.DeepEqual(err, tc.expectErr) {
+ t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("test %d, want no error, have %v", i, err)
+ continue
+ }
+ have, _ := json.Marshal(result)
+ want := tc.want
+ if string(have) != want {
+ t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want)
+ }
+ }
+}
+
+func TestTracingWithOverrides(t *testing.T) {
+ t.Parallel()
+ // Initialize test accounts
+ accounts := newAccounts(3)
+ genesis := &core.Genesis{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{}
+ api := NewAPI(newTestBackend(t, 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)
+ }))
+ randomAccounts := newAccounts(3)
+ type res struct {
+ Gas int
+ Failed bool
+ returnValue string
+ }
+ var testSuite = []struct {
+ blockNumber rpc.BlockNumber
+ call ethapi.TransactionArgs
+ config *TraceCallConfig
+ expectErr error
+ want string
+ }{
+ // Call which can only succeed if state is state overridden
+ {
+ blockNumber: rpc.PendingBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &randomAccounts[1].addr,
+ Value: (*hexutil.Big)(big.NewInt(1000)),
+ },
+ config: &TraceCallConfig{
+ StateOverrides: ðapi.StateOverride{
+ randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
+ },
+ },
+ want: `{"gas":21000,"failed":false,"returnValue":""}`,
+ },
+ // Invalid call without state overriding
+ {
+ blockNumber: rpc.PendingBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &randomAccounts[1].addr,
+ Value: (*hexutil.Big)(big.NewInt(1000)),
+ },
+ config: &TraceCallConfig{},
+ expectErr: core.ErrInsufficientFunds,
+ },
+ // Successful simple contract call
+ //
+ // // SPDX-License-Identifier: GPL-3.0
+ //
+ // pragma solidity >=0.7.0 <0.8.0;
+ //
+ // /**
+ // * @title Storage
+ // * @dev Store & retrieve value in a variable
+ // */
+ // contract Storage {
+ // uint256 public number;
+ // constructor() {
+ // number = block.number;
+ // }
+ // }
+ {
+ blockNumber: rpc.PendingBlockNumber,
+ call: ethapi.TransactionArgs{
+ From: &randomAccounts[0].addr,
+ To: &randomAccounts[2].addr,
+ Data: newRPCBytes(common.Hex2Bytes("8381f58a")), // call number()
+ },
+ config: &TraceCallConfig{
+ //Tracer: &tracer,
+ StateOverrides: ðapi.StateOverride{
+ randomAccounts[2].addr: ethapi.OverrideAccount{
+ Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")),
+ StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}),
},
},
},
+ want: `{"gas":23347,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000007b"}`,
},
}
- for _, testspec := range testSuite {
- result, err := api.TraceBlockByNumber(context.Background(), testspec.blockNumber, testspec.config)
- if testspec.expectErr != nil {
+ for i, tc := range testSuite {
+ result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config)
+ if tc.expectErr != nil {
if err == nil {
- t.Errorf("Expect error %v, get nothing", testspec.expectErr)
+ t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
continue
}
- if !reflect.DeepEqual(err, testspec.expectErr) {
- t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err)
- }
- } else {
- if err != nil {
- t.Errorf("Expect no error, get %v", err)
- continue
- }
- if !reflect.DeepEqual(result, testspec.expect) {
- t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result)
+ if !errors.Is(err, tc.expectErr) {
+ t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
}
+ continue
+ }
+ if err != nil {
+ t.Errorf("test %d: want no error, have %v", i, err)
+ continue
+ }
+ // Turn result into res-struct
+ var (
+ have res
+ want res
+ )
+ resBytes, _ := json.Marshal(result)
+ json.Unmarshal(resBytes, &have)
+ json.Unmarshal([]byte(tc.want), &want)
+ if !reflect.DeepEqual(have, want) {
+ t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(resBytes), want)
}
}
}
@@ -617,11 +563,6 @@ func newRPCBalance(balance *big.Int) **hexutil.Big {
return &rpcBalance
}
-func newRPCUint64(number uint64) *hexutil.Uint64 {
- rpcUint64 := hexutil.Uint64(number)
- return &rpcUint64
-}
-
func newRPCBytes(bytes []byte) *hexutil.Bytes {
rpcBytes := hexutil.Bytes(bytes)
return &rpcBytes
diff --git a/eth/tracers/internal/tracers/4byte_tracer.js b/eth/tracers/internal/tracers/4byte_tracer.js
deleted file mode 100644
index 9ec3209f8..000000000
--- a/eth/tracers/internal/tracers/4byte_tracer.js
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// 4byteTracer searches for 4byte-identifiers, and collects them for post-processing.
-// It collects the methods identifiers along with the size of the supplied data, so
-// a reversed signature can be matched against the size of the data.
-//
-// Example:
-// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
-// {
-// 0x27dc297e-128: 1,
-// 0x38cc4831-0: 2,
-// 0x524f3889-96: 1,
-// 0xadf59f99-288: 1,
-// 0xc281d19e-0: 1
-// }
-{
- // ids aggregates the 4byte ids found.
- ids : {},
-
- // store save the given indentifier and datasize.
- store: function(id, size){
- var key = "" + toHex(id) + "-" + size;
- this.ids[key] = this.ids[key] + 1 || 1;
- },
-
- enter: function(frame) {
- // Skip any pre-compile invocations, those are just fancy opcodes
- if (isPrecompiled(frame.getTo())) {
- return;
- }
- var input = frame.getInput()
- if (input.length >= 4) {
- this.store(slice(input, 0, 4), input.length - 4);
- }
- },
-
- exit: function(frameResult) {},
-
- // fault is invoked when the actual execution of an opcode fails.
- fault: function(log, db) {},
-
- // result is invoked when all the opcodes have been iterated over and returns
- // the final result of the tracing.
- result: function(ctx) {
- // Save the outer calldata also
- if (ctx.input.length >= 4) {
- this.store(slice(ctx.input, 0, 4), ctx.input.length-4)
- }
- return this.ids;
- },
-}
diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go
similarity index 56%
rename from eth/tracers/testing/calltrace_test.go
rename to eth/tracers/internal/tracetest/calltrace_test.go
index 03db904f4..7521a98f2 100644
--- a/eth/tracers/testing/calltrace_test.go
+++ b/eth/tracers/internal/tracetest/calltrace_test.go
@@ -1,4 +1,20 @@
-package testing
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package tracetest
import (
"encoding/json"
@@ -17,14 +33,67 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"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/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
- // Force-load the native, to trigger registration
+ // Force-load native and js pacakges, to trigger registration
+ _ "github.com/ethereum/go-ethereum/eth/tracers/js"
_ "github.com/ethereum/go-ethereum/eth/tracers/native"
)
+// To generate a new callTracer test, copy paste the makeTest method below into
+// a Geth console and call it with a transaction hash you which to export.
+
+/*
+// makeTest generates a callTracer test by running a prestate reassembled and a
+// call trace run, assembling all the gathered information into a test case.
+var makeTest = function(tx, rewind) {
+ // Generate the genesis block from the block, transaction and prestate data
+ var block = eth.getBlock(eth.getTransaction(tx).blockHash);
+ var genesis = eth.getBlock(block.parentHash);
+
+ delete genesis.gasUsed;
+ delete genesis.logsBloom;
+ delete genesis.parentHash;
+ delete genesis.receiptsRoot;
+ delete genesis.sha3Uncles;
+ delete genesis.size;
+ delete genesis.transactions;
+ delete genesis.transactionsRoot;
+ delete genesis.uncles;
+
+ genesis.gasLimit = genesis.gasLimit.toString();
+ genesis.number = genesis.number.toString();
+ genesis.timestamp = genesis.timestamp.toString();
+
+ genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
+ for (var key in genesis.alloc) {
+ genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString();
+ }
+ genesis.config = admin.nodeInfo.protocols.eth.config;
+
+ // Generate the call trace and produce the test input
+ var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
+ delete result.time;
+
+ console.log(JSON.stringify({
+ genesis: genesis,
+ context: {
+ number: block.number.toString(),
+ difficulty: block.difficulty,
+ timestamp: block.timestamp.toString(),
+ gasLimit: block.gasLimit.toString(),
+ miner: block.miner,
+ },
+ input: eth.getRawTransaction(tx),
+ result: result,
+ }, null, 2));
+}
+*/
+
type callContext struct {
Number math.HexOrDecimal64 `json:"number"`
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
@@ -70,7 +139,7 @@ func TestCallTracerNative(t *testing.T) {
}
func testCallTracer(tracerName string, dirPath string, t *testing.T) {
- files, err := ioutil.ReadDir(filepath.Join("..", "testdata", dirPath))
+ files, err := ioutil.ReadDir(filepath.Join("testdata", dirPath))
if err != nil {
t.Fatalf("failed to retrieve tracer test suite: %v", err)
}
@@ -87,7 +156,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
tx = new(types.Transaction)
)
// Call tracer test found, read if from disk
- if blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", dirPath, file.Name())); err != nil {
+ if blob, err := ioutil.ReadFile(filepath.Join("testdata", dirPath, file.Name())); err != nil {
t.Fatalf("failed to read testcase: %v", err)
} else if err := json.Unmarshal(blob, test); err != nil {
t.Fatalf("failed to parse testcase: %v", err)
@@ -175,7 +244,7 @@ func camel(str string) string {
return strings.Join(pieces, "")
}
func BenchmarkTracers(b *testing.B) {
- files, err := ioutil.ReadDir(filepath.Join("..", "testdata", "call_tracer"))
+ files, err := ioutil.ReadDir(filepath.Join("testdata", "call_tracer"))
if err != nil {
b.Fatalf("failed to retrieve tracer test suite: %v", err)
}
@@ -185,7 +254,7 @@ func BenchmarkTracers(b *testing.B) {
}
file := file // capture range variable
b.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(b *testing.B) {
- blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", "call_tracer", file.Name()))
+ blob, err := ioutil.ReadFile(filepath.Join("testdata", "call_tracer", file.Name()))
if err != nil {
b.Fatalf("failed to read testcase: %v", err)
}
@@ -244,3 +313,82 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
statedb.RevertToSnapshot(snap)
}
}
+
+// TestZeroValueToNotExitCall tests the calltracer(s) on the following:
+// Tx to A, A calls B with zero value. B does not already exist.
+// Expected: that enter/exit is invoked and the inner call is shown in the result
+func TestZeroValueToNotExitCall(t *testing.T) {
+ var to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
+ privkey, err := crypto.HexToECDSA("0000000000000000deadbeef00000000000000000000000000000000deadbeef")
+ if err != nil {
+ t.Fatalf("err %v", err)
+ }
+ signer := types.NewEIP155Signer(big.NewInt(1))
+ tx, err := types.SignNewTx(privkey, signer, &types.LegacyTx{
+ GasPrice: big.NewInt(0),
+ Gas: 50000,
+ To: &to,
+ })
+ if err != nil {
+ t.Fatalf("err %v", err)
+ }
+ origin, _ := signer.Sender(tx)
+ txContext := vm.TxContext{
+ Origin: origin,
+ GasPrice: big.NewInt(1),
+ }
+ context := vm.BlockContext{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ Coinbase: common.Address{},
+ BlockNumber: new(big.Int).SetUint64(8000000),
+ Time: new(big.Int).SetUint64(5),
+ Difficulty: big.NewInt(0x30000),
+ GasLimit: uint64(6000000),
+ }
+ var code = []byte{
+ byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero
+ byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.GAS), // value=0,address=0xff, gas=GAS
+ byte(vm.CALL),
+ }
+ var alloc = core.GenesisAlloc{
+ to: core.GenesisAccount{
+ Nonce: 1,
+ Code: code,
+ },
+ origin: core.GenesisAccount{
+ Nonce: 0,
+ Balance: big.NewInt(500000000000000),
+ },
+ }
+ _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
+ // Create the tracer, the EVM environment and run it
+ tracer, err := tracers.New("callTracer", nil)
+ if err != nil {
+ t.Fatalf("failed to create call tracer: %v", err)
+ }
+ evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, nil)
+ if err != nil {
+ t.Fatalf("failed to prepare transaction for tracing: %v", err)
+ }
+ st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
+ if _, err = st.TransitionDb(); err != nil {
+ t.Fatalf("failed to execute transaction: %v", err)
+ }
+ // Retrieve the trace result and compare against the etalon
+ res, err := tracer.GetResult()
+ if err != nil {
+ t.Fatalf("failed to retrieve trace result: %v", err)
+ }
+ have := new(callTrace)
+ if err := json.Unmarshal(res, have); err != nil {
+ t.Fatalf("failed to unmarshal trace result: %v", err)
+ }
+ wantStr := `{"type":"CALL","from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","to":"0x00000000000000000000000000000000deadbeef","value":"0x0","gas":"0x7148","gasUsed":"0x2d0","input":"0x","output":"0x","calls":[{"type":"CALL","from":"0x00000000000000000000000000000000deadbeef","to":"0x00000000000000000000000000000000000000ff","value":"0x0","gas":"0x6cbf","gasUsed":"0x0","input":"0x","output":"0x"}]}`
+ want := new(callTrace)
+ json.Unmarshal([]byte(wantStr), want)
+ if !jsonEqual(have, want) {
+ t.Error("have != want")
+ }
+}
diff --git a/eth/tracers/testdata/call_tracer/create.json b/eth/tracers/internal/tracetest/testdata/call_tracer/create.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/create.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/create.json
diff --git a/eth/tracers/testdata/call_tracer/deep_calls.json b/eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/deep_calls.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json
diff --git a/eth/tracers/testdata/call_tracer/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/delegatecall.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json
diff --git a/eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json
diff --git a/eth/tracers/testdata/call_tracer/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/inner_instafail.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json
diff --git a/eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json
diff --git a/eth/tracers/testdata/call_tracer/oog.json b/eth/tracers/internal/tracetest/testdata/call_tracer/oog.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/oog.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/oog.json
diff --git a/eth/tracers/testdata/call_tracer/revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer/revert.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/revert.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/revert.json
diff --git a/eth/tracers/testdata/call_tracer/revert_reason.json b/eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/revert_reason.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json
diff --git a/eth/tracers/testdata/call_tracer/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/selfdestruct.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json
diff --git a/eth/tracers/testdata/call_tracer/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer/simple.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/simple.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/simple.json
diff --git a/eth/tracers/testdata/call_tracer/throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer/throw.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer/throw.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer/throw.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/create.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/create.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/deep_calls.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/deep_calls.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/delegatecall.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/inner_instafail.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/oog.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/oog.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/revert.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/revert_reason.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/revert_reason.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/selfdestruct.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/simple.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json
diff --git a/eth/tracers/testdata/call_tracer_legacy/throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json
similarity index 100%
rename from eth/tracers/testdata/call_tracer_legacy/throw.json
rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json
diff --git a/eth/tracers/tracer.go b/eth/tracers/js/bigint.go
similarity index 50%
rename from eth/tracers/tracer.go
rename to eth/tracers/js/bigint.go
index 4fee7ed96..9aeb33042 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/js/bigint.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,845 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package tracers
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "math/big"
- "sync/atomic"
- "time"
- "unsafe"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/log"
- "gopkg.in/olebedev/go-duktape.v3"
-)
+package js
// bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js.
const bigIntegerJS = `var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return String(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt`
-
-// makeSlice convert an unsafe memory pointer with the given type into a Go byte
-// slice.
-//
-// Note, the returned slice uses the same memory area as the input arguments.
-// If those are duktape stack items, popping them off **will** make the slice
-// contents change.
-func makeSlice(ptr unsafe.Pointer, size uint) []byte {
- var sl = struct {
- addr uintptr
- len int
- cap int
- }{uintptr(ptr), int(size), int(size)}
-
- return *(*[]byte)(unsafe.Pointer(&sl))
-}
-
-// popSlice pops a buffer off the JavaScript stack and returns it as a slice.
-func popSlice(ctx *duktape.Context) []byte {
- blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
- ctx.Pop()
- return blob
-}
-
-// pushBigInt create a JavaScript BigInteger in the VM.
-func pushBigInt(n *big.Int, ctx *duktape.Context) {
- ctx.GetGlobalString("bigInt")
- ctx.PushString(n.String())
- ctx.Call(1)
-}
-
-// opWrapper provides a JavaScript wrapper around OpCode.
-type opWrapper struct {
- op vm.OpCode
-}
-
-// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
-// onto the VM stack.
-func (ow *opWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
- vm.PutPropString(obj, "toNumber")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
- vm.PutPropString(obj, "toString")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
- vm.PutPropString(obj, "isPush")
-}
-
-// memoryWrapper provides a JavaScript wrapper around vm.Memory.
-type memoryWrapper struct {
- memory *vm.Memory
-}
-
-// slice returns the requested range of memory as a byte slice.
-func (mw *memoryWrapper) slice(begin, end int64) []byte {
- if end == begin {
- return []byte{}
- }
- if end < begin || begin < 0 {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end)
- return nil
- }
- if mw.memory.Len() < int(end) {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
- return nil
- }
- return mw.memory.GetCopy(begin, end-begin)
-}
-
-// getUint returns the 32 bytes at the specified address interpreted as a uint.
-func (mw *memoryWrapper) getUint(addr int64) *big.Int {
- if mw.memory.Len() < int(addr)+32 || addr < 0 {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
- return new(big.Int)
- }
- return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
-}
-
-// pushObject assembles a JSVM object wrapping a swappable memory and pushes it
-// onto the VM stack.
-func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- // Generate the `slice` method which takes two ints and returns a buffer
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
- ctx.Pop2()
-
- ptr := ctx.PushFixedBuffer(len(blob))
- copy(makeSlice(ptr, uint(len(blob))), blob)
- return 1
- })
- vm.PutPropString(obj, "slice")
-
- // Generate the `getUint` method which takes an int and returns a bigint
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- offset := int64(ctx.GetInt(-1))
- ctx.Pop()
-
- pushBigInt(mw.getUint(offset), ctx)
- return 1
- })
- vm.PutPropString(obj, "getUint")
-}
-
-// stackWrapper provides a JavaScript wrapper around vm.Stack.
-type stackWrapper struct {
- stack *vm.Stack
-}
-
-// peek returns the nth-from-the-top element of the stack.
-func (sw *stackWrapper) peek(idx int) *big.Int {
- if len(sw.stack.Data()) <= idx || idx < 0 {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
- return new(big.Int)
- }
- return sw.stack.Back(idx).ToBig()
-}
-
-// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
-// onto the VM stack.
-func (sw *stackWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
- vm.PutPropString(obj, "length")
-
- // Generate the `peek` method which takes an int and returns a bigint
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- offset := ctx.GetInt(-1)
- ctx.Pop()
-
- pushBigInt(sw.peek(offset), ctx)
- return 1
- })
- vm.PutPropString(obj, "peek")
-}
-
-// dbWrapper provides a JavaScript wrapper around vm.Database.
-type dbWrapper struct {
- db vm.StateDB
-}
-
-// pushObject assembles a JSVM object wrapping a swappable database and pushes it
-// onto the VM stack.
-func (dw *dbWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- // Push the wrapper for statedb.GetBalance
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
- return 1
- })
- vm.PutPropString(obj, "getBalance")
-
- // Push the wrapper for statedb.GetNonce
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
- return 1
- })
- vm.PutPropString(obj, "getNonce")
-
- // Push the wrapper for statedb.GetCode
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
-
- ptr := ctx.PushFixedBuffer(len(code))
- copy(makeSlice(ptr, uint(len(code))), code)
- return 1
- })
- vm.PutPropString(obj, "getCode")
-
- // Push the wrapper for statedb.GetState
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- hash := popSlice(ctx)
- addr := popSlice(ctx)
-
- state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
-
- ptr := ctx.PushFixedBuffer(len(state))
- copy(makeSlice(ptr, uint(len(state))), state[:])
- return 1
- })
- vm.PutPropString(obj, "getState")
-
- // Push the wrapper for statedb.Exists
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
- return 1
- })
- vm.PutPropString(obj, "exists")
-}
-
-// contractWrapper provides a JavaScript wrapper around vm.Contract
-type contractWrapper struct {
- contract *vm.Contract
-}
-
-// pushObject assembles a JSVM object wrapping a swappable contract and pushes it
-// onto the VM stack.
-func (cw *contractWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- // Push the wrapper for contract.Caller
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ptr := ctx.PushFixedBuffer(20)
- copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
- return 1
- })
- vm.PutPropString(obj, "getCaller")
-
- // Push the wrapper for contract.Address
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ptr := ctx.PushFixedBuffer(20)
- copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
- return 1
- })
- vm.PutPropString(obj, "getAddress")
-
- // Push the wrapper for contract.Value
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- pushBigInt(cw.contract.Value(), ctx)
- return 1
- })
- vm.PutPropString(obj, "getValue")
-
- // Push the wrapper for contract.Input
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- blob := cw.contract.Input
-
- ptr := ctx.PushFixedBuffer(len(blob))
- copy(makeSlice(ptr, uint(len(blob))), blob)
- return 1
- })
- vm.PutPropString(obj, "getInput")
-}
-
-type frame struct {
- typ *string
- from *common.Address
- to *common.Address
- input []byte
- gas *uint
- value *big.Int
-}
-
-func newFrame() *frame {
- return &frame{
- typ: new(string),
- from: new(common.Address),
- to: new(common.Address),
- gas: new(uint),
- }
-}
-
-func (f *frame) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 })
- vm.PutPropString(obj, "getType")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 })
- vm.PutPropString(obj, "getFrom")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 })
- vm.PutPropString(obj, "getTo")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 })
- vm.PutPropString(obj, "getInput")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 })
- vm.PutPropString(obj, "getGas")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- if f.value != nil {
- pushValue(ctx, f.value)
- } else {
- ctx.PushUndefined()
- }
- return 1
- })
- vm.PutPropString(obj, "getValue")
-}
-
-type frameResult struct {
- gasUsed *uint
- output []byte
- errorValue *string
-}
-
-func newFrameResult() *frameResult {
- return &frameResult{
- gasUsed: new(uint),
- }
-}
-
-func (r *frameResult) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 })
- vm.PutPropString(obj, "getGasUsed")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 })
- vm.PutPropString(obj, "getOutput")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- if r.errorValue != nil {
- pushValue(ctx, *r.errorValue)
- } else {
- ctx.PushUndefined()
- }
- return 1
- })
- vm.PutPropString(obj, "getError")
-}
-
-// jsTracer provides an implementation of Tracer that evaluates a Javascript
-// function for each VM execution step.
-type jsTracer struct {
- vm *duktape.Context // Javascript VM instance
-
- tracerObject int // Stack index of the tracer JavaScript object
- stateObject int // Stack index of the global state to pull arguments from
-
- opWrapper *opWrapper // Wrapper around the VM opcode
- stackWrapper *stackWrapper // Wrapper around the VM stack
- memoryWrapper *memoryWrapper // Wrapper around the VM memory
- contractWrapper *contractWrapper // Wrapper around the contract object
- dbWrapper *dbWrapper // Wrapper around the VM environment
-
- pcValue *uint // Swappable pc value wrapped by a log accessor
- gasValue *uint // Swappable gas value wrapped by a log accessor
- costValue *uint // Swappable cost value wrapped by a log accessor
- depthValue *uint // Swappable depth value wrapped by a log accessor
- errorValue *string // Swappable error value wrapped by a log accessor
- refundValue *uint // Swappable refund value wrapped by a log accessor
-
- frame *frame // Represents entry into call frame. Fields are swappable
- frameResult *frameResult // Represents exit from a call frame. Fields are swappable
-
- ctx map[string]interface{} // Transaction context gathered throughout execution
- err error // Error, if one has occurred
-
- interrupt uint32 // Atomic flag to signal execution interruption
- reason error // Textual reason for the interruption
-
- activePrecompiles []common.Address // Updated on CaptureStart based on given rules
- traceSteps bool // When true, will invoke step() on each opcode
- traceCallFrames bool // When true, will invoke enter() and exit() js funcs
-}
-
-// Context contains some contextual infos for a transaction execution that is not
-// available from within the EVM object.
-type Context struct {
- BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call)
- TxIndex int // Index of the transaction within a block (zero if dangling tx or call)
- TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
-}
-
-// New instantiates a new tracer instance. code specifies a Javascript snippet,
-// which must evaluate to an expression returning an object with 'step', 'fault'
-// and 'result' functions.
-func newJsTracer(code string, ctx *Context) (*jsTracer, error) {
- tracer := &jsTracer{
- vm: duktape.New(),
- ctx: make(map[string]interface{}),
- opWrapper: new(opWrapper),
- stackWrapper: new(stackWrapper),
- memoryWrapper: new(memoryWrapper),
- contractWrapper: new(contractWrapper),
- dbWrapper: new(dbWrapper),
- pcValue: new(uint),
- gasValue: new(uint),
- costValue: new(uint),
- depthValue: new(uint),
- refundValue: new(uint),
- frame: newFrame(),
- frameResult: newFrameResult(),
- }
- if ctx.BlockHash != (common.Hash{}) {
- tracer.ctx["blockHash"] = ctx.BlockHash
-
- if ctx.TxHash != (common.Hash{}) {
- tracer.ctx["txIndex"] = ctx.TxIndex
- tracer.ctx["txHash"] = ctx.TxHash
- }
- }
- // Set up builtins for this environment
- tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
- ctx.PushString(hexutil.Encode(popSlice(ctx)))
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
- var word common.Hash
- if ptr, size := ctx.GetBuffer(-1); ptr != nil {
- word = common.BytesToHash(makeSlice(ptr, size))
- } else {
- word = common.HexToHash(ctx.GetString(-1))
- }
- ctx.Pop()
- copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
- var addr common.Address
- if ptr, size := ctx.GetBuffer(-1); ptr != nil {
- addr = common.BytesToAddress(makeSlice(ptr, size))
- } else {
- addr = common.HexToAddress(ctx.GetString(-1))
- }
- ctx.Pop()
- copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
- var from common.Address
- if ptr, size := ctx.GetBuffer(-2); ptr != nil {
- from = common.BytesToAddress(makeSlice(ptr, size))
- } else {
- from = common.HexToAddress(ctx.GetString(-2))
- }
- nonce := uint64(ctx.GetInt(-1))
- ctx.Pop2()
-
- contract := crypto.CreateAddress(from, nonce)
- copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int {
- var from common.Address
- if ptr, size := ctx.GetBuffer(-3); ptr != nil {
- from = common.BytesToAddress(makeSlice(ptr, size))
- } else {
- from = common.HexToAddress(ctx.GetString(-3))
- }
- // Retrieve salt hex string from js stack
- salt := common.HexToHash(ctx.GetString(-2))
- // Retrieve code slice from js stack
- var code []byte
- if ptr, size := ctx.GetBuffer(-1); ptr != nil {
- code = common.CopyBytes(makeSlice(ptr, size))
- } else {
- code = common.FromHex(ctx.GetString(-1))
- }
- codeHash := crypto.Keccak256(code)
- ctx.Pop3()
- contract := crypto.CreateAddress2(from, salt, codeHash)
- copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
- addr := common.BytesToAddress(popSlice(ctx))
- for _, p := range tracer.activePrecompiles {
- if p == addr {
- ctx.PushBoolean(true)
- return 1
- }
- }
- ctx.PushBoolean(false)
- return 1
- })
- tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
- start, end := ctx.GetInt(-2), ctx.GetInt(-1)
- ctx.Pop2()
-
- blob := popSlice(ctx)
- size := end - start
-
- if start < 0 || start > end || end > len(blob) {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
- ctx.PushFixedBuffer(0)
- return 1
- }
- copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
- return 1
- })
- // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
- if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
- log.Warn("Failed to compile tracer", "err", err)
- return nil, err
- }
- tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
-
- hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step")
- tracer.vm.Pop()
-
- if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
- return nil, fmt.Errorf("trace object must expose a function fault()")
- }
- tracer.vm.Pop()
-
- if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
- return nil, fmt.Errorf("trace object must expose a function result()")
- }
- tracer.vm.Pop()
-
- hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter")
- tracer.vm.Pop()
- hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit")
- tracer.vm.Pop()
- if hasEnter != hasExit {
- return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()")
- }
- tracer.traceCallFrames = hasEnter && hasExit
- tracer.traceSteps = hasStep
-
- // Tracer is valid, inject the big int library to access large numbers
- tracer.vm.EvalString(bigIntegerJS)
- tracer.vm.PutGlobalString("bigInt")
-
- // Push the global environment state as object #1 into the JSVM stack
- tracer.stateObject = tracer.vm.PushObject()
-
- logObject := tracer.vm.PushObject()
-
- tracer.opWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "op")
-
- tracer.stackWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "stack")
-
- tracer.memoryWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "memory")
-
- tracer.contractWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "contract")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
- tracer.vm.PutPropString(logObject, "getPC")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
- tracer.vm.PutPropString(logObject, "getGas")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
- tracer.vm.PutPropString(logObject, "getCost")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
- tracer.vm.PutPropString(logObject, "getDepth")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
- tracer.vm.PutPropString(logObject, "getRefund")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
- if tracer.errorValue != nil {
- ctx.PushString(*tracer.errorValue)
- } else {
- ctx.PushUndefined()
- }
- return 1
- })
- tracer.vm.PutPropString(logObject, "getError")
-
- tracer.vm.PutPropString(tracer.stateObject, "log")
-
- tracer.frame.pushObject(tracer.vm)
- tracer.vm.PutPropString(tracer.stateObject, "frame")
-
- tracer.frameResult.pushObject(tracer.vm)
- tracer.vm.PutPropString(tracer.stateObject, "frameResult")
-
- tracer.dbWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(tracer.stateObject, "db")
-
- return tracer, nil
-}
-
-// Stop terminates execution of the tracer at the first opportune moment.
-func (jst *jsTracer) Stop(err error) {
- jst.reason = err
- atomic.StoreUint32(&jst.interrupt, 1)
-}
-
-// call executes a method on a JS object, catching any errors, formatting and
-// returning them as error objects.
-func (jst *jsTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) {
- // Execute the JavaScript call and return any error
- jst.vm.PushString(method)
- for _, arg := range args {
- jst.vm.GetPropString(jst.stateObject, arg)
- }
- code := jst.vm.PcallProp(jst.tracerObject, len(args))
- defer jst.vm.Pop()
-
- if code != 0 {
- err := jst.vm.SafeToString(-1)
- return nil, errors.New(err)
- }
- // No error occurred, extract return value and return
- if noret {
- return nil, nil
- }
- // Push a JSON marshaller onto the stack. We can't marshal from the out-
- // side because duktape can crash on large nestings and we can't catch
- // C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?!
- jst.vm.PushString("(JSON.stringify)")
- jst.vm.Eval()
-
- jst.vm.Swap(-1, -2)
- if code = jst.vm.Pcall(1); code != 0 {
- err := jst.vm.SafeToString(-1)
- return nil, errors.New(err)
- }
- return json.RawMessage(jst.vm.SafeToString(-1)), nil
-}
-
-func wrapError(context string, err error) error {
- return fmt.Errorf("%v in server-side tracer function '%v'", err, context)
-}
-
-// CaptureStart implements the Tracer interface to initialize the tracing operation.
-func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
- jst.ctx["type"] = "CALL"
- if create {
- jst.ctx["type"] = "CREATE"
- }
- jst.ctx["from"] = from
- jst.ctx["to"] = to
- jst.ctx["input"] = input
- jst.ctx["gas"] = gas
- jst.ctx["gasPrice"] = env.TxContext.GasPrice
- jst.ctx["value"] = value
-
- // Initialize the context
- jst.ctx["block"] = env.Context.BlockNumber.Uint64()
- jst.dbWrapper.db = env.StateDB
- // Update list of precompiles based on current block
- rules := env.ChainConfig().Rules(env.Context.BlockNumber)
- jst.activePrecompiles = vm.ActivePrecompiles(rules)
-
- // Compute intrinsic gas
- isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
- isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
- intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
- if err != nil {
- return
- }
- jst.ctx["intrinsicGas"] = intrinsicGas
-}
-
-// CaptureState implements the Tracer interface to trace a single step of VM execution.
-func (jst *jsTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
- if !jst.traceSteps {
- return
- }
- if jst.err != nil {
- return
- }
- // If tracing was interrupted, set the error and stop
- if atomic.LoadUint32(&jst.interrupt) > 0 {
- jst.err = jst.reason
- env.Cancel()
- return
- }
- jst.opWrapper.op = op
- jst.stackWrapper.stack = scope.Stack
- jst.memoryWrapper.memory = scope.Memory
- jst.contractWrapper.contract = scope.Contract
-
- *jst.pcValue = uint(pc)
- *jst.gasValue = uint(gas)
- *jst.costValue = uint(cost)
- *jst.depthValue = uint(depth)
- *jst.refundValue = uint(env.StateDB.GetRefund())
-
- jst.errorValue = nil
- if err != nil {
- jst.errorValue = new(string)
- *jst.errorValue = err.Error()
- }
-
- if _, err := jst.call(true, "step", "log", "db"); err != nil {
- jst.err = wrapError("step", err)
- }
-}
-
-// CaptureFault implements the Tracer interface to trace an execution fault
-func (jst *jsTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
- if jst.err != nil {
- return
- }
- // Apart from the error, everything matches the previous invocation
- jst.errorValue = new(string)
- *jst.errorValue = err.Error()
-
- if _, err := jst.call(true, "fault", "log", "db"); err != nil {
- jst.err = wrapError("fault", err)
- }
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
- jst.ctx["output"] = output
- jst.ctx["time"] = t.String()
- jst.ctx["gasUsed"] = gasUsed
-
- if err != nil {
- jst.ctx["error"] = err.Error()
- }
-}
-
-// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
- if !jst.traceCallFrames {
- return
- }
- if jst.err != nil {
- return
- }
- // If tracing was interrupted, set the error and stop
- if atomic.LoadUint32(&jst.interrupt) > 0 {
- jst.err = jst.reason
- return
- }
-
- *jst.frame.typ = typ.String()
- *jst.frame.from = from
- *jst.frame.to = to
- jst.frame.input = common.CopyBytes(input)
- *jst.frame.gas = uint(gas)
- jst.frame.value = nil
- if value != nil {
- jst.frame.value = new(big.Int).SetBytes(value.Bytes())
- }
-
- if _, err := jst.call(true, "enter", "frame"); err != nil {
- jst.err = wrapError("enter", err)
- }
-}
-
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
-// execute any code.
-func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
- if !jst.traceCallFrames {
- return
- }
- // If tracing was interrupted, set the error and stop
- if atomic.LoadUint32(&jst.interrupt) > 0 {
- jst.err = jst.reason
- return
- }
-
- jst.frameResult.output = common.CopyBytes(output)
- *jst.frameResult.gasUsed = uint(gasUsed)
- jst.frameResult.errorValue = nil
- if err != nil {
- jst.frameResult.errorValue = new(string)
- *jst.frameResult.errorValue = err.Error()
- }
-
- if _, err := jst.call(true, "exit", "frameResult"); err != nil {
- jst.err = wrapError("exit", err)
- }
-}
-
-// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
-func (jst *jsTracer) GetResult() (json.RawMessage, error) {
- // Transform the context into a JavaScript object and inject into the state
- obj := jst.vm.PushObject()
-
- for key, val := range jst.ctx {
- jst.addToObj(obj, key, val)
- }
- jst.vm.PutPropString(jst.stateObject, "ctx")
-
- // Finalize the trace and return the results
- result, err := jst.call(false, "result", "ctx", "db")
- if err != nil {
- jst.err = wrapError("result", err)
- }
- // Clean up the JavaScript environment
- jst.vm.DestroyHeap()
- jst.vm.Destroy()
-
- return result, jst.err
-}
-
-// addToObj pushes a field to a JS object.
-func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {
- pushValue(jst.vm, val)
- jst.vm.PutPropString(obj, key)
-}
-
-func pushValue(ctx *duktape.Context, val interface{}) {
- switch val := val.(type) {
- case uint64:
- ctx.PushUint(uint(val))
- case string:
- ctx.PushString(val)
- case []byte:
- ptr := ctx.PushFixedBuffer(len(val))
- copy(makeSlice(ptr, uint(len(val))), val)
- case common.Address:
- ptr := ctx.PushFixedBuffer(20)
- copy(makeSlice(ptr, 20), val[:])
- case *big.Int:
- pushBigInt(val, ctx)
- case int:
- ctx.PushInt(val)
- case uint:
- ctx.PushUint(val)
- case common.Hash:
- ptr := ctx.PushFixedBuffer(32)
- copy(makeSlice(ptr, 32), val[:])
- default:
- panic(fmt.Sprintf("unsupported type: %T", val))
- }
-}
diff --git a/eth/tracers/internal/tracers/4byte_tracer_legacy.js b/eth/tracers/js/internal/tracers/4byte_tracer_legacy.js
similarity index 100%
rename from eth/tracers/internal/tracers/4byte_tracer_legacy.js
rename to eth/tracers/js/internal/tracers/4byte_tracer_legacy.js
diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/js/internal/tracers/assets.go
similarity index 92%
rename from eth/tracers/internal/tracers/assets.go
rename to eth/tracers/js/internal/tracers/assets.go
index 37e370240..caeccb7f3 100644
--- a/eth/tracers/internal/tracers/assets.go
+++ b/eth/tracers/js/internal/tracers/assets.go
@@ -1,6 +1,5 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
-// 4byte_tracer.js (2.224kB)
// 4byte_tracer_legacy.js (2.933kB)
// bigram_tracer.js (1.712kB)
// call_tracer_js.js (3.497kB)
@@ -79,26 +78,6 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil
}
-var __4byte_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x55\x5b\x6f\x22\x39\x13\x7d\x86\x5f\x71\xc4\x13\x68\x9a\x4b\x73\x09\x97\xf9\x32\x12\xdf\x28\x99\x41\xca\x66\x22\x42\x34\x8a\x56\xfb\x60\xda\xd5\xdd\xde\x18\xbb\x65\xbb\xb9\x6c\x26\xff\x7d\x65\x37\xe4\x36\xbb\xda\x79\x02\xec\xaa\x73\xaa\x4e\x1d\x17\xdd\x2e\x3e\xeb\xe2\x60\x44\x96\x3b\xf4\x7b\xf1\x18\xab\x9c\x90\xe9\x36\xb9\x9c\x0c\x95\x1b\xcc\x4b\x97\x6b\x63\xeb\xdd\x2e\x56\xb9\xb0\x48\x85\x24\x08\x8b\x82\x19\x07\x9d\xc2\xbd\x8b\x97\x62\x6d\x98\x39\x74\xea\xdd\x6e\x95\xf3\x8f\xd7\x1e\x21\x35\x44\xb0\x3a\x75\x3b\x66\x68\x86\x83\x2e\x91\x30\x05\x43\x5c\x58\x67\xc4\xba\x74\x04\xe1\xc0\x14\xef\x6a\x83\x8d\xe6\x22\x3d\x78\x48\xe1\x50\x2a\x4e\x26\x50\x3b\x32\x1b\x7b\xaa\xe3\xcb\xf5\x1d\xae\xc8\x5a\x32\xf8\x42\x8a\x0c\x93\xb8\x29\xd7\x52\x24\xb8\x12\x09\x29\x4b\x60\x16\x85\x3f\xb1\x39\x71\xac\x03\x9c\x4f\xbc\xf4\xa5\xdc\x1e\x4b\xc1\xa5\x2e\x15\x67\x4e\x68\x15\x81\x84\xaf\x1c\x5b\x32\x56\x68\x85\xc1\x89\xea\x08\x18\x41\x1b\x0f\xd2\x64\xce\x37\x60\xa0\x0b\x9f\xd7\x02\x53\x07\x48\xe6\x5e\x52\x7f\x41\x90\x97\xbe\x39\x84\x0a\x34\xb9\x2e\x08\x2e\x67\xce\x77\xbd\x13\x52\x62\x4d\x28\x2d\xa5\xa5\x8c\x3c\xda\xba\x74\xf8\xbe\x58\x7d\xfd\x76\xb7\xc2\xfc\xfa\x1e\xdf\xe7\xcb\xe5\xfc\x7a\x75\xff\x11\x3b\xe1\x72\x5d\x3a\xd0\x96\x2a\x28\xb1\x29\xa4\x20\x8e\x1d\x33\x86\x29\x77\x80\x4e\x3d\xc2\x6f\x17\xcb\xcf\x5f\xe7\xd7\xab\xf9\xff\x17\x57\x8b\xd5\x3d\xb4\xc1\xe5\x62\x75\x7d\x71\x7b\x8b\xcb\x6f\x4b\xcc\x71\x33\x5f\xae\x16\x9f\xef\xae\xe6\x4b\xdc\xdc\x2d\x6f\xbe\xdd\x5e\x74\x70\x4b\xbe\x2a\xf2\xf9\xff\xad\x79\x1a\xa6\x67\x08\x9c\x1c\x13\xd2\x9e\x94\xb8\xd7\x25\x6c\xae\x4b\xc9\x91\xb3\x2d\xc1\x50\x42\x62\x4b\x1c\x0c\x89\x2e\x0e\xbf\x3c\x54\x8f\xc5\xa4\x56\x59\xe8\xf9\x5f\x0d\x89\x45\x0a\xa5\x5d\x04\x4b\x84\xff\xe5\xce\x15\xb3\x6e\x77\xb7\xdb\x75\x32\x55\x76\xb4\xc9\xba\xb2\x82\xb3\xdd\x4f\x9d\xba\xc7\x1c\xae\x0f\x8e\x56\x86\x25\x64\x60\x89\x99\x24\x27\x1b\x9a\x09\x17\x6d\xc1\x49\x39\x91\x0a\x32\x36\xf2\x26\x45\xa2\xa5\xa4\xc4\x59\x5f\xc1\x26\x04\x16\xda\xba\x76\x61\x74\x42\xd6\x0a\x95\xf9\xc6\xb1\x70\x6f\x02\xb1\x21\x97\x6b\x6e\xf1\x0a\xee\x7d\x37\x56\xfc\x45\x27\x35\x6c\x59\x54\x63\xe4\xcc\xb1\x08\x56\x87\xee\x61\xc8\xdb\x8c\x38\xac\xc8\x14\x73\xa5\xa1\xf0\x96\xd6\x84\x0d\x73\x89\x37\x3b\xcb\x98\x50\xd6\xfd\x04\xe8\x71\x4e\x13\xb9\xd8\xb3\x4d\x21\x69\xe6\xbf\x03\x9f\xc0\x69\x5d\x66\x1d\xe7\x25\x58\x19\xa6\x2c\x4b\xbc\xb9\x9b\x68\xf4\xf6\xfd\x78\x48\xa3\xe9\x98\x06\x23\xce\x7a\x93\xc1\xd9\xb4\x9f\x8e\x06\x93\xb3\x78\x18\xd3\xd9\x34\x1d\x8e\x69\x3a\x1e\xac\xfb\xc9\xe8\x8c\xc6\x6c\xd2\x1b\x0f\xd6\x31\xb1\xde\x24\xe5\xe3\xd1\x38\xa6\x29\xa7\x46\x84\xc7\x00\x6c\x66\x68\xbc\x52\xba\xf1\xd4\xaa\xd8\x1f\xab\x0f\xa0\xb7\xef\x8f\x79\xd2\x9f\x8e\xa9\x1d\xf7\x27\x33\xc4\xd1\xcb\xcd\x60\x92\x24\xc3\xc9\x20\x6e\xf7\x66\xe8\xbf\x3a\x1f\xf5\x87\xe9\x60\x32\x99\xb6\xa7\x67\x6f\x13\x18\x4f\x47\xd3\x74\x3a\x6d\xf7\x27\xef\xa0\x92\xfe\x24\xe6\xf1\x94\x3c\x54\x5c\x1d\x3f\xd5\x1f\xeb\x35\xbf\x70\xb8\x05\xcb\x32\x43\x19\x73\x54\x4d\x2d\x54\x1c\x2e\x52\xbf\x2c\x3a\xf5\x9a\xff\x3e\xc3\xe3\x53\x54\x0f\x39\xd6\x79\xc7\x5b\xef\xeb\x60\x48\xe1\x9f\xa1\x50\xcf\x43\x0e\x8e\xf1\xda\xfb\x59\x74\xea\xb5\x10\x3f\x43\x5a\xaa\x4a\x63\xc1\xa3\x30\xa6\xd6\x63\xbd\x56\xdb\x32\x83\x07\x3a\xe0\x1c\x8d\x06\x3e\xc0\xe9\xaf\xb4\x6f\x0a\xde\xc2\x07\x34\xda\xfe\xc4\x47\x7e\xac\xd7\x6a\x2e\x17\xb6\x23\xb8\xfd\xfd\x81\x0e\x7f\xe0\x1c\x6f\x7f\x7f\x40\x8c\x1f\x3f\x10\x7f\xac\xd7\x42\x99\xa4\x9c\x97\xff\x99\x33\x35\x6c\x43\x2d\x78\xc6\x6e\x17\xb7\x0f\xa2\x08\x6b\xac\x30\xd4\x4e\xf4\xa6\x08\x8b\x5f\x6d\x75\x12\x56\xa3\x8d\xe0\x72\xed\x57\xaa\x21\xfc\x59\x5a\x87\x94\xa9\xe4\x00\x5d\x24\x9a\x93\xad\xd7\x6a\x22\x45\x53\xd8\x1b\x43\xc7\x64\x5e\x11\x74\x32\x72\x2b\xdd\x6c\xb5\x2a\xa6\x9a\x21\x57\x1a\xe5\xab\x7f\x3a\xb6\x2a\x54\x51\x3a\x9c\xe3\x39\x7c\xe1\x0f\x9a\xad\x13\xa6\xff\xd5\x91\xa4\x32\x97\xe3\xd3\x39\x86\x47\xa0\xd0\x6c\xd0\xb1\x69\xfd\x5b\xae\x02\x23\xf4\x22\x0c\x5b\x11\xde\xa4\xb5\x31\x6c\x1d\x29\x2b\x29\xf6\xc2\xbd\x57\x62\x49\xb6\x94\xae\xf5\x32\xd3\x94\x95\xd2\xf9\x45\xed\x55\x78\xf0\xab\x34\x3f\xee\x56\x96\xb8\x92\x49\xd0\x9e\x92\xd2\x03\xf8\xc7\xc5\xd4\x51\x0b\xa4\xd5\xd6\xab\x85\xfc\x57\x2c\x52\x67\x11\xf8\xfa\x15\x83\x09\x94\x3f\x51\x30\x29\x03\xcd\x51\xdb\x6a\x5d\xae\xc9\x3b\xca\x91\x61\xfe\xff\x42\x6f\x8f\x9e\xaa\xe4\xb4\x01\xce\xe7\xa4\x42\x31\x79\x02\x3e\xbe\x79\xff\xf0\xc2\x3e\xaa\x55\xe7\xaf\x6a\x4a\xdc\xfe\xc5\x01\x27\xf7\xea\xd2\xff\x91\x25\x4c\x4a\xef\x58\x30\x69\xf5\x71\x16\x89\xdb\x77\x7e\x79\x1e\xcf\xc1\xcf\x33\x79\x9f\xde\x1e\xb6\x8e\x3e\xa8\xda\x78\x36\x70\x65\xd9\xa7\xfa\xdf\x01\x00\x00\xff\xff\xf6\xa8\xa1\xb9\xb0\x08\x00\x00")
-
-func _4byte_tracerJsBytes() ([]byte, error) {
- return bindataRead(
- __4byte_tracerJs,
- "4byte_tracer.js",
- )
-}
-
-func _4byte_tracerJs() (*asset, error) {
- bytes, err := _4byte_tracerJsBytes()
- if err != nil {
- return nil, err
- }
-
- info := bindataFileInfo{name: "4byte_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
- a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6b, 0xa8, 0x46, 0xa2, 0x3a, 0x2b, 0xaa, 0xb9, 0xb9, 0xba, 0xe2, 0x22, 0x10, 0xe, 0xe7, 0x4c, 0x24, 0xfc, 0x4c, 0x85, 0xeb, 0x96, 0x48, 0xe8, 0x7f, 0xc8, 0xe0, 0xd0, 0xd, 0x26, 0xa1, 0xb2}}
- return a, nil
-}
-
var __4byte_tracer_legacyJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x56\x5b\x6f\xdb\x4a\x0e\x7e\xb6\x7f\x05\xd7\x2f\xb5\x51\x59\x8e\x2f\x89\x2f\xd9\x16\xf0\xe6\xa4\x6d\x80\x9c\x24\x88\xdd\x3d\x28\x16\xfb\x30\x9e\xa1\xac\xd9\xc8\x33\xc2\x0c\xe5\x4b\x73\xf2\xdf\x17\x1c\x49\x89\x93\xd3\x62\xbb\x4f\x96\x47\xc3\x8f\x1f\xc9\x8f\xa4\x7a\x3d\xb8\xb0\xf9\xc1\xe9\x75\x4a\x30\x38\xe9\x8f\x61\x99\x22\xac\x6d\x17\x29\x45\x87\xc5\x06\xe6\x05\xa5\xd6\xf9\x66\xaf\x07\xcb\x54\x7b\x48\x74\x86\xa0\x3d\xe4\xc2\x11\xd8\x04\xe8\xcd\xfd\x4c\xaf\x9c\x70\x87\xb8\xd9\xeb\x95\x36\x3f\x7c\xcd\x08\x89\x43\x04\x6f\x13\xda\x09\x87\x33\x38\xd8\x02\xa4\x30\xe0\x50\x69\x4f\x4e\xaf\x0a\x42\xd0\x04\xc2\xa8\x9e\x75\xb0\xb1\x4a\x27\x07\x86\xd4\x04\x85\x51\xe8\x82\x6b\x42\xb7\xf1\x35\x8f\xcf\x37\x5f\xe1\x1a\xbd\x47\x07\x9f\xd1\xa0\x13\x19\xdc\x15\xab\x4c\x4b\xb8\xd6\x12\x8d\x47\x10\x1e\x72\x3e\xf1\x29\x2a\x58\x05\x38\x36\xfc\xc4\x54\x16\x15\x15\xf8\x64\x0b\xa3\x04\x69\x6b\x22\x40\xcd\xcc\x61\x8b\xce\x6b\x6b\x60\x58\xbb\xaa\x00\x23\xb0\x8e\x41\xda\x82\x38\x00\x07\x36\x67\xbb\x0e\x08\x73\x80\x4c\xd0\x8b\xe9\x2f\x24\xe4\x25\x6e\x05\xda\x04\x37\xa9\xcd\x11\x28\x15\xc4\x51\xef\x74\x96\xc1\x0a\xa1\xf0\x98\x14\x59\xc4\x68\xab\x82\xe0\x8f\xab\xe5\x97\xdb\xaf\x4b\x98\xdf\x7c\x83\x3f\xe6\xf7\xf7\xf3\x9b\xe5\xb7\x73\xd8\x69\x4a\x6d\x41\x80\x5b\x2c\xa1\xf4\x26\xcf\x34\x2a\xd8\x09\xe7\x84\xa1\x03\xd8\x84\x11\x7e\xbf\xbc\xbf\xf8\x32\xbf\x59\xce\xff\x71\x75\x7d\xb5\xfc\x06\xd6\xc1\xa7\xab\xe5\xcd\xe5\x62\x01\x9f\x6e\xef\x61\x0e\x77\xf3\xfb\xe5\xd5\xc5\xd7\xeb\xf9\x3d\xdc\x7d\xbd\xbf\xbb\x5d\x5c\xc6\xb0\x40\x66\x85\x6c\xff\xbf\x73\x9e\x84\xea\x39\x04\x85\x24\x74\xe6\xeb\x4c\x7c\xb3\x05\xf8\xd4\x16\x99\x82\x54\x6c\x11\x1c\x4a\xd4\x5b\x54\x20\x40\xda\xfc\xf0\xcb\x45\x65\x2c\x91\x59\xb3\x0e\x31\xff\x54\x90\x70\x95\x80\xb1\x14\x81\x47\x84\xbf\xa7\x44\xf9\xac\xd7\xdb\xed\x76\xf1\xda\x14\xb1\x75\xeb\x5e\x56\xc2\xf9\xde\xc7\xb8\xc9\x98\xa3\xd5\x81\x70\xe9\x84\x44\x07\x1e\x85\x93\x29\xfa\x10\x4c\x78\xd1\xd5\x0a\x0d\xe9\x44\xa3\xf3\x11\x8b\x14\xa4\xcd\x32\x94\xe4\x99\xc1\x26\x5c\xcc\xad\xa7\x6e\xee\xac\x44\xef\xb5\x59\x73\xe0\x70\x45\xaf\x2e\xc2\x06\x29\xb5\xca\xc3\x11\xdc\xdb\x68\xbc\xfe\x8e\x75\x36\x7c\x91\x97\x65\x54\x82\x44\x04\xde\x86\xe8\xc1\x21\xcb\x0c\x15\x78\xbd\x36\x82\x0a\x87\xa1\x97\x56\x08\x1b\x41\x92\xc5\x2e\xd6\x42\x1b\x4f\x7f\x01\x64\x9c\xba\x22\x97\x7b\xb1\xc9\x33\x9c\xf1\x33\xc0\x47\x50\xb8\x2a\xd6\x31\x71\x0a\x96\x4e\x18\x2f\x24\x8b\xbb\x0d\xad\x93\xfd\xa0\x3f\xc2\xd3\xe9\x18\x87\xa7\x4a\x9c\x4c\x86\x67\xd3\x41\x72\x3a\x9c\x9c\xf5\x47\x7d\x3c\x9b\x26\xa3\x31\x4e\xc7\xc3\xd5\x40\x9e\x9e\xe1\x58\x4c\x4e\xc6\xc3\x55\x1f\xc5\xc9\x24\x51\xe3\xd3\x71\x1f\xa7\x0a\x5b\x11\x3c\x06\x60\x37\x83\xd6\x51\xa6\x5b\x4f\x9d\xd2\xfb\x63\xf9\x03\x70\xb2\x1f\x8c\x95\x1c\x4c\xc7\xd8\xed\x0f\x26\x33\xe8\x47\x2f\x6f\x86\x13\x29\x47\x93\x61\xbf\x7b\x32\x83\xc1\xd1\xf9\xe9\x60\x94\x0c\x27\x93\x69\x77\x7a\xf6\xda\x40\xa8\xe4\x74\x9a\x4c\xa7\xdd\xc1\xe4\x0d\x94\x1c\x4c\xfa\xaa\x3f\x45\x86\xea\x97\xc7\x4f\xcd\xc7\x66\x83\x07\x8e\xf2\x20\xd6\x6b\x87\x6b\x41\x58\x56\x2d\x30\x0e\x2f\x12\x1e\x16\x71\xb3\xc1\xcf\x33\x78\x7c\x8a\x9a\xc1\x46\x8a\x2c\x5b\x1e\x72\x56\x35\x15\xce\x78\x78\x97\x88\xcc\xe3\xbb\xa0\x0b\x63\x4d\x97\x2f\x78\x1e\x1f\x01\x2f\x47\x7c\xe8\x6a\xa3\x70\x1f\x2e\xf0\x51\xa2\x9d\x27\x1e\xb3\x62\x13\x10\x45\xc2\xd3\xe4\xdd\x56\x64\x05\xbe\x8b\x40\xc7\x18\xc3\x06\x37\x5c\x54\xe1\x28\x6e\x36\x6a\x97\x33\x48\x0a\x53\x56\xca\xe6\x9e\x5c\xe7\xb1\xd9\x68\xf8\x9d\x26\x99\x1e\x1d\x48\xe1\x11\x5a\x17\xf3\xeb\xeb\xd6\x0c\x5e\xfe\x5c\xdc\xfe\x76\xd9\x9a\x35\x1b\x0d\x76\xb9\x16\x2c\x6d\xa5\x5c\x04\x5b\x91\x45\xa5\xbb\xea\xc7\x7f\x0f\x0f\xb6\xa0\xfa\xd7\x7f\x67\xb3\x32\x5e\x18\x9e\x43\xaf\x07\x9e\x84\x7c\x80\x9c\x1c\x90\x2d\xcd\x9a\xcf\xae\x7f\xbb\xbc\xbe\xfc\x3c\x5f\x5e\xbe\xa2\xb0\x58\xce\x97\x57\x17\xe5\xd1\x5f\x49\xfc\x1f\xfe\x07\x3f\xf3\xdf\x68\x3c\x35\x9f\x6f\x85\x9a\x9c\x37\x1b\x75\xd5\x3c\xf1\x9c\xf2\x3c\x8d\xc2\x18\xd1\x3c\x3c\xb9\x2c\x55\x6b\x86\x3e\xe7\x8e\xe1\x0e\x8a\x9b\x8d\x70\xff\x28\xdf\x5a\x45\xa1\xb9\x42\x86\xb7\xc2\xc1\x03\x1e\xe0\x03\xb4\x5a\xf0\x1e\xc8\x7e\xc1\x7d\x5b\xab\x0e\xbc\x87\x56\x97\x4f\xf8\xe6\x79\xb3\xd1\xa0\x54\xfb\x58\x2b\xff\xaf\x07\x3c\xfc\x1b\x3e\xc0\xeb\xff\xef\xa1\x0f\x7f\xfe\x09\xfd\x57\x34\x31\xe7\x85\xa1\xcd\xd6\x3e\xa0\x0a\x92\xe1\x01\x70\x00\x9b\x4b\xab\xaa\x8d\xc1\x11\xfc\xf3\x77\xc0\x3d\xca\x82\xd0\x07\xba\x98\x1f\xb1\xcd\xec\x3a\x02\xb5\xea\x00\xb3\xed\xf5\x60\xf1\xa0\xf3\xb0\xb8\x4a\x14\x5f\xc2\xf0\x46\x34\x96\x40\x1b\x42\x67\x44\x16\xa4\xed\xab\xf8\x24\xd5\x7c\x6b\xf5\x31\x6a\x6c\xf3\x98\xec\x82\x9c\x36\xeb\x76\xa7\xc3\x31\xea\x04\xda\x7f\x93\x54\xfa\xaa\xd2\x7f\x5e\x15\xe3\xd8\x75\xee\xb0\x2b\xed\x26\x0f\x5f\x19\x66\x6b\x65\xd8\xc3\x3e\x02\x4a\x2d\xef\x6f\x87\xf0\x9f\xc2\x13\x24\xc2\xc8\x67\xa2\x15\xbe\xf6\x77\x0e\x2b\x63\xd5\x26\x3b\x57\xca\xa1\xf7\x81\x51\x50\x42\xcc\x6d\xd6\xee\x77\x5e\xc8\xf5\xcf\x3a\x9d\xce\xcf\x48\x7d\x16\x61\xf7\xbf\x0a\xbc\x5e\x62\x55\xfc\xda\x2c\xbe\xc3\x07\x78\xe3\x41\x12\x57\xad\x13\x87\x5e\xbd\x4d\xda\xcf\x19\x08\xd7\x3f\x7e\x80\x51\xe5\xb2\x84\xb8\x4d\x92\x1f\x61\xbc\xb1\x2f\x65\x12\x14\x17\x22\x62\xd1\xbb\x43\xec\x79\x6d\xb5\x03\x48\x54\x61\xbd\x87\x51\x27\x0a\xd4\xba\xa3\x4e\x15\x4f\x2d\x9d\x44\x14\x19\x1d\x6b\x67\x97\x56\xdf\x07\x42\x52\x21\xb2\x4a\x2e\xfc\xad\x63\x13\x10\xa6\x56\x54\x52\x6e\xee\x46\xb0\xff\xa1\x86\xa0\x76\xe1\xd0\xff\xc8\x07\x27\x8f\xfd\xd4\xe2\x0a\x3b\x7f\x85\xdc\x60\x84\x4e\xf0\x47\x8f\xdd\x56\x2d\x56\x0d\xcd\x00\x57\xce\x42\xce\x7f\x05\x5c\x2d\x2e\xde\x1e\x61\xa9\x36\xca\xf3\x23\x52\x92\xf6\x2f\xa2\xae\x9b\xd9\x16\x3c\x3f\xb9\x86\xdc\xc0\x20\x32\x6f\xab\xaa\x48\xda\xc7\xda\xe4\x05\xc5\x19\x9a\x35\xa5\xc7\x15\x3a\x4a\x7a\x99\xe9\xe7\xcb\x11\x9c\x44\x21\xd1\x6f\xcd\xbb\xa3\xce\xeb\x29\x53\xf7\x73\xd9\xc1\x4f\xcd\xff\x06\x00\x00\xff\xff\x8e\xc8\x27\x72\x75\x0b\x00\x00")
func _4byte_tracer_legacyJsBytes() ([]byte, error) {
@@ -390,7 +369,6 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
- "4byte_tracer.js": _4byte_tracerJs,
"4byte_tracer_legacy.js": _4byte_tracer_legacyJs,
"bigram_tracer.js": bigram_tracerJs,
"call_tracer_js.js": call_tracer_jsJs,
@@ -447,7 +425,6 @@ type bintree struct {
}
var _bintree = &bintree{nil, map[string]*bintree{
- "4byte_tracer.js": {_4byte_tracerJs, map[string]*bintree{}},
"4byte_tracer_legacy.js": {_4byte_tracer_legacyJs, map[string]*bintree{}},
"bigram_tracer.js": {bigram_tracerJs, map[string]*bintree{}},
"call_tracer_js.js": {call_tracer_jsJs, map[string]*bintree{}},
diff --git a/eth/tracers/internal/tracers/bigram_tracer.js b/eth/tracers/js/internal/tracers/bigram_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/bigram_tracer.js
rename to eth/tracers/js/internal/tracers/bigram_tracer.js
diff --git a/eth/tracers/internal/tracers/call_tracer_js.js b/eth/tracers/js/internal/tracers/call_tracer_js.js
similarity index 100%
rename from eth/tracers/internal/tracers/call_tracer_js.js
rename to eth/tracers/js/internal/tracers/call_tracer_js.js
diff --git a/eth/tracers/internal/tracers/call_tracer_legacy.js b/eth/tracers/js/internal/tracers/call_tracer_legacy.js
similarity index 100%
rename from eth/tracers/internal/tracers/call_tracer_legacy.js
rename to eth/tracers/js/internal/tracers/call_tracer_legacy.js
diff --git a/eth/tracers/internal/tracers/evmdis_tracer.js b/eth/tracers/js/internal/tracers/evmdis_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/evmdis_tracer.js
rename to eth/tracers/js/internal/tracers/evmdis_tracer.js
diff --git a/eth/tracers/internal/tracers/noop_tracer.js b/eth/tracers/js/internal/tracers/noop_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/noop_tracer.js
rename to eth/tracers/js/internal/tracers/noop_tracer.js
diff --git a/eth/tracers/internal/tracers/opcount_tracer.js b/eth/tracers/js/internal/tracers/opcount_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/opcount_tracer.js
rename to eth/tracers/js/internal/tracers/opcount_tracer.js
diff --git a/eth/tracers/internal/tracers/prestate_tracer.js b/eth/tracers/js/internal/tracers/prestate_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/prestate_tracer.js
rename to eth/tracers/js/internal/tracers/prestate_tracer.js
diff --git a/eth/tracers/internal/tracers/tracers.go b/eth/tracers/js/internal/tracers/tracers.go
similarity index 100%
rename from eth/tracers/internal/tracers/tracers.go
rename to eth/tracers/js/internal/tracers/tracers.go
diff --git a/eth/tracers/internal/tracers/trigram_tracer.js b/eth/tracers/js/internal/tracers/trigram_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/trigram_tracer.js
rename to eth/tracers/js/internal/tracers/trigram_tracer.js
diff --git a/eth/tracers/internal/tracers/unigram_tracer.js b/eth/tracers/js/internal/tracers/unigram_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/unigram_tracer.js
rename to eth/tracers/js/internal/tracers/unigram_tracer.js
diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go
new file mode 100644
index 000000000..b8e035e6f
--- /dev/null
+++ b/eth/tracers/js/tracer.go
@@ -0,0 +1,880 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// package js is a collection of tracers written in javascript.
+package js
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
+ "strings"
+ "sync/atomic"
+ "time"
+ "unicode"
+ "unsafe"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ tracers2 "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
+ "github.com/ethereum/go-ethereum/log"
+ "gopkg.in/olebedev/go-duktape.v3"
+)
+
+// camel converts a snake cased input string into a camel cased output.
+func camel(str string) string {
+ pieces := strings.Split(str, "_")
+ for i := 1; i < len(pieces); i++ {
+ pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
+ }
+ return strings.Join(pieces, "")
+}
+
+var assetTracers = make(map[string]string)
+
+// init retrieves the JavaScript transaction tracers included in go-ethereum.
+func init() {
+ for _, file := range tracers.AssetNames() {
+ name := camel(strings.TrimSuffix(file, ".js"))
+ assetTracers[name] = string(tracers.MustAsset(file))
+ }
+ tracers2.RegisterLookup(true, newJsTracer)
+}
+
+// makeSlice convert an unsafe memory pointer with the given type into a Go byte
+// slice.
+//
+// Note, the returned slice uses the same memory area as the input arguments.
+// If those are duktape stack items, popping them off **will** make the slice
+// contents change.
+func makeSlice(ptr unsafe.Pointer, size uint) []byte {
+ var sl = struct {
+ addr uintptr
+ len int
+ cap int
+ }{uintptr(ptr), int(size), int(size)}
+
+ return *(*[]byte)(unsafe.Pointer(&sl))
+}
+
+// popSlice pops a buffer off the JavaScript stack and returns it as a slice.
+func popSlice(ctx *duktape.Context) []byte {
+ blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
+ ctx.Pop()
+ return blob
+}
+
+// pushBigInt create a JavaScript BigInteger in the VM.
+func pushBigInt(n *big.Int, ctx *duktape.Context) {
+ ctx.GetGlobalString("bigInt")
+ ctx.PushString(n.String())
+ ctx.Call(1)
+}
+
+// opWrapper provides a JavaScript wrapper around OpCode.
+type opWrapper struct {
+ op vm.OpCode
+}
+
+// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
+// onto the VM stack.
+func (ow *opWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
+ vm.PutPropString(obj, "toNumber")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
+ vm.PutPropString(obj, "toString")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
+ vm.PutPropString(obj, "isPush")
+}
+
+// memoryWrapper provides a JavaScript wrapper around vm.Memory.
+type memoryWrapper struct {
+ memory *vm.Memory
+}
+
+// slice returns the requested range of memory as a byte slice.
+func (mw *memoryWrapper) slice(begin, end int64) []byte {
+ if end == begin {
+ return []byte{}
+ }
+ if end < begin || begin < 0 {
+ // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end)
+ return nil
+ }
+ if mw.memory.Len() < int(end) {
+ // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
+ return nil
+ }
+ return mw.memory.GetCopy(begin, end-begin)
+}
+
+// getUint returns the 32 bytes at the specified address interpreted as a uint.
+func (mw *memoryWrapper) getUint(addr int64) *big.Int {
+ if mw.memory.Len() < int(addr)+32 || addr < 0 {
+ // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
+ return new(big.Int)
+ }
+ return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
+}
+
+// pushObject assembles a JSVM object wrapping a swappable memory and pushes it
+// onto the VM stack.
+func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ // Generate the `slice` method which takes two ints and returns a buffer
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
+ ctx.Pop2()
+
+ ptr := ctx.PushFixedBuffer(len(blob))
+ copy(makeSlice(ptr, uint(len(blob))), blob)
+ return 1
+ })
+ vm.PutPropString(obj, "slice")
+
+ // Generate the `getUint` method which takes an int and returns a bigint
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ offset := int64(ctx.GetInt(-1))
+ ctx.Pop()
+
+ pushBigInt(mw.getUint(offset), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "getUint")
+}
+
+// stackWrapper provides a JavaScript wrapper around vm.Stack.
+type stackWrapper struct {
+ stack *vm.Stack
+}
+
+// peek returns the nth-from-the-top element of the stack.
+func (sw *stackWrapper) peek(idx int) *big.Int {
+ if len(sw.stack.Data()) <= idx || idx < 0 {
+ // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
+ return new(big.Int)
+ }
+ return sw.stack.Back(idx).ToBig()
+}
+
+// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
+// onto the VM stack.
+func (sw *stackWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
+ vm.PutPropString(obj, "length")
+
+ // Generate the `peek` method which takes an int and returns a bigint
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ offset := ctx.GetInt(-1)
+ ctx.Pop()
+
+ pushBigInt(sw.peek(offset), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "peek")
+}
+
+// dbWrapper provides a JavaScript wrapper around vm.Database.
+type dbWrapper struct {
+ db vm.StateDB
+}
+
+// pushObject assembles a JSVM object wrapping a swappable database and pushes it
+// onto the VM stack.
+func (dw *dbWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ // Push the wrapper for statedb.GetBalance
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "getBalance")
+
+ // Push the wrapper for statedb.GetNonce
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
+ return 1
+ })
+ vm.PutPropString(obj, "getNonce")
+
+ // Push the wrapper for statedb.GetCode
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
+
+ ptr := ctx.PushFixedBuffer(len(code))
+ copy(makeSlice(ptr, uint(len(code))), code)
+ return 1
+ })
+ vm.PutPropString(obj, "getCode")
+
+ // Push the wrapper for statedb.GetState
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ hash := popSlice(ctx)
+ addr := popSlice(ctx)
+
+ state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
+
+ ptr := ctx.PushFixedBuffer(len(state))
+ copy(makeSlice(ptr, uint(len(state))), state[:])
+ return 1
+ })
+ vm.PutPropString(obj, "getState")
+
+ // Push the wrapper for statedb.Exists
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
+ return 1
+ })
+ vm.PutPropString(obj, "exists")
+}
+
+// contractWrapper provides a JavaScript wrapper around vm.Contract
+type contractWrapper struct {
+ contract *vm.Contract
+}
+
+// pushObject assembles a JSVM object wrapping a swappable contract and pushes it
+// onto the VM stack.
+func (cw *contractWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ // Push the wrapper for contract.Caller
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ptr := ctx.PushFixedBuffer(20)
+ copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
+ return 1
+ })
+ vm.PutPropString(obj, "getCaller")
+
+ // Push the wrapper for contract.Address
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ptr := ctx.PushFixedBuffer(20)
+ copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
+ return 1
+ })
+ vm.PutPropString(obj, "getAddress")
+
+ // Push the wrapper for contract.Value
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ pushBigInt(cw.contract.Value(), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "getValue")
+
+ // Push the wrapper for contract.Input
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ blob := cw.contract.Input
+
+ ptr := ctx.PushFixedBuffer(len(blob))
+ copy(makeSlice(ptr, uint(len(blob))), blob)
+ return 1
+ })
+ vm.PutPropString(obj, "getInput")
+}
+
+type frame struct {
+ typ *string
+ from *common.Address
+ to *common.Address
+ input []byte
+ gas *uint
+ value *big.Int
+}
+
+func newFrame() *frame {
+ return &frame{
+ typ: new(string),
+ from: new(common.Address),
+ to: new(common.Address),
+ gas: new(uint),
+ }
+}
+
+func (f *frame) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 })
+ vm.PutPropString(obj, "getType")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 })
+ vm.PutPropString(obj, "getFrom")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 })
+ vm.PutPropString(obj, "getTo")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 })
+ vm.PutPropString(obj, "getInput")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 })
+ vm.PutPropString(obj, "getGas")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ if f.value != nil {
+ pushValue(ctx, f.value)
+ } else {
+ ctx.PushUndefined()
+ }
+ return 1
+ })
+ vm.PutPropString(obj, "getValue")
+}
+
+type frameResult struct {
+ gasUsed *uint
+ output []byte
+ errorValue *string
+}
+
+func newFrameResult() *frameResult {
+ return &frameResult{
+ gasUsed: new(uint),
+ }
+}
+
+func (r *frameResult) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 })
+ vm.PutPropString(obj, "getGasUsed")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 })
+ vm.PutPropString(obj, "getOutput")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ if r.errorValue != nil {
+ pushValue(ctx, *r.errorValue)
+ } else {
+ ctx.PushUndefined()
+ }
+ return 1
+ })
+ vm.PutPropString(obj, "getError")
+}
+
+// jsTracer provides an implementation of Tracer that evaluates a Javascript
+// function for each VM execution step.
+type jsTracer struct {
+ vm *duktape.Context // Javascript VM instance
+ env *vm.EVM // EVM instance executing the code being traced
+
+ tracerObject int // Stack index of the tracer JavaScript object
+ stateObject int // Stack index of the global state to pull arguments from
+
+ opWrapper *opWrapper // Wrapper around the VM opcode
+ stackWrapper *stackWrapper // Wrapper around the VM stack
+ memoryWrapper *memoryWrapper // Wrapper around the VM memory
+ contractWrapper *contractWrapper // Wrapper around the contract object
+ dbWrapper *dbWrapper // Wrapper around the VM environment
+
+ pcValue *uint // Swappable pc value wrapped by a log accessor
+ gasValue *uint // Swappable gas value wrapped by a log accessor
+ costValue *uint // Swappable cost value wrapped by a log accessor
+ depthValue *uint // Swappable depth value wrapped by a log accessor
+ errorValue *string // Swappable error value wrapped by a log accessor
+ refundValue *uint // Swappable refund value wrapped by a log accessor
+
+ frame *frame // Represents entry into call frame. Fields are swappable
+ frameResult *frameResult // Represents exit from a call frame. Fields are swappable
+
+ ctx map[string]interface{} // Transaction context gathered throughout execution
+ err error // Error, if one has occurred
+
+ interrupt uint32 // Atomic flag to signal execution interruption
+ reason error // Textual reason for the interruption
+
+ activePrecompiles []common.Address // Updated on CaptureStart based on given rules
+ traceSteps bool // When true, will invoke step() on each opcode
+ traceCallFrames bool // When true, will invoke enter() and exit() js funcs
+}
+
+// New instantiates a new tracer instance. code specifies a Javascript snippet,
+// which must evaluate to an expression returning an object with 'step', 'fault'
+// and 'result' functions.
+func newJsTracer(code string, ctx *tracers2.Context) (tracers2.Tracer, error) {
+ if c, ok := assetTracers[code]; ok {
+ code = c
+ }
+ if ctx == nil {
+ ctx = new(tracers2.Context)
+ }
+ tracer := &jsTracer{
+ vm: duktape.New(),
+ ctx: make(map[string]interface{}),
+ opWrapper: new(opWrapper),
+ stackWrapper: new(stackWrapper),
+ memoryWrapper: new(memoryWrapper),
+ contractWrapper: new(contractWrapper),
+ dbWrapper: new(dbWrapper),
+ pcValue: new(uint),
+ gasValue: new(uint),
+ costValue: new(uint),
+ depthValue: new(uint),
+ refundValue: new(uint),
+ frame: newFrame(),
+ frameResult: newFrameResult(),
+ }
+ if ctx.BlockHash != (common.Hash{}) {
+ tracer.ctx["blockHash"] = ctx.BlockHash
+
+ if ctx.TxHash != (common.Hash{}) {
+ tracer.ctx["txIndex"] = ctx.TxIndex
+ tracer.ctx["txHash"] = ctx.TxHash
+ }
+ }
+ // Set up builtins for this environment
+ tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
+ ctx.PushString(hexutil.Encode(popSlice(ctx)))
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
+ var word common.Hash
+ if ptr, size := ctx.GetBuffer(-1); ptr != nil {
+ word = common.BytesToHash(makeSlice(ptr, size))
+ } else {
+ word = common.HexToHash(ctx.GetString(-1))
+ }
+ ctx.Pop()
+ copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
+ var addr common.Address
+ if ptr, size := ctx.GetBuffer(-1); ptr != nil {
+ addr = common.BytesToAddress(makeSlice(ptr, size))
+ } else {
+ addr = common.HexToAddress(ctx.GetString(-1))
+ }
+ ctx.Pop()
+ copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
+ var from common.Address
+ if ptr, size := ctx.GetBuffer(-2); ptr != nil {
+ from = common.BytesToAddress(makeSlice(ptr, size))
+ } else {
+ from = common.HexToAddress(ctx.GetString(-2))
+ }
+ nonce := uint64(ctx.GetInt(-1))
+ ctx.Pop2()
+
+ contract := crypto.CreateAddress(from, nonce)
+ copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int {
+ var from common.Address
+ if ptr, size := ctx.GetBuffer(-3); ptr != nil {
+ from = common.BytesToAddress(makeSlice(ptr, size))
+ } else {
+ from = common.HexToAddress(ctx.GetString(-3))
+ }
+ // Retrieve salt hex string from js stack
+ salt := common.HexToHash(ctx.GetString(-2))
+ // Retrieve code slice from js stack
+ var code []byte
+ if ptr, size := ctx.GetBuffer(-1); ptr != nil {
+ code = common.CopyBytes(makeSlice(ptr, size))
+ } else {
+ code = common.FromHex(ctx.GetString(-1))
+ }
+ codeHash := crypto.Keccak256(code)
+ ctx.Pop3()
+ contract := crypto.CreateAddress2(from, salt, codeHash)
+ copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
+ addr := common.BytesToAddress(popSlice(ctx))
+ for _, p := range tracer.activePrecompiles {
+ if p == addr {
+ ctx.PushBoolean(true)
+ return 1
+ }
+ }
+ ctx.PushBoolean(false)
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
+ start, end := ctx.GetInt(-2), ctx.GetInt(-1)
+ ctx.Pop2()
+
+ blob := popSlice(ctx)
+ size := end - start
+
+ if start < 0 || start > end || end > len(blob) {
+ // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
+ ctx.PushFixedBuffer(0)
+ return 1
+ }
+ copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
+ return 1
+ })
+ // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
+ if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
+ log.Warn("Failed to compile tracer", "err", err)
+ return nil, err
+ }
+ tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
+
+ hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step")
+ tracer.vm.Pop()
+
+ if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
+ return nil, fmt.Errorf("trace object must expose a function fault()")
+ }
+ tracer.vm.Pop()
+
+ if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
+ return nil, fmt.Errorf("trace object must expose a function result()")
+ }
+ tracer.vm.Pop()
+
+ hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter")
+ tracer.vm.Pop()
+ hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit")
+ tracer.vm.Pop()
+ if hasEnter != hasExit {
+ return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()")
+ }
+ tracer.traceCallFrames = hasEnter && hasExit
+ tracer.traceSteps = hasStep
+
+ // Tracer is valid, inject the big int library to access large numbers
+ tracer.vm.EvalString(bigIntegerJS)
+ tracer.vm.PutGlobalString("bigInt")
+
+ // Push the global environment state as object #1 into the JSVM stack
+ tracer.stateObject = tracer.vm.PushObject()
+
+ logObject := tracer.vm.PushObject()
+
+ tracer.opWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "op")
+
+ tracer.stackWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "stack")
+
+ tracer.memoryWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "memory")
+
+ tracer.contractWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "contract")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getPC")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getGas")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getCost")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getDepth")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getRefund")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
+ if tracer.errorValue != nil {
+ ctx.PushString(*tracer.errorValue)
+ } else {
+ ctx.PushUndefined()
+ }
+ return 1
+ })
+ tracer.vm.PutPropString(logObject, "getError")
+
+ tracer.vm.PutPropString(tracer.stateObject, "log")
+
+ tracer.frame.pushObject(tracer.vm)
+ tracer.vm.PutPropString(tracer.stateObject, "frame")
+
+ tracer.frameResult.pushObject(tracer.vm)
+ tracer.vm.PutPropString(tracer.stateObject, "frameResult")
+
+ tracer.dbWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(tracer.stateObject, "db")
+
+ return tracer, nil
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (jst *jsTracer) Stop(err error) {
+ jst.reason = err
+ atomic.StoreUint32(&jst.interrupt, 1)
+}
+
+// call executes a method on a JS object, catching any errors, formatting and
+// returning them as error objects.
+func (jst *jsTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) {
+ // Execute the JavaScript call and return any error
+ jst.vm.PushString(method)
+ for _, arg := range args {
+ jst.vm.GetPropString(jst.stateObject, arg)
+ }
+ code := jst.vm.PcallProp(jst.tracerObject, len(args))
+ defer jst.vm.Pop()
+
+ if code != 0 {
+ err := jst.vm.SafeToString(-1)
+ return nil, errors.New(err)
+ }
+ // No error occurred, extract return value and return
+ if noret {
+ return nil, nil
+ }
+ // Push a JSON marshaller onto the stack. We can't marshal from the out-
+ // side because duktape can crash on large nestings and we can't catch
+ // C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?!
+ jst.vm.PushString("(JSON.stringify)")
+ jst.vm.Eval()
+
+ jst.vm.Swap(-1, -2)
+ if code = jst.vm.Pcall(1); code != 0 {
+ err := jst.vm.SafeToString(-1)
+ return nil, errors.New(err)
+ }
+ return json.RawMessage(jst.vm.SafeToString(-1)), nil
+}
+
+func wrapError(context string, err error) error {
+ return fmt.Errorf("%v in server-side tracer function '%v'", err, context)
+}
+
+// CaptureStart implements the Tracer interface to initialize the tracing operation.
+func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ jst.env = env
+ jst.ctx["type"] = "CALL"
+ if create {
+ jst.ctx["type"] = "CREATE"
+ }
+ jst.ctx["from"] = from
+ jst.ctx["to"] = to
+ jst.ctx["input"] = input
+ jst.ctx["gas"] = gas
+ jst.ctx["gasPrice"] = env.TxContext.GasPrice
+ jst.ctx["value"] = value
+
+ // Initialize the context
+ jst.ctx["block"] = env.Context.BlockNumber.Uint64()
+ jst.dbWrapper.db = env.StateDB
+ // Update list of precompiles based on current block
+ rules := env.ChainConfig().Rules(env.Context.BlockNumber)
+ jst.activePrecompiles = vm.ActivePrecompiles(rules)
+
+ // Compute intrinsic gas
+ isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
+ isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
+ intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
+ if err != nil {
+ return
+ }
+ jst.ctx["intrinsicGas"] = intrinsicGas
+}
+
+// CaptureState implements the Tracer interface to trace a single step of VM execution.
+func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+ if !jst.traceSteps {
+ return
+ }
+ if jst.err != nil {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ jst.env.Cancel()
+ return
+ }
+ jst.opWrapper.op = op
+ jst.stackWrapper.stack = scope.Stack
+ jst.memoryWrapper.memory = scope.Memory
+ jst.contractWrapper.contract = scope.Contract
+
+ *jst.pcValue = uint(pc)
+ *jst.gasValue = uint(gas)
+ *jst.costValue = uint(cost)
+ *jst.depthValue = uint(depth)
+ *jst.refundValue = uint(jst.env.StateDB.GetRefund())
+
+ jst.errorValue = nil
+ if err != nil {
+ jst.errorValue = new(string)
+ *jst.errorValue = err.Error()
+ }
+
+ if _, err := jst.call(true, "step", "log", "db"); err != nil {
+ jst.err = wrapError("step", err)
+ }
+}
+
+// CaptureFault implements the Tracer interface to trace an execution fault
+func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+ if jst.err != nil {
+ return
+ }
+ // Apart from the error, everything matches the previous invocation
+ jst.errorValue = new(string)
+ *jst.errorValue = err.Error()
+
+ if _, err := jst.call(true, "fault", "log", "db"); err != nil {
+ jst.err = wrapError("fault", err)
+ }
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
+ jst.ctx["output"] = output
+ jst.ctx["time"] = t.String()
+ jst.ctx["gasUsed"] = gasUsed
+
+ if err != nil {
+ jst.ctx["error"] = err.Error()
+ }
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ if !jst.traceCallFrames {
+ return
+ }
+ if jst.err != nil {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ return
+ }
+
+ *jst.frame.typ = typ.String()
+ *jst.frame.from = from
+ *jst.frame.to = to
+ jst.frame.input = common.CopyBytes(input)
+ *jst.frame.gas = uint(gas)
+ jst.frame.value = nil
+ if value != nil {
+ jst.frame.value = new(big.Int).SetBytes(value.Bytes())
+ }
+
+ if _, err := jst.call(true, "enter", "frame"); err != nil {
+ jst.err = wrapError("enter", err)
+ }
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+ if !jst.traceCallFrames {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ return
+ }
+
+ jst.frameResult.output = common.CopyBytes(output)
+ *jst.frameResult.gasUsed = uint(gasUsed)
+ jst.frameResult.errorValue = nil
+ if err != nil {
+ jst.frameResult.errorValue = new(string)
+ *jst.frameResult.errorValue = err.Error()
+ }
+
+ if _, err := jst.call(true, "exit", "frameResult"); err != nil {
+ jst.err = wrapError("exit", err)
+ }
+}
+
+// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
+func (jst *jsTracer) GetResult() (json.RawMessage, error) {
+ // Transform the context into a JavaScript object and inject into the state
+ obj := jst.vm.PushObject()
+
+ for key, val := range jst.ctx {
+ jst.addToObj(obj, key, val)
+ }
+ jst.vm.PutPropString(jst.stateObject, "ctx")
+
+ // Finalize the trace and return the results
+ result, err := jst.call(false, "result", "ctx", "db")
+ if err != nil {
+ jst.err = wrapError("result", err)
+ }
+ // Clean up the JavaScript environment
+ jst.vm.DestroyHeap()
+ jst.vm.Destroy()
+
+ return result, jst.err
+}
+
+// addToObj pushes a field to a JS object.
+func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {
+ pushValue(jst.vm, val)
+ jst.vm.PutPropString(obj, key)
+}
+
+func pushValue(ctx *duktape.Context, val interface{}) {
+ switch val := val.(type) {
+ case uint64:
+ ctx.PushUint(uint(val))
+ case string:
+ ctx.PushString(val)
+ case []byte:
+ ptr := ctx.PushFixedBuffer(len(val))
+ copy(makeSlice(ptr, uint(len(val))), val)
+ case common.Address:
+ ptr := ctx.PushFixedBuffer(20)
+ copy(makeSlice(ptr, 20), val[:])
+ case *big.Int:
+ pushBigInt(val, ctx)
+ case int:
+ ctx.PushInt(val)
+ case uint:
+ ctx.PushUint(val)
+ case common.Hash:
+ ptr := ctx.PushFixedBuffer(32)
+ copy(makeSlice(ptr, 32), val[:])
+ default:
+ panic(fmt.Sprintf("unsupported type: %T", val))
+ }
+}
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/js/tracer_test.go
similarity index 75%
rename from eth/tracers/tracer_test.go
rename to eth/tracers/js/tracer_test.go
index 0e78f34b6..cf0a4aa82 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/js/tracer_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package tracers
+package js
import (
"encoding/json"
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params"
)
@@ -58,13 +59,13 @@ func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
}
-func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
- env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer})
+func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
var (
+ env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer})
startGas uint64 = 10000
value = big.NewInt(0)
+ contract = vm.NewContract(account{}, account{}, value, startGas)
)
- contract := vm.NewContract(account{}, account{}, value, startGas)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
@@ -79,14 +80,11 @@ func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (js
func TestTracer(t *testing.T) {
execTracer := func(code string) ([]byte, string) {
t.Helper()
- tracer, err := New(code, new(Context))
+ tracer, err := newJsTracer(code, nil)
if err != nil {
t.Fatal(err)
}
- ret, err := runTrace(tracer, &vmContext{
- blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)},
- txCtx: vm.TxContext{GasPrice: big.NewInt(100000)},
- }, params.TestChainConfig)
+ ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
if err != nil {
return nil, err.Error() // Stringify to allow comparison without nil checks
}
@@ -131,9 +129,8 @@ func TestTracer(t *testing.T) {
func TestHalt(t *testing.T) {
t.Skip("duktape doesn't support abortion")
-
timeout := errors.New("stahp")
- tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", new(Context))
+ tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil)
if err != nil {
t.Fatal(err)
}
@@ -147,18 +144,19 @@ func TestHalt(t *testing.T) {
}
func TestHaltBetweenSteps(t *testing.T) {
- tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context))
+ tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil)
if err != nil {
t.Fatal(err)
}
- env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
}
- tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil)
+ tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0))
+ tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
timeout := errors.New("stahp")
tracer.Stop(timeout)
- tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil)
+ tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
t.Errorf("Expected timeout error, got %v", err)
@@ -168,24 +166,16 @@ func TestHaltBetweenSteps(t *testing.T) {
// TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb
// in 'result'
func TestNoStepExec(t *testing.T) {
- runEmptyTrace := func(tracer Tracer, vmctx *vmContext) (json.RawMessage, error) {
- env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- startGas := uint64(10000)
- contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas)
- tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0))
- tracer.CaptureEnd(nil, startGas-contract.Gas, 1, nil)
- return tracer.GetResult()
- }
execTracer := func(code string) []byte {
t.Helper()
- tracer, err := New(code, new(Context))
+ tracer, err := newJsTracer(code, nil)
if err != nil {
t.Fatal(err)
}
- ret, err := runEmptyTrace(tracer, &vmContext{
- blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)},
- txCtx: vm.TxContext{GasPrice: big.NewInt(100000)},
- })
+ env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
+ tracer.CaptureEnd(nil, 0, 1, nil)
+ ret, err := tracer.GetResult()
if err != nil {
t.Fatal(err)
}
@@ -212,7 +202,7 @@ func TestIsPrecompile(t *testing.T) {
chaincfg.IstanbulBlock = big.NewInt(200)
chaincfg.BerlinBlock = big.NewInt(300)
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
- tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context))
+ tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil)
if err != nil {
t.Fatal(err)
}
@@ -226,7 +216,7 @@ func TestIsPrecompile(t *testing.T) {
t.Errorf("Tracer should not consider blake2f as precompile in byzantium")
}
- tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context))
+ tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil)
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
if err != nil {
@@ -239,23 +229,20 @@ func TestIsPrecompile(t *testing.T) {
func TestEnterExit(t *testing.T) {
// test that either both or none of enter() and exit() are defined
- if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(Context)); err == nil {
+ if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context)); err == nil {
t.Fatal("tracer creation should've failed without exit() definition")
}
- if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(Context)); err != nil {
+ if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context)); err != nil {
t.Fatal(err)
}
-
// test that the enter and exit method are correctly invoked and the values passed
- tracer, err := New("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(Context))
+ tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context))
if err != nil {
t.Fatal(err)
}
-
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
}
-
tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.CaptureExit([]byte{}, 400, nil)
diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go
new file mode 100644
index 000000000..e60e82de4
--- /dev/null
+++ b/eth/tracers/native/4byte.go
@@ -0,0 +1,148 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+ "strconv"
+ "sync/atomic"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
+)
+
+func init() {
+ register("4byteTracer", newFourByteTracer)
+}
+
+// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing.
+// It collects the methods identifiers along with the size of the supplied data, so
+// a reversed signature can be matched against the size of the data.
+//
+// Example:
+// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
+// {
+// 0x27dc297e-128: 1,
+// 0x38cc4831-0: 2,
+// 0x524f3889-96: 1,
+// 0xadf59f99-288: 1,
+// 0xc281d19e-0: 1
+// }
+type fourByteTracer struct {
+ env *vm.EVM
+ ids map[string]int // ids aggregates the 4byte ids found
+ interrupt uint32 // Atomic flag to signal execution interruption
+ reason error // Textual reason for the interruption
+ activePrecompiles []common.Address // Updated on CaptureStart based on given rules
+}
+
+// newFourByteTracer returns a native go tracer which collects
+// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
+func newFourByteTracer() tracers.Tracer {
+ t := &fourByteTracer{
+ ids: make(map[string]int),
+ }
+ return t
+}
+
+// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
+func (t *fourByteTracer) isPrecompiled(addr common.Address) bool {
+ for _, p := range t.activePrecompiles {
+ if p == addr {
+ return true
+ }
+ }
+ return false
+}
+
+// store saves the given identifier and datasize.
+func (t *fourByteTracer) store(id []byte, size int) {
+ key := bytesToHex(id) + "-" + strconv.Itoa(size)
+ t.ids[key] += 1
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ t.env = env
+
+ // Update list of precompiles based on current block
+ rules := env.ChainConfig().Rules(env.Context.BlockNumber)
+ t.activePrecompiles = vm.ActivePrecompiles(rules)
+
+ // Save the outer calldata also
+ if len(input) >= 4 {
+ t.store(input[0:4], len(input)-4)
+ }
+}
+
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *fourByteTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ // Skip if tracing was interrupted
+ if atomic.LoadUint32(&t.interrupt) > 0 {
+ t.env.Cancel()
+ return
+ }
+ if len(input) < 4 {
+ return
+ }
+ // primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT
+ if op != vm.DELEGATECALL && op != vm.STATICCALL &&
+ op != vm.CALL && op != vm.CALLCODE {
+ return
+ }
+ // Skip any pre-compile invocations, those are just fancy opcodes
+ if t.isPrecompiled(to) {
+ return
+ }
+ t.store(input[0:4], len(input)-4)
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (t *fourByteTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+}
+
+// CaptureFault implements the EVMLogger interface to trace an execution fault.
+func (t *fourByteTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (t *fourByteTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
+}
+
+// GetResult returns the json-encoded nested list of call traces, and any
+// error arising from the encoding or forceful termination (via `Stop`).
+func (t *fourByteTracer) GetResult() (json.RawMessage, error) {
+ res, err := json.Marshal(t.ids)
+ if err != nil {
+ return nil, err
+ }
+ return res, t.reason
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *fourByteTracer) Stop(err error) {
+ t.reason = err
+ atomic.StoreUint32(&t.interrupt, 1)
+}
diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go
index 8f22baa10..16ea75aa4 100644
--- a/eth/tracers/native/call.go
+++ b/eth/tracers/native/call.go
@@ -31,7 +31,7 @@ import (
)
func init() {
- tracers.RegisterNativeTracer("callTracer", NewCallTracer)
+ register("callTracer", newCallTracer)
}
type callFrame struct {
@@ -48,21 +48,24 @@ type callFrame struct {
}
type callTracer struct {
+ env *vm.EVM
callstack []callFrame
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
}
-// NewCallTracer returns a native go tracer which tracks
+// newCallTracer returns a native go tracer which tracks
// call frames of a tx, and implements vm.EVMLogger.
-func NewCallTracer() tracers.Tracer {
+func newCallTracer() tracers.Tracer {
// First callframe contains tx context info
// and is populated on start and end.
t := &callTracer{callstack: make([]callFrame, 1)}
return t
}
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ t.env = env
t.callstack[0] = callFrame{
Type: "CALL",
From: addrToHex(from),
@@ -76,6 +79,7 @@ func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
}
}
+// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
t.callstack[0].GasUsed = uintToHex(gasUsed)
if err != nil {
@@ -88,16 +92,19 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration,
}
}
-func (t *callTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
}
-func (t *callTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
+// CaptureFault implements the EVMLogger interface to trace an execution fault.
+func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
}
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
// Skip if tracing was interrupted
if atomic.LoadUint32(&t.interrupt) > 0 {
- // TODO: env.Cancel()
+ t.env.Cancel()
return
}
@@ -112,6 +119,8 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
t.callstack = append(t.callstack, call)
}
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
size := len(t.callstack)
if size <= 1 {
@@ -134,6 +143,8 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
}
+// GetResult returns the json-encoded nested list of call traces, and any
+// error arising from the encoding or forceful termination (via `Stop`).
func (t *callTracer) GetResult() (json.RawMessage, error) {
if len(t.callstack) != 1 {
return nil, errors.New("incorrect number of top-level calls")
@@ -145,6 +156,7 @@ func (t *callTracer) GetResult() (json.RawMessage, error) {
return json.RawMessage(res), t.reason
}
+// Stop terminates execution of the tracer at the first opportune moment.
func (t *callTracer) Stop(err error) {
t.reason = err
atomic.StoreUint32(&t.interrupt, 1)
diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go
index 554bb18f1..ee110ef7d 100644
--- a/eth/tracers/native/noop.go
+++ b/eth/tracers/native/noop.go
@@ -1,3 +1,19 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
package native
import (
@@ -11,36 +27,48 @@ import (
)
func init() {
- tracers.RegisterNativeTracer("noopTracerNative", NewNoopTracer)
+ register("noopTracerNative", newNoopTracer)
}
+// noopTracer is a go implementation of the Tracer interface which
+// performs no action. It's mostly useful for testing purposes.
type noopTracer struct{}
-func NewNoopTracer() tracers.Tracer {
+// newNoopTracer returns a new noop tracer.
+func newNoopTracer() tracers.Tracer {
return &noopTracer{}
}
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
+// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
}
-func (t *noopTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *noopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
}
-func (t *noopTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
+// CaptureFault implements the EVMLogger interface to trace an execution fault.
+func (t *noopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
}
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *noopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
func (t *noopTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
}
+// GetResult returns an empty json object.
func (t *noopTracer) GetResult() (json.RawMessage, error) {
return json.RawMessage(`{}`), nil
}
+// Stop terminates execution of the tracer at the first opportune moment.
func (t *noopTracer) Stop(err error) {
}
diff --git a/eth/tracers/native/tracer.go b/eth/tracers/native/tracer.go
new file mode 100644
index 000000000..3158654f3
--- /dev/null
+++ b/eth/tracers/native/tracer.go
@@ -0,0 +1,79 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+/*
+Package native is a collection of tracers written in go.
+
+In order to add a native tracer and have it compiled into the binary, a new
+file needs to be added to this folder, containing an implementation of the
+`eth.tracers.Tracer` interface.
+
+Aside from implementing the tracer, it also needs to register itself, using the
+`register` method -- and this needs to be done in the package initialization.
+
+Example:
+
+```golang
+func init() {
+ register("noopTracerNative", newNoopTracer)
+}
+```
+*/
+package native
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/eth/tracers"
+)
+
+// init registers itself this packages as a lookup for tracers.
+func init() {
+ tracers.RegisterLookup(false, lookup)
+}
+
+/*
+ctors is a map of package-local tracer constructors.
+
+We cannot be certain about the order of init-functions within a package,
+The go spec (https://golang.org/ref/spec#Package_initialization) says
+
+> To ensure reproducible initialization behavior, build systems
+> are encouraged to present multiple files belonging to the same
+> package in lexical file name order to a compiler.
+
+Hence, we cannot make the map in init, but must make it upon first use.
+*/
+var ctors map[string]func() tracers.Tracer
+
+// register is used by native tracers to register their presence.
+func register(name string, ctor func() tracers.Tracer) {
+ if ctors == nil {
+ ctors = make(map[string]func() tracers.Tracer)
+ }
+ ctors[name] = ctor
+}
+
+// lookup returns a tracer, if one can be matched to the given name.
+func lookup(name string, ctx *tracers.Context) (tracers.Tracer, error) {
+ if ctors == nil {
+ ctors = make(map[string]func() tracers.Tracer)
+ }
+ if ctor, ok := ctors[name]; ok {
+ return ctor(), nil
+ }
+ return nil, errors.New("no tracer found")
+}
diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go
index 79534c636..e7073e7d2 100644
--- a/eth/tracers/tracers.go
+++ b/eth/tracers/tracers.go
@@ -14,18 +14,25 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// Package tracers is a collection of JavaScript transaction tracers.
+// Package tracers is a manager for transaction tracing engines.
package tracers
import (
"encoding/json"
- "strings"
- "unicode"
+ "errors"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers/internal/tracers"
)
+// Context contains some contextual infos for a transaction execution that is not
+// available from within the EVM object.
+type Context struct {
+ BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call)
+ TxIndex int // Index of the transaction within a block (zero if dangling tx or call)
+ TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
+}
+
// Tracer interface extends vm.EVMLogger and additionally
// allows collecting the tracing result.
type Tracer interface {
@@ -35,50 +42,31 @@ type Tracer interface {
Stop(err error)
}
+type lookupFunc func(string, *Context) (Tracer, error)
+
var (
- nativeTracers map[string]func() Tracer = make(map[string]func() Tracer)
- jsTracers = make(map[string]string)
+ lookups []lookupFunc
)
-// RegisterNativeTracer makes native tracers which adhere
-// to the `Tracer` interface available to the rest of the codebase.
-// It is typically invoked in the `init()` function, e.g. see the `native/call.go`.
-func RegisterNativeTracer(name string, ctor func() Tracer) {
- nativeTracers[name] = ctor
+// RegisterLookup registers a method as a lookup for tracers, meaning that
+// users can invoke a named tracer through that lookup. If 'wildcard' is true,
+// then the lookup will be placed last. This is typically meant for interpreted
+// engines (js) which can evaluate dynamic user-supplied code.
+func RegisterLookup(wildcard bool, lookup lookupFunc) {
+ if wildcard {
+ lookups = append(lookups, lookup)
+ } else {
+ lookups = append([]lookupFunc{lookup}, lookups...)
+ }
}
-// New returns a new instance of a tracer,
-// 1. If 'code' is the name of a registered native tracer, then that tracer
-// is instantiated and returned
-// 2. If 'code' is the name of a registered js-tracer, then that tracer is
-// instantiated and returned
-// 3. Otherwise, the code is interpreted as the js code of a js-tracer, and
-// is evaluated and returned.
+// New returns a new instance of a tracer, by iterating through the
+// registered lookups.
func New(code string, ctx *Context) (Tracer, error) {
- // Resolve native tracer
- if fn, ok := nativeTracers[code]; ok {
- return fn(), nil
- }
- // Resolve js-tracers by name and assemble the tracer object
- if tracer, ok := jsTracers[code]; ok {
- code = tracer
- }
- return newJsTracer(code, ctx)
-}
-
-// camel converts a snake cased input string into a camel cased output.
-func camel(str string) string {
- pieces := strings.Split(str, "_")
- for i := 1; i < len(pieces); i++ {
- pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
- }
- return strings.Join(pieces, "")
-}
-
-// init retrieves the JavaScript transaction tracers included in go-ethereum.
-func init() {
- for _, file := range tracers.AssetNames() {
- name := camel(strings.TrimSuffix(file, ".js"))
- jsTracers[name] = string(tracers.MustAsset(file))
+ for _, lookup := range lookups {
+ if tracer, err := lookup(code, ctx); err == nil {
+ return tracer, nil
+ }
}
+ return nil, errors.New("tracer not found")
}
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index a027caa96..915cab10d 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -17,11 +17,7 @@
package tracers
import (
- "crypto/ecdsa"
- "crypto/rand"
- "encoding/json"
"math/big"
- "reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -35,56 +31,6 @@ import (
"github.com/ethereum/go-ethereum/tests"
)
-// To generate a new callTracer test, copy paste the makeTest method below into
-// a Geth console and call it with a transaction hash you which to export.
-
-/*
-// makeTest generates a callTracer test by running a prestate reassembled and a
-// call trace run, assembling all the gathered information into a test case.
-var makeTest = function(tx, rewind) {
- // Generate the genesis block from the block, transaction and prestate data
- var block = eth.getBlock(eth.getTransaction(tx).blockHash);
- var genesis = eth.getBlock(block.parentHash);
-
- delete genesis.gasUsed;
- delete genesis.logsBloom;
- delete genesis.parentHash;
- delete genesis.receiptsRoot;
- delete genesis.sha3Uncles;
- delete genesis.size;
- delete genesis.transactions;
- delete genesis.transactionsRoot;
- delete genesis.uncles;
-
- genesis.gasLimit = genesis.gasLimit.toString();
- genesis.number = genesis.number.toString();
- genesis.timestamp = genesis.timestamp.toString();
-
- genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
- for (var key in genesis.alloc) {
- genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString();
- }
- genesis.config = admin.nodeInfo.protocols.eth.config;
-
- // Generate the call trace and produce the test input
- var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
- delete result.time;
-
- console.log(JSON.stringify({
- genesis: genesis,
- context: {
- number: block.number.toString(),
- difficulty: block.difficulty,
- timestamp: block.timestamp.toString(),
- gasLimit: block.gasLimit.toString(),
- miner: block.miner,
- },
- input: eth.getRawTransaction(tx),
- result: result,
- }, null, 2));
-}
-*/
-
// callTrace is the result of a callTracer run.
type callTrace struct {
Type string `json:"type"`
@@ -99,184 +45,6 @@ type callTrace struct {
Calls []callTrace `json:"calls,omitempty"`
}
-// TestZeroValueToNotExitCall tests the calltracer(s) on the following:
-// Tx to A, A calls B with zero value. B does not already exist.
-// Expected: that enter/exit is invoked and the inner call is shown in the result
-func TestZeroValueToNotExitCall(t *testing.T) {
- var to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
- privkey, err := crypto.HexToECDSA("0000000000000000deadbeef00000000000000000000000000000000deadbeef")
- if err != nil {
- t.Fatalf("err %v", err)
- }
- signer := types.NewEIP155Signer(big.NewInt(1))
- tx, err := types.SignNewTx(privkey, signer, &types.LegacyTx{
- GasPrice: big.NewInt(0),
- Gas: 50000,
- To: &to,
- })
- if err != nil {
- t.Fatalf("err %v", err)
- }
- origin, _ := signer.Sender(tx)
- txContext := vm.TxContext{
- Origin: origin,
- GasPrice: big.NewInt(1),
- }
- context := vm.BlockContext{
- CanTransfer: core.CanTransfer,
- Transfer: core.Transfer,
- Coinbase: common.Address{},
- BlockNumber: new(big.Int).SetUint64(8000000),
- Time: new(big.Int).SetUint64(5),
- Difficulty: big.NewInt(0x30000),
- GasLimit: uint64(6000000),
- }
- var code = []byte{
- byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero
- byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.GAS), // value=0,address=0xff, gas=GAS
- byte(vm.CALL),
- }
- var alloc = core.GenesisAlloc{
- to: core.GenesisAccount{
- Nonce: 1,
- Code: code,
- },
- origin: core.GenesisAccount{
- Nonce: 0,
- Balance: big.NewInt(500000000000000),
- },
- }
- _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
- // Create the tracer, the EVM environment and run it
- tracer, err := New("callTracerJs", new(Context))
- if err != nil {
- t.Fatalf("failed to create call tracer: %v", err)
- }
- evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
- msg, err := tx.AsMessage(signer, nil)
- if err != nil {
- t.Fatalf("failed to prepare transaction for tracing: %v", err)
- }
- st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, err = st.TransitionDb(); err != nil {
- t.Fatalf("failed to execute transaction: %v", err)
- }
- // Retrieve the trace result and compare against the etalon
- res, err := tracer.GetResult()
- if err != nil {
- t.Fatalf("failed to retrieve trace result: %v", err)
- }
- have := new(callTrace)
- if err := json.Unmarshal(res, have); err != nil {
- t.Fatalf("failed to unmarshal trace result: %v", err)
- }
- wantStr := `{"type":"CALL","from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","to":"0x00000000000000000000000000000000deadbeef","value":"0x0","gas":"0x7148","gasUsed":"0x2d0","input":"0x","output":"0x","calls":[{"type":"CALL","from":"0x00000000000000000000000000000000deadbeef","to":"0x00000000000000000000000000000000000000ff","value":"0x0","gas":"0x6cbf","gasUsed":"0x0","input":"0x","output":"0x"}]}`
- want := new(callTrace)
- json.Unmarshal([]byte(wantStr), want)
- if !jsonEqual(have, want) {
- t.Error("have != want")
- }
-}
-
-func TestPrestateTracerCreate2(t *testing.T) {
- unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
- new(big.Int), 5000000, big.NewInt(1), []byte{})
-
- privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
- if err != nil {
- t.Fatalf("err %v", err)
- }
- signer := types.NewEIP155Signer(big.NewInt(1))
- tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA)
- if err != nil {
- t.Fatalf("err %v", err)
- }
- /**
- This comes from one of the test-vectors on the Skinny Create2 - EIP
-
- address 0x00000000000000000000000000000000deadbeef
- salt 0x00000000000000000000000000000000000000000000000000000000cafebabe
- init_code 0xdeadbeef
- gas (assuming no mem expansion): 32006
- result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7
- */
- origin, _ := signer.Sender(tx)
- txContext := vm.TxContext{
- Origin: origin,
- GasPrice: big.NewInt(1),
- }
- context := vm.BlockContext{
- CanTransfer: core.CanTransfer,
- Transfer: core.Transfer,
- Coinbase: common.Address{},
- BlockNumber: new(big.Int).SetUint64(8000000),
- Time: new(big.Int).SetUint64(5),
- Difficulty: big.NewInt(0x30000),
- GasLimit: uint64(6000000),
- }
- alloc := core.GenesisAlloc{}
-
- // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
- // the address
- alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
- Nonce: 1,
- Code: hexutil.MustDecode("0x63deadbeef60005263cafebabe6004601c6000F560005260206000F3"),
- Balance: big.NewInt(1),
- }
- alloc[origin] = core.GenesisAccount{
- Nonce: 1,
- Code: []byte{},
- Balance: big.NewInt(500000000000000),
- }
- _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
-
- // Create the tracer, the EVM environment and run it
- tracer, err := New("prestateTracer", new(Context))
- if err != nil {
- t.Fatalf("failed to create call tracer: %v", err)
- }
- evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
-
- msg, err := tx.AsMessage(signer, nil)
- if err != nil {
- t.Fatalf("failed to prepare transaction for tracing: %v", err)
- }
- st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, err = st.TransitionDb(); err != nil {
- t.Fatalf("failed to execute transaction: %v", err)
- }
- // Retrieve the trace result and compare against the etalon
- res, err := tracer.GetResult()
- if err != nil {
- t.Fatalf("failed to retrieve trace result: %v", err)
- }
- ret := make(map[string]interface{})
- if err := json.Unmarshal(res, &ret); err != nil {
- t.Fatalf("failed to unmarshal trace result: %v", err)
- }
- if _, has := ret["0x60f3f640a8508fc6a86d45df051962668e1e8ac7"]; !has {
- t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result")
- }
-}
-
-// jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to
-// comparison
-func jsonEqual(x, y interface{}) bool {
- xTrace := new(callTrace)
- yTrace := new(callTrace)
- if xj, err := json.Marshal(x); err == nil {
- json.Unmarshal(xj, xTrace)
- } else {
- return false
- }
- if yj, err := json.Marshal(y); err == nil {
- json.Unmarshal(yj, yTrace)
- } else {
- return false
- }
- return reflect.DeepEqual(xTrace, yTrace)
-}
-
func BenchmarkTransactionTrace(b *testing.B) {
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
from := crypto.PubkeyToAddress(key.PublicKey)
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 18e0a4941..37680807d 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -233,6 +233,8 @@ func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction,
if err == nil {
return sender, nil
}
+
+ // It was not found in cache, ask the server.
var meta struct {
Hash common.Hash
From common.Address
diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go
index a958c1e32..d56febc91 100644
--- a/ethclient/ethclient_test.go
+++ b/ethclient/ethclient_test.go
@@ -187,9 +187,34 @@ var (
testBalance = big.NewInt(2e15)
)
+var genesis = &core.Genesis{
+ Config: params.AllEthashProtocolChanges,
+ Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
+ ExtraData: []byte("test genesis"),
+ Timestamp: 9000,
+ BaseFee: big.NewInt(params.InitialBaseFee),
+}
+
+var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
+ Nonce: 0,
+ Value: big.NewInt(12),
+ GasPrice: big.NewInt(params.InitialBaseFee),
+ Gas: params.TxGas,
+ To: &common.Address{2},
+})
+
+var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
+ Nonce: 1,
+ Value: big.NewInt(8),
+ GasPrice: big.NewInt(params.InitialBaseFee),
+ Gas: params.TxGas,
+ To: &common.Address{2},
+})
+
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
// Generate test chain.
- genesis, blocks := generateTestChain()
+ blocks := generateTestChain()
+
// Create node
n, err := node.New(&node.Config{})
if err != nil {
@@ -212,25 +237,22 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
return n, blocks
}
-func generateTestChain() (*core.Genesis, []*types.Block) {
+func generateTestChain() []*types.Block {
db := rawdb.NewMemoryDatabase()
- config := params.AllEthashProtocolChanges
- genesis := &core.Genesis{
- Config: config,
- Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
- ExtraData: []byte("test genesis"),
- Timestamp: 9000,
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
generate := func(i int, g *core.BlockGen) {
g.OffsetTime(5)
g.SetExtra([]byte("test"))
+ if i == 1 {
+ // Test transactions are included in block #2.
+ g.AddTx(testTx1)
+ g.AddTx(testTx2)
+ }
}
gblock := genesis.ToBlock(db)
engine := ethash.NewFaker()
- blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate)
+ blocks, _ := core.GenerateChain(genesis.Config, gblock, engine, db, 2, generate)
blocks = append([]*types.Block{gblock}, blocks...)
- return genesis, blocks
+ return blocks
}
func TestEthClient(t *testing.T) {
@@ -242,30 +264,33 @@ func TestEthClient(t *testing.T) {
tests := map[string]struct {
test func(t *testing.T)
}{
- "TestHeader": {
+ "Header": {
func(t *testing.T) { testHeader(t, chain, client) },
},
- "TestBalanceAt": {
+ "BalanceAt": {
func(t *testing.T) { testBalanceAt(t, client) },
},
- "TestTxInBlockInterrupted": {
+ "TxInBlockInterrupted": {
func(t *testing.T) { testTransactionInBlockInterrupted(t, client) },
},
- "TestChainID": {
+ "ChainID": {
func(t *testing.T) { testChainID(t, client) },
},
- "TestGetBlock": {
+ "GetBlock": {
func(t *testing.T) { testGetBlock(t, client) },
},
- "TestStatusFunctions": {
+ "StatusFunctions": {
func(t *testing.T) { testStatusFunctions(t, client) },
},
- "TestCallContract": {
+ "CallContract": {
func(t *testing.T) { testCallContract(t, client) },
},
- "TestAtFunctions": {
+ "AtFunctions": {
func(t *testing.T) { testAtFunctions(t, client) },
},
+ "TransactionSender": {
+ func(t *testing.T) { testTransactionSender(t, client) },
+ },
}
t.Parallel()
@@ -321,6 +346,11 @@ func testBalanceAt(t *testing.T, client *rpc.Client) {
want *big.Int
wantErr error
}{
+ "valid_account_genesis": {
+ account: testAddr,
+ block: big.NewInt(0),
+ want: testBalance,
+ },
"valid_account": {
account: testAddr,
block: big.NewInt(1),
@@ -358,23 +388,25 @@ func testBalanceAt(t *testing.T, client *rpc.Client) {
func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) {
ec := NewClient(client)
- // Get current block by number
+ // Get current block by number.
block, err := ec.BlockByNumber(context.Background(), nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
- // Test tx in block interupted
+
+ // Test tx in block interupted.
ctx, cancel := context.WithCancel(context.Background())
cancel()
- tx, err := ec.TransactionInBlock(ctx, block.Hash(), 1)
+ tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
if tx != nil {
t.Fatal("transaction should be nil")
}
if err == nil || err == ethereum.NotFound {
t.Fatal("error should not be nil/notfound")
}
- // Test tx in block not found
- if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 1); err != ethereum.NotFound {
+
+ // Test tx in block not found.
+ if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound {
t.Fatal("error should be ethereum.NotFound")
}
}
@@ -392,12 +424,13 @@ func testChainID(t *testing.T, client *rpc.Client) {
func testGetBlock(t *testing.T, client *rpc.Client) {
ec := NewClient(client)
+
// Get current block number
blockNumber, err := ec.BlockNumber(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
- if blockNumber != 1 {
+ if blockNumber != 2 {
t.Fatalf("BlockNumber returned wrong number: %d", blockNumber)
}
// Get current block by number
@@ -445,6 +478,7 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) {
if progress != nil {
t.Fatalf("unexpected progress: %v", progress)
}
+
// NetworkID
networkID, err := ec.NetworkID(context.Background())
if err != nil {
@@ -453,20 +487,22 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) {
if networkID.Cmp(big.NewInt(0)) != 0 {
t.Fatalf("unexpected networkID: %v", networkID)
}
- // SuggestGasPrice (should suggest 1 Gwei)
+
+ // SuggestGasPrice
gasPrice, err := ec.SuggestGasPrice(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
- if gasPrice.Cmp(big.NewInt(1875000000)) != 0 { // 1 gwei tip + 0.875 basefee after a 1 gwei fee empty block
+ if gasPrice.Cmp(big.NewInt(1000000000)) != 0 {
t.Fatalf("unexpected gas price: %v", gasPrice)
}
- // SuggestGasTipCap (should suggest 1 Gwei)
+
+ // SuggestGasTipCap
gasTipCap, err := ec.SuggestGasTipCap(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
- if gasTipCap.Cmp(big.NewInt(1000000000)) != 0 {
+ if gasTipCap.Cmp(big.NewInt(234375000)) != 0 {
t.Fatalf("unexpected gas tip cap: %v", gasTipCap)
}
}
@@ -500,9 +536,11 @@ func testCallContract(t *testing.T, client *rpc.Client) {
func testAtFunctions(t *testing.T, client *rpc.Client) {
ec := NewClient(client)
+
// send a transaction for some interesting pending status
sendTransaction(ec)
time.Sleep(100 * time.Millisecond)
+
// Check pending transaction count
pending, err := ec.PendingTransactionCount(context.Background())
if err != nil {
@@ -561,23 +599,66 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
}
}
+func testTransactionSender(t *testing.T, client *rpc.Client) {
+ ec := NewClient(client)
+ ctx := context.Background()
+
+ // Retrieve testTx1 via RPC.
+ block2, err := ec.HeaderByNumber(ctx, big.NewInt(2))
+ if err != nil {
+ t.Fatal("can't get block 1:", err)
+ }
+ tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0)
+ if err != nil {
+ t.Fatal("can't get tx:", err)
+ }
+ if tx1.Hash() != testTx1.Hash() {
+ t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash())
+ }
+
+ // The sender address is cached in tx1, so no additional RPC should be required in
+ // TransactionSender. Ensure the server is not asked by canceling the context here.
+ canceledCtx, cancel := context.WithCancel(context.Background())
+ cancel()
+ sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if sender1 != testAddr {
+ t.Fatal("wrong sender:", sender1)
+ }
+
+ // Now try to get the sender of testTx2, which was not fetched through RPC.
+ // TransactionSender should query the server here.
+ sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if sender2 != testAddr {
+ t.Fatal("wrong sender:", sender2)
+ }
+}
+
func sendTransaction(ec *Client) error {
- // Retrieve chainID
chainID, err := ec.ChainID(context.Background())
if err != nil {
return err
}
- // Create transaction
- tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(params.InitialBaseFee), nil)
+ nonce, err := ec.PendingNonceAt(context.Background(), testAddr)
+ if err != nil {
+ return err
+ }
+
signer := types.LatestSignerForChainID(chainID)
- signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
+ tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{
+ Nonce: nonce,
+ To: &common.Address{2},
+ Value: big.NewInt(1),
+ Gas: 22000,
+ GasPrice: big.NewInt(params.InitialBaseFee),
+ })
if err != nil {
return err
}
- signedTx, err := tx.WithSignature(signer, signature)
- if err != nil {
- return err
- }
- // Send transaction
- return ec.SendTransaction(context.Background(), signedTx)
+ return ec.SendTransaction(context.Background(), tx)
}
diff --git a/ethclient/signer.go b/ethclient/signer.go
index 9de020b35..f827d4eb5 100644
--- a/ethclient/signer.go
+++ b/ethclient/signer.go
@@ -45,7 +45,7 @@ func (s *senderFromServer) Equal(other types.Signer) bool {
}
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) {
- if s.blockhash == (common.Hash{}) {
+ if s.addr == (common.Address{}) {
return common.Address{}, errNotCached
}
return s.addr, nil
diff --git a/miner/miner_test.go b/miner/miner_test.go
index da1e472db..4b5bff1df 100644
--- a/miner/miner_test.go
+++ b/miner/miner_test.go
@@ -237,7 +237,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux) {
// Create chainConfig
memdb := memorydb.New()
chainDB := rawdb.NewDatabase(memdb)
- genesis := core.DeveloperGenesisBlock(15, common.HexToAddress("12345"))
+ genesis := core.DeveloperGenesisBlock(15, 11_500_000, common.HexToAddress("12345"))
chainConfig, _, err := core.SetupGenesisBlock(chainDB, genesis)
if err != nil {
t.Fatalf("can't create new chain config: %v", err)
diff --git a/p2p/discover/v4wire/v4wire.go b/p2p/discover/v4wire/v4wire.go
index 23e713441..bc537a4cf 100644
--- a/p2p/discover/v4wire/v4wire.go
+++ b/p2p/discover/v4wire/v4wire.go
@@ -102,7 +102,7 @@ type (
}
)
-// This number is the maximum number of neighbor nodes in a Neigbors packet.
+// This number is the maximum number of neighbor nodes in a Neighbors packet.
const MaxNeighbors = 12
// This code computes the MaxNeighbors constant value.
diff --git a/p2p/enode/localnode.go b/p2p/enode/localnode.go
index 4827b6c0a..a18204e75 100644
--- a/p2p/enode/localnode.go
+++ b/p2p/enode/localnode.go
@@ -63,7 +63,7 @@ type LocalNode struct {
type lnEndpoint struct {
track *netutil.IPTracker
staticIP, fallbackIP net.IP
- fallbackUDP int
+ fallbackUDP uint16 // port
}
// NewLocalNode creates a local node.
@@ -208,8 +208,8 @@ func (ln *LocalNode) SetFallbackUDP(port int) {
ln.mu.Lock()
defer ln.mu.Unlock()
- ln.endpoint4.fallbackUDP = port
- ln.endpoint6.fallbackUDP = port
+ ln.endpoint4.fallbackUDP = uint16(port)
+ ln.endpoint6.fallbackUDP = uint16(port)
ln.updateEndpoints()
}
@@ -261,7 +261,7 @@ func (ln *LocalNode) updateEndpoints() {
}
// get returns the endpoint with highest precedence.
-func (e *lnEndpoint) get() (newIP net.IP, newPort int) {
+func (e *lnEndpoint) get() (newIP net.IP, newPort uint16) {
newPort = e.fallbackUDP
if e.fallbackIP != nil {
newIP = e.fallbackIP
@@ -277,15 +277,18 @@ func (e *lnEndpoint) get() (newIP net.IP, newPort int) {
// predictAddr wraps IPTracker.PredictEndpoint, converting from its string-based
// endpoint representation to IP and port types.
-func predictAddr(t *netutil.IPTracker) (net.IP, int) {
+func predictAddr(t *netutil.IPTracker) (net.IP, uint16) {
ep := t.PredictEndpoint()
if ep == "" {
return nil, 0
}
ipString, portString, _ := net.SplitHostPort(ep)
ip := net.ParseIP(ipString)
- port, _ := strconv.Atoi(portString)
- return ip, port
+ port, err := strconv.ParseUint(portString, 10, 16)
+ if err != nil {
+ return nil, 0
+ }
+ return ip, uint16(port)
}
func (ln *LocalNode) invalidate() {
diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go
index 1da464a10..aeb8ef777 100644
--- a/p2p/simulations/adapters/types.go
+++ b/p2p/simulations/adapters/types.go
@@ -242,7 +242,7 @@ func assignTCPPort() (uint16, error) {
if err != nil {
return 0, err
}
- p, err := strconv.ParseInt(port, 10, 32)
+ p, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return 0, err
}
diff --git a/params/version.go b/params/version.go
index c28ded936..4a5b9835f 100644
--- a/params/version.go
+++ b/params/version.go
@@ -23,7 +23,7 @@ import (
const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 10 // Minor version component of the current release
- VersionPatch = 12 // Patch version component of the current release
+ VersionPatch = 13 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string
)
diff --git a/trie/proof.go b/trie/proof.go
index 08a9e4042..51ecea0c3 100644
--- a/trie/proof.go
+++ b/trie/proof.go
@@ -472,12 +472,17 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key
if len(keys) != len(values) {
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
}
- // Ensure the received batch is monotonic increasing.
+ // Ensure the received batch is monotonic increasing and contains no deletions
for i := 0; i < len(keys)-1; i++ {
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
return false, errors.New("range is not monotonically increasing")
}
}
+ for _, value := range values {
+ if len(value) == 0 {
+ return false, errors.New("range contains deletion")
+ }
+ }
// Special case, there is no edge proof at all. The given range is expected
// to be the whole leaf-set in the trie.
if proof == nil {
diff --git a/trie/proof_test.go b/trie/proof_test.go
index a35b7144c..95ad6169c 100644
--- a/trie/proof_test.go
+++ b/trie/proof_test.go
@@ -813,6 +813,85 @@ func TestBloatedProof(t *testing.T) {
}
}
+// TestEmptyValueRangeProof tests normal range proof with both edge proofs
+// as the existent proof, but with an extra empty value included, which is a
+// noop technically, but practically should be rejected.
+func TestEmptyValueRangeProof(t *testing.T) {
+ trie, values := randomTrie(512)
+ var entries entrySlice
+ for _, kv := range values {
+ entries = append(entries, kv)
+ }
+ sort.Sort(entries)
+
+ // Create a new entry with a slightly modified key
+ mid := len(entries) / 2
+ key := common.CopyBytes(entries[mid-1].k)
+ for n := len(key) - 1; n >= 0; n-- {
+ if key[n] < 0xff {
+ key[n]++
+ break
+ }
+ }
+ noop := &kv{key, []byte{}, false}
+ entries = append(append(append([]*kv{}, entries[:mid]...), noop), entries[mid:]...)
+
+ start, end := 1, len(entries)-1
+
+ proof := memorydb.New()
+ if err := trie.Prove(entries[start].k, 0, proof); err != nil {
+ t.Fatalf("Failed to prove the first node %v", err)
+ }
+ if err := trie.Prove(entries[end-1].k, 0, proof); err != nil {
+ t.Fatalf("Failed to prove the last node %v", err)
+ }
+ var keys [][]byte
+ var vals [][]byte
+ for i := start; i < end; i++ {
+ keys = append(keys, entries[i].k)
+ vals = append(vals, entries[i].v)
+ }
+ _, err := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof)
+ if err == nil {
+ t.Fatalf("Expected failure on noop entry")
+ }
+}
+
+// TestAllElementsEmptyValueRangeProof tests the range proof with all elements,
+// but with an extra empty value included, which is a noop technically, but
+// practically should be rejected.
+func TestAllElementsEmptyValueRangeProof(t *testing.T) {
+ trie, values := randomTrie(512)
+ var entries entrySlice
+ for _, kv := range values {
+ entries = append(entries, kv)
+ }
+ sort.Sort(entries)
+
+ // Create a new entry with a slightly modified key
+ mid := len(entries) / 2
+ key := common.CopyBytes(entries[mid-1].k)
+ for n := len(key) - 1; n >= 0; n-- {
+ if key[n] < 0xff {
+ key[n]++
+ break
+ }
+ }
+ noop := &kv{key, []byte{}, false}
+ entries = append(append(append([]*kv{}, entries[:mid]...), noop), entries[mid:]...)
+
+ var keys [][]byte
+ var vals [][]byte
+ for i := 0; i < len(entries); i++ {
+ keys = append(keys, entries[i].k)
+ vals = append(vals, entries[i].v)
+ }
+ _, err := VerifyRangeProof(trie.Hash(), nil, nil, keys, vals, nil)
+ if err == nil {
+ t.Fatalf("Expected failure on noop entry")
+ }
+}
+
// mutateByte changes one byte in b.
func mutateByte(b []byte) {
for r := mrand.Intn(len(b)); ; {
diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go
index bd2574d5d..fb39e4252 100644
--- a/trie/stacktrie_test.go
+++ b/trie/stacktrie_test.go
@@ -26,6 +26,166 @@ import (
"github.com/ethereum/go-ethereum/ethdb/memorydb"
)
+func TestStackTrieInsertAndHash(t *testing.T) {
+ type KeyValueHash struct {
+ K string // Hex string for key.
+ V string // Value, directly converted to bytes.
+ H string // Expected root hash after insert of (K, V) to an existing trie.
+ }
+ tests := [][]KeyValueHash{
+ { // {0:0, 7:0, f:0}
+ {"00", "v_______________________0___0", "5cb26357b95bb9af08475be00243ceb68ade0b66b5cd816b0c18a18c612d2d21"},
+ {"70", "v_______________________0___1", "8ff64309574f7a437a7ad1628e690eb7663cfde10676f8a904a8c8291dbc1603"},
+ {"f0", "v_______________________0___2", "9e3a01bd8d43efb8e9d4b5506648150b8e3ed1caea596f84ee28e01a72635470"},
+ },
+ { // {1:0cc, e:{1:fc, e:fc}}
+ {"10cc", "v_______________________1___0", "233e9b257843f3dfdb1cce6676cdaf9e595ac96ee1b55031434d852bc7ac9185"},
+ {"e1fc", "v_______________________1___1", "39c5e908ae83d0c78520c7c7bda0b3782daf594700e44546e93def8f049cca95"},
+ {"eefc", "v_______________________1___2", "d789567559fd76fe5b7d9cc42f3750f942502ac1c7f2a466e2f690ec4b6c2a7c"},
+ },
+ { // {b:{a:ac, b:ac}, d:acc}
+ {"baac", "v_______________________2___0", "8be1c86ba7ec4c61e14c1a9b75055e0464c2633ae66a055a24e75450156a5d42"},
+ {"bbac", "v_______________________2___1", "8495159b9895a7d88d973171d737c0aace6fe6ac02a4769fff1bc43bcccce4cc"},
+ {"dacc", "v_______________________2___2", "9bcfc5b220a27328deb9dc6ee2e3d46c9ebc9c69e78acda1fa2c7040602c63ca"},
+ },
+ { // {0:0cccc, 2:456{0:0, 2:2}
+ {"00cccc", "v_______________________3___0", "e57dc2785b99ce9205080cb41b32ebea7ac3e158952b44c87d186e6d190a6530"},
+ {"245600", "v_______________________3___1", "0335354adbd360a45c1871a842452287721b64b4234dfe08760b243523c998db"},
+ {"245622", "v_______________________3___2", "9e6832db0dca2b5cf81c0e0727bfde6afc39d5de33e5720bccacc183c162104e"},
+ },
+ { // {1:4567{1:1c, 3:3c}, 3:0cccccc}
+ {"1456711c", "v_______________________4___0", "f2389e78d98fed99f3e63d6d1623c1d4d9e8c91cb1d585de81fbc7c0e60d3529"},
+ {"1456733c", "v_______________________4___1", "101189b3fab852be97a0120c03d95eefcf984d3ed639f2328527de6def55a9c0"},
+ {"30cccccc", "v_______________________4___2", "3780ce111f98d15751dfde1eb21080efc7d3914b429e5c84c64db637c55405b3"},
+ },
+ { // 8800{1:f, 2:e, 3:d}
+ {"88001f", "v_______________________5___0", "e817db50d84f341d443c6f6593cafda093fc85e773a762421d47daa6ac993bd5"},
+ {"88002e", "v_______________________5___1", "d6e3e6047bdc110edd296a4d63c030aec451bee9d8075bc5a198eee8cda34f68"},
+ {"88003d", "v_______________________5___2", "b6bdf8298c703342188e5f7f84921a402042d0e5fb059969dd53a6b6b1fb989e"},
+ },
+ { // 0{1:fc, 2:ec, 4:dc}
+ {"01fc", "v_______________________6___0", "693268f2ca80d32b015f61cd2c4dba5a47a6b52a14c34f8e6945fad684e7a0d5"},
+ {"02ec", "v_______________________6___1", "e24ddd44469310c2b785a2044618874bf486d2f7822603a9b8dce58d6524d5de"},
+ {"04dc", "v_______________________6___2", "33fc259629187bbe54b92f82f0cd8083b91a12e41a9456b84fc155321e334db7"},
+ },
+ { // f{0:fccc, f:ff{0:f, f:f}}
+ {"f0fccc", "v_______________________7___0", "b0966b5aa469a3e292bc5fcfa6c396ae7a657255eef552ea7e12f996de795b90"},
+ {"ffff0f", "v_______________________7___1", "3b1ca154ec2a3d96d8d77bddef0abfe40a53a64eb03cecf78da9ec43799fa3d0"},
+ {"ffffff", "v_______________________7___2", "e75463041f1be8252781be0ace579a44ea4387bf5b2739f4607af676f7719678"},
+ },
+ { // ff{0:f{0:f, f:f}, f:fcc}
+ {"ff0f0f", "v_______________________8___0", "0928af9b14718ec8262ab89df430f1e5fbf66fac0fed037aff2b6767ae8c8684"},
+ {"ff0fff", "v_______________________8___1", "d870f4d3ce26b0bf86912810a1960693630c20a48ba56be0ad04bc3e9ddb01e6"},
+ {"ffffcc", "v_______________________8___2", "4239f10dd9d9915ecf2e047d6a576bdc1733ed77a30830f1bf29deaf7d8e966f"},
+ },
+ {
+ {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"},
+ {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"},
+ {"123f", "x___________________________2", "1164d7299964e74ac40d761f9189b2a3987fae959800d0f7e29d3aaf3eae9e15"},
+ },
+ {
+ {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"},
+ {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"},
+ {"124a", "x___________________________2", "661a96a669869d76b7231380da0649d013301425fbea9d5c5fae6405aa31cfce"},
+ },
+ {
+ {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"},
+ {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"},
+ {"13aa", "x___________________________2", "6590120e1fd3ffd1a90e8de5bb10750b61079bb0776cca4414dd79a24e4d4356"},
+ },
+ {
+ {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"},
+ {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"},
+ {"2aaa", "x___________________________2", "f869b40e0c55eace1918332ef91563616fbf0755e2b946119679f7ef8e44b514"},
+ },
+ {
+ {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"},
+ {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"},
+ {"1234fa", "x___________________________2", "4f4e368ab367090d5bc3dbf25f7729f8bd60df84de309b4633a6b69ab66142c0"},
+ },
+ {
+ {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"},
+ {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"},
+ {"1235aa", "x___________________________2", "21840121d11a91ac8bbad9a5d06af902a5c8d56a47b85600ba813814b7bfcb9b"},
+ },
+ {
+ {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"},
+ {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"},
+ {"124aaa", "x___________________________2", "ea4040ddf6ae3fbd1524bdec19c0ab1581015996262006632027fa5cf21e441e"},
+ },
+ {
+ {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"},
+ {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"},
+ {"13aaaa", "x___________________________2", "e4beb66c67e44f2dd8ba36036e45a44ff68f8d52942472b1911a45f886a34507"},
+ },
+ {
+ {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"},
+ {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"},
+ {"2aaaaa", "x___________________________2", "5f5989b820ff5d76b7d49e77bb64f26602294f6c42a1a3becc669cd9e0dc8ec9"},
+ },
+ {
+ {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"},
+ {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"},
+ {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"},
+ {"1234fa", "x___________________________3", "65bb3aafea8121111d693ffe34881c14d27b128fd113fa120961f251fe28428d"},
+ },
+ {
+ {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"},
+ {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"},
+ {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"},
+ {"1235aa", "x___________________________3", "f670e4d2547c533c5f21e0045442e2ecb733f347ad6d29ef36e0f5ba31bb11a8"},
+ },
+ {
+ {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"},
+ {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"},
+ {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"},
+ {"124aaa", "x___________________________3", "c17464123050a9a6f29b5574bb2f92f6d305c1794976b475b7fb0316b6335598"},
+ },
+ {
+ {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"},
+ {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"},
+ {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"},
+ {"13aaaa", "x___________________________3", "aa8301be8cb52ea5cd249f5feb79fb4315ee8de2140c604033f4b3fff78f0105"},
+ },
+ {
+ {"0000", "x___________________________0", "cb8c09ad07ae882136f602b3f21f8733a9f5a78f1d2525a8d24d1c13258000b2"},
+ {"123d", "x___________________________1", "8f09663deb02f08958136410dc48565e077f76bb6c9d8c84d35fc8913a657d31"},
+ {"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"},
+ {"123f", "x___________________________3", "80f7bad1893ca57e3443bb3305a517723a74d3ba831bcaca22a170645eb7aafb"},
+ },
+ {
+ {"0000", "x___________________________0", "cb8c09ad07ae882136f602b3f21f8733a9f5a78f1d2525a8d24d1c13258000b2"},
+ {"123d", "x___________________________1", "8f09663deb02f08958136410dc48565e077f76bb6c9d8c84d35fc8913a657d31"},
+ {"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"},
+ {"124a", "x___________________________3", "383bc1bb4f019e6bc4da3751509ea709b58dd1ac46081670834bae072f3e9557"},
+ },
+ {
+ {"0000", "x___________________________0", "cb8c09ad07ae882136f602b3f21f8733a9f5a78f1d2525a8d24d1c13258000b2"},
+ {"123d", "x___________________________1", "8f09663deb02f08958136410dc48565e077f76bb6c9d8c84d35fc8913a657d31"},
+ {"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"},
+ {"13aa", "x___________________________3", "ff0dc70ce2e5db90ee42a4c2ad12139596b890e90eb4e16526ab38fa465b35cf"},
+ },
+ }
+ st := NewStackTrie(nil)
+ for i, test := range tests {
+ // The StackTrie does not allow Insert(), Hash(), Insert(), ...
+ // so we will create new trie for every sequence length of inserts.
+ for l := 1; l <= len(test); l++ {
+ st.Reset()
+ for j := 0; j < l; j++ {
+ kv := &test[j]
+ if err := st.TryUpdate(common.FromHex(kv.K), []byte(kv.V)); err != nil {
+ t.Fatal(err)
+ }
+ }
+ expected := common.HexToHash(test[l-1].H)
+ if h := st.Hash(); h != expected {
+ t.Errorf("%d(%d): root hash mismatch: %x, expected %x", i, l, h, expected)
+ }
+ }
+ }
+}
+
func TestSizeBug(t *testing.T) {
st := NewStackTrie(nil)
nt, _ := New(common.Hash{}, NewDatabase(memorydb.New()))