mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
dev: chg: more regression changes for bor after merge
This commit is contained in:
parent
162af07d38
commit
d3f6da90e1
47 changed files with 253 additions and 1271 deletions
|
|
@ -35,7 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
cli "github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||||
t.Logf("Post-merge header: %d", block.NumberU64())
|
t.Logf("Post-merge header: %d", block.NumberU64())
|
||||||
}
|
}
|
||||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
// Verify the blocks before the merging
|
// Verify the blocks before the merging
|
||||||
|
|
|
||||||
|
|
@ -238,10 +238,10 @@ type BlockChain struct {
|
||||||
vmConfig vm.Config
|
vmConfig vm.Config
|
||||||
|
|
||||||
// Bor related changes
|
// Bor related changes
|
||||||
borReceiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Cache for the most recent bor receipt receipts per block
|
borReceiptsCache *lru.Cache[common.Hash, *types.Receipt] // Cache for the most recent bor receipt receipts per block
|
||||||
stateSyncData []*types.StateSyncData // State sync data
|
stateSyncData []*types.StateSyncData // State sync data
|
||||||
stateSyncFeed event.Feed // State sync feed
|
stateSyncFeed event.Feed // State sync feed
|
||||||
chain2HeadFeed event.Feed // Reorg/NewHead/Fork data feed
|
chain2HeadFeed event.Feed // Reorg/NewHead/Fork data feed
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlockChain returns a fully initialised block chain using information
|
// NewBlockChain returns a fully initialised block chain using information
|
||||||
|
|
@ -295,7 +295,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
engine: engine,
|
engine: engine,
|
||||||
vmConfig: vmConfig,
|
vmConfig: vmConfig,
|
||||||
|
|
||||||
borReceiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
|
borReceiptsCache: lru.NewCache[common.Hash, *types.Receipt](receiptsCacheLimit),
|
||||||
}
|
}
|
||||||
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
|
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
|
||||||
bc.forker = NewForkChoice(bc, shouldPreserve, checker)
|
bc.forker = NewForkChoice(bc, shouldPreserve, checker)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ func TestChain2HeadEvent(t *testing.T) {
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
chain2HeadCh := make(chan Chain2HeadEvent, 64)
|
chain2HeadCh := make(chan Chain2HeadEvent, 64)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
// GetBorReceiptByHash retrieves the bor block receipt in a given block.
|
// GetBorReceiptByHash retrieves the bor block receipt in a given block.
|
||||||
func (bc *BlockChain) GetBorReceiptByHash(hash common.Hash) *types.Receipt {
|
func (bc *BlockChain) GetBorReceiptByHash(hash common.Hash) *types.Receipt {
|
||||||
if receipt, ok := bc.borReceiptsCache.Get(hash); ok {
|
if receipt, ok := bc.borReceiptsCache.Get(hash); ok {
|
||||||
return receipt.(*types.Receipt)
|
return receipt
|
||||||
}
|
}
|
||||||
|
|
||||||
// read header from hash
|
// read header from hash
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ func TestGenerateWithdrawalChain(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec, nil, beacon.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec, nil, beacon.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -33,11 +34,12 @@ func TestPastChainInsert(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||||
)
|
)
|
||||||
|
|
||||||
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
gspec.Commit(db, trie.NewDatabase(db))
|
||||||
|
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -48,11 +50,11 @@ func TestPastChainInsert(t *testing.T) {
|
||||||
}
|
}
|
||||||
validate := func(currentHeader *types.Header, chain []*types.Header) (bool, error) {
|
validate := func(currentHeader *types.Header, chain []*types.Header) (bool, error) {
|
||||||
// Put all explicit conditions here
|
// Put all explicit conditions here
|
||||||
// If canonical chain is empty and we're importing a chain of 64 blocks
|
// If canonical chain is empty, and we're importing a chain of 64 blocks
|
||||||
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
if currentHeader.Number.Uint64() == uint64(0) && len(chain) == 64 {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
// If canonical chain is of len 64 and we're importing a past chain from 54-64, then accept it
|
// If canonical chain is of len 64, and we're importing a past chain from 54-64, then accept it
|
||||||
if currentHeader.Number.Uint64() == uint64(64) && chain[0].Number.Uint64() == 55 && len(chain) == 10 {
|
if currentHeader.Number.Uint64() == uint64(64) && chain[0].Number.Uint64() == 55 && len(chain) == 10 {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +66,7 @@ func TestPastChainInsert(t *testing.T) {
|
||||||
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||||
|
|
||||||
// chain A: G->A1->A2...A64
|
// chain A: G->A1->A2...A64
|
||||||
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
genDb, chainA := makeHeaderChainWithGenesis(gspec, 64, ethash.NewFaker(), 10)
|
||||||
|
|
||||||
// Inserting 64 headers on an empty chain
|
// Inserting 64 headers on an empty chain
|
||||||
// expecting 1 write status with no error
|
// expecting 1 write status with no error
|
||||||
|
|
@ -72,11 +74,11 @@ func TestPastChainInsert(t *testing.T) {
|
||||||
|
|
||||||
// The current chain is: G->A1->A2...A64
|
// The current chain is: G->A1->A2...A64
|
||||||
// chain B: G->A1->A2...A44->B45->B46...B64
|
// chain B: G->A1->A2...A44->B45->B46...B64
|
||||||
chainB := makeHeaderChain(chainA[43], 20, ethash.NewFaker(), db, 10)
|
chainB := makeHeaderChain(gspec.Config, chainA[43], 20, ethash.NewFaker(), genDb, 10)
|
||||||
|
|
||||||
// The current chain is: G->A1->A2...A64
|
// The current chain is: G->A1->A2...A64
|
||||||
// chain C: G->A1->A2...A54->C55->C56...C64
|
// chain C: G->A1->A2...A54->C55->C56...C64
|
||||||
chainC := makeHeaderChain(chainA[53], 10, ethash.NewFaker(), db, 10)
|
chainC := makeHeaderChain(gspec.Config, chainA[53], 10, ethash.NewFaker(), genDb, 10)
|
||||||
|
|
||||||
// Update the function to consider chainC with higher difficulty
|
// Update the function to consider chainC with higher difficulty
|
||||||
getTd = func(hash common.Hash, number uint64) *big.Int {
|
getTd = func(hash common.Hash, number uint64) *big.Int {
|
||||||
|
|
@ -103,11 +105,12 @@ func TestFutureChainInsert(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||||
)
|
)
|
||||||
|
|
||||||
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
gspec.Commit(db, trie.NewDatabase(db))
|
||||||
|
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +137,7 @@ func TestFutureChainInsert(t *testing.T) {
|
||||||
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||||
|
|
||||||
// chain A: G->A1->A2...A64
|
// chain A: G->A1->A2...A64
|
||||||
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
genDb, chainA := makeHeaderChainWithGenesis(gspec, 64, ethash.NewFaker(), 10)
|
||||||
|
|
||||||
// Inserting 64 headers on an empty chain
|
// Inserting 64 headers on an empty chain
|
||||||
// expecting 1 write status with no error
|
// expecting 1 write status with no error
|
||||||
|
|
@ -142,7 +145,7 @@ func TestFutureChainInsert(t *testing.T) {
|
||||||
|
|
||||||
// The current chain is: G->A1->A2...A64
|
// The current chain is: G->A1->A2...A64
|
||||||
// chain B: G->A1->A2...A64->B65->B66...B84
|
// chain B: G->A1->A2...A64->B65->B66...B84
|
||||||
chainB := makeHeaderChain(chainA[63], 20, ethash.NewFaker(), db, 10)
|
chainB := makeHeaderChain(gspec.Config, chainA[63], 20, ethash.NewFaker(), genDb, 10)
|
||||||
|
|
||||||
// Inserting 20 headers on the canonical chain
|
// Inserting 20 headers on the canonical chain
|
||||||
// expecting 0 write status with no error
|
// expecting 0 write status with no error
|
||||||
|
|
@ -150,7 +153,7 @@ func TestFutureChainInsert(t *testing.T) {
|
||||||
|
|
||||||
// The current chain is: G->A1->A2...A64
|
// The current chain is: G->A1->A2...A64
|
||||||
// chain C: G->A1->A2...A64->C65->C66...C74
|
// chain C: G->A1->A2...A64->C65->C66...C74
|
||||||
chainC := makeHeaderChain(chainA[63], 10, ethash.NewFaker(), db, 10)
|
chainC := makeHeaderChain(gspec.Config, chainA[63], 10, ethash.NewFaker(), genDb, 10)
|
||||||
|
|
||||||
// Inserting 10 headers on the canonical chain
|
// Inserting 10 headers on the canonical chain
|
||||||
// expecting 0 write status with no error
|
// expecting 0 write status with no error
|
||||||
|
|
@ -161,11 +164,12 @@ func TestOverlappingChainInsert(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||||
)
|
)
|
||||||
|
|
||||||
hc, err := NewHeaderChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), func() bool { return false })
|
gspec.Commit(db, trie.NewDatabase(db))
|
||||||
|
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +196,7 @@ func TestOverlappingChainInsert(t *testing.T) {
|
||||||
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
mockForker := NewForkChoice(mockChainReader, nil, mockChainValidator)
|
||||||
|
|
||||||
// chain A: G->A1->A2...A64
|
// chain A: G->A1->A2...A64
|
||||||
chainA := makeHeaderChain(genesis.Header(), 64, ethash.NewFaker(), db, 10)
|
genDb, chainA := makeHeaderChainWithGenesis(gspec, 64, ethash.NewFaker(), 10)
|
||||||
|
|
||||||
// Inserting 64 headers on an empty chain
|
// Inserting 64 headers on an empty chain
|
||||||
// expecting 1 write status with no error
|
// expecting 1 write status with no error
|
||||||
|
|
@ -200,7 +204,7 @@ func TestOverlappingChainInsert(t *testing.T) {
|
||||||
|
|
||||||
// The current chain is: G->A1->A2...A64
|
// The current chain is: G->A1->A2...A64
|
||||||
// chain B: G->A1->A2...A54->B55->B56...B84
|
// chain B: G->A1->A2...A54->B55->B56...B84
|
||||||
chainB := makeHeaderChain(chainA[53], 30, ethash.NewFaker(), db, 10)
|
chainB := makeHeaderChain(gspec.Config, chainA[53], 30, ethash.NewFaker(), genDb, 10)
|
||||||
|
|
||||||
// Inserting 20 blocks on canonical chain
|
// Inserting 20 blocks on canonical chain
|
||||||
// expecting 2 write status with no error
|
// expecting 2 write status with no error
|
||||||
|
|
@ -208,7 +212,7 @@ func TestOverlappingChainInsert(t *testing.T) {
|
||||||
|
|
||||||
// The current chain is: G->A1->A2...A64
|
// The current chain is: G->A1->A2...A64
|
||||||
// chain C: G->A1->A2...A54->C55->C56...C74
|
// chain C: G->A1->A2...A54->C55->C56...C74
|
||||||
chainC := makeHeaderChain(chainA[53], 20, ethash.NewFaker(), db, 10)
|
chainC := makeHeaderChain(gspec.Config, chainA[53], 20, ethash.NewFaker(), genDb, 10)
|
||||||
|
|
||||||
// Inserting 10 blocks on canonical chain
|
// Inserting 10 blocks on canonical chain
|
||||||
// expecting 1 write status with no error
|
// expecting 1 write status with no error
|
||||||
|
|
|
||||||
|
|
@ -345,7 +345,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil, nil)
|
||||||
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||||
smallInitCode = [320]byte{}
|
smallInitCode = [320]byte{}
|
||||||
)
|
)
|
||||||
|
|
@ -386,7 +386,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
|
func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
|
||||||
difficulty := big.NewInt(0)
|
difficulty := big.NewInt(0)
|
||||||
if !config.TerminalTotalDifficultyPassed {
|
if !config.TerminalTotalDifficultyPassed {
|
||||||
difficulty = engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
|
difficulty = engine.CalcDifficulty(&fakeChainReader{config: config}, parent.Time()+10, &types.Header{
|
||||||
Number: parent.Number(),
|
Number: parent.Number(),
|
||||||
Time: parent.Time(),
|
Time: parent.Time(),
|
||||||
Difficulty: parent.Difficulty(),
|
Difficulty: parent.Difficulty(),
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
|
@ -28,6 +28,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO marcello merge this file with txpool2_test.go
|
||||||
|
|
||||||
func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
|
func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
|
||||||
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(value), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
|
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(value), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
|
||||||
return tx
|
return tx
|
||||||
|
|
@ -36,9 +38,9 @@ func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gaspric
|
||||||
func count(t *testing.T, pool *TxPool) (pending int, queued int) {
|
func count(t *testing.T, pool *TxPool) (pending int, queued int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
pending, queued = pool.stats()
|
pending, queued = pool.Stats()
|
||||||
|
|
||||||
if err := validateTxPoolInternals(pool); err != nil {
|
if err := validatePoolInternals(pool); err != nil {
|
||||||
t.Fatalf("pool internal state corrupted: %v", err)
|
t.Fatalf("pool internal state corrupted: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +57,7 @@ func fillPool(t *testing.T, pool *TxPool) {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000))
|
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000))
|
||||||
// Add executable ones
|
// Add executable ones
|
||||||
for j := 0; j < int(pool.config.AccountSlots); j++ {
|
for j := 0; j < int(pool.config().AccountSlots); j++ {
|
||||||
executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key))
|
executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +53,8 @@ import (
|
||||||
"github.com/JekaMas/crand"
|
"github.com/JekaMas/crand"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO marcello merge this file with txpool2_test_BOR.go
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// testTxPoolConfig is a transaction pool configuration without stateful disk
|
// testTxPoolConfig is a transaction pool configuration without stateful disk
|
||||||
// sideeffects used during testing.
|
// sideeffects used during testing.
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsLegacyStoredReceipts tries to parse the RLP-encoded blob
|
|
||||||
// first as an array of v3 stored receipt, then v4 stored receipt and
|
|
||||||
// returns true if successful.
|
|
||||||
func IsLegacyStoredReceipts(raw []byte) (bool, error) {
|
|
||||||
var v3 []v3StoredReceiptRLP
|
|
||||||
if err := rlp.DecodeBytes(raw, &v3); err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
var v4 []v4StoredReceiptRLP
|
|
||||||
if err := rlp.DecodeBytes(raw, &v4); err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
var v5 []storedReceiptRLP
|
|
||||||
// Check to see valid fresh stored receipt
|
|
||||||
if err := rlp.DecodeBytes(raw, &v5); err == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, errors.New("value is not a valid receipt encoding")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConvertLegacyStoredReceipts takes the RLP encoding of an array of legacy
|
|
||||||
// stored receipts and returns a fresh RLP-encoded stored receipt.
|
|
||||||
func ConvertLegacyStoredReceipts(raw []byte) ([]byte, error) {
|
|
||||||
var receipts []ReceiptForStorage
|
|
||||||
if err := rlp.DecodeBytes(raw, &receipts); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return rlp.EncodeToBytes(&receipts)
|
|
||||||
}
|
|
||||||
|
|
@ -56,7 +56,7 @@ type Transaction struct {
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
hash atomic.Pointer[common.Hash]
|
hash atomic.Pointer[common.Hash]
|
||||||
size atomic.Pointer[common.StorageSize]
|
size atomic.Pointer[uint64]
|
||||||
from atomic.Pointer[sigCache]
|
from atomic.Pointer[sigCache]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,8 +207,7 @@ func (tx *Transaction) setDecoded(inner TxData, size uint64) {
|
||||||
tx.inner = inner
|
tx.inner = inner
|
||||||
tx.time = time.Now()
|
tx.time = time.Now()
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
v := float64(size)
|
tx.size.Store(&size)
|
||||||
tx.size.Store((*common.StorageSize)(&v))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -486,17 +485,19 @@ func (tx *Transaction) Hash() common.Hash {
|
||||||
|
|
||||||
// Size returns the true encoded storage size of the transaction, either by encoding
|
// Size returns the true encoded storage size of the transaction, either by encoding
|
||||||
// and returning it, or returning a previously cached value.
|
// and returning it, or returning a previously cached value.
|
||||||
func (tx *Transaction) Size() common.StorageSize {
|
func (tx *Transaction) Size() uint64 {
|
||||||
if size := tx.size.Load(); size != nil {
|
if size := tx.size.Load(); size != nil {
|
||||||
return *size
|
return *size
|
||||||
}
|
}
|
||||||
|
|
||||||
c := writeCounter(0)
|
c := writeCounter(0)
|
||||||
|
|
||||||
rlp.Encode(&c, &tx.inner)
|
rlp.Encode(&c, &tx.inner)
|
||||||
tx.size.Store((*common.StorageSize)(&c))
|
|
||||||
|
|
||||||
return common.StorageSize(c)
|
size := uint64(c)
|
||||||
|
if tx.Type() != LegacyTxType {
|
||||||
|
size += 1 // type byte
|
||||||
|
}
|
||||||
|
tx.size.Store(&size)
|
||||||
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSignature returns a new transaction with the given signature.
|
// WithSignature returns a new transaction with the given signature.
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ func TestCreateGas(t *testing.T) {
|
||||||
|
|
||||||
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
|
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
|
||||||
var startGas = uint64(testGas)
|
var startGas = uint64(testGas)
|
||||||
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
|
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package filters
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -22,7 +21,7 @@ func (es *EventSystem) SubscribeNewDeposits(data chan *types.StateSyncData) *Sub
|
||||||
typ: StateSyncSubscription,
|
typ: StateSyncSubscription,
|
||||||
created: time.Now(),
|
created: time.Now(),
|
||||||
logs: make(chan []*types.Log),
|
logs: make(chan []*types.Log),
|
||||||
hashes: make(chan []common.Hash),
|
txs: make(chan []*types.Transaction),
|
||||||
headers: make(chan *types.Header),
|
headers: make(chan *types.Header),
|
||||||
stateSyncData: data,
|
stateSyncData: data,
|
||||||
installed: make(chan struct{}),
|
installed: make(chan struct{}),
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ func TestBorFilters(t *testing.T) {
|
||||||
// Block 1
|
// Block 1
|
||||||
backend.expectBorReceiptsFromMock([]*common.Hash{nil, &hash1, &hash2, &hash3, &hash4})
|
backend.expectBorReceiptsFromMock([]*common.Hash{nil, &hash1, &hash2, &hash3, &hash4})
|
||||||
|
|
||||||
|
// TODO marcello regenerate ctrls for gomock to include the new methods
|
||||||
filter := NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, 18, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
|
filter := NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, 18, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
|
||||||
logs, err := filter.Logs(context.Background())
|
logs, err := filter.Logs(context.Background())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ type testBackend struct {
|
||||||
rmLogsFeed event.Feed
|
rmLogsFeed event.Feed
|
||||||
pendingLogsFeed event.Feed
|
pendingLogsFeed event.Feed
|
||||||
chainFeed event.Feed
|
chainFeed event.Feed
|
||||||
|
|
||||||
|
stateSyncFeed event.Feed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
|
||||||
|
return b.stateSyncFeed.Subscribe(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) ChainConfig() *params.ChainConfig {
|
func (b *testBackend) ChainConfig() *params.ChainConfig {
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,11 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: discuss consequences of setting preferDisk false.
|
// TODO: discuss consequences of setting preferDisk false.
|
||||||
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
|
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer release()
|
||||||
|
|
||||||
// Execute all the transaction contained within the block concurrently
|
// Execute all the transaction contained within the block concurrently
|
||||||
var (
|
var (
|
||||||
|
|
@ -77,25 +78,25 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
|
||||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
|
|
||||||
traceTxn := func(indx int, tx *types.Transaction, borTx bool) *TxTraceResult {
|
traceTxn := func(indx int, tx *types.Transaction, borTx bool) *TxTraceResult {
|
||||||
message, _ := tx.AsMessage(signer, block.BaseFee())
|
message, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
txContext := core.NewEVMTxContext(message)
|
txContext := core.NewEVMTxContext(message)
|
||||||
|
|
||||||
tracer := logger.NewStructLogger(config.Config)
|
tracer := logger.NewStructLogger(config.Config)
|
||||||
|
|
||||||
// Run the transaction with tracing enabled.
|
// Run the transaction with tracing enabled.
|
||||||
vmenv := vm.NewEVM(blockCtx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
|
vmenv := vm.NewEVM(blockCtx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true})
|
||||||
|
|
||||||
// Call Prepare to clear out the statedb access list
|
// Call Prepare to clear out the statedb access list
|
||||||
// Not sure if we need to do this
|
// Not sure if we need to do this
|
||||||
statedb.Prepare(tx.Hash(), indx)
|
statedb.SetTxContext(tx.Hash(), indx)
|
||||||
|
|
||||||
var execRes *core.ExecutionResult
|
var execRes *core.ExecutionResult
|
||||||
|
|
||||||
if borTx {
|
if borTx {
|
||||||
callmsg := prepareCallMessage(message)
|
callmsg := prepareCallMessage(*message)
|
||||||
execRes, err = statefull.ApplyBorMessage(*vmenv, callmsg)
|
execRes, err = statefull.ApplyBorMessage(*vmenv, callmsg)
|
||||||
} else {
|
} else {
|
||||||
execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), nil)
|
execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,880 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// 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, env.Context.Random != nil)
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/*
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
@ -504,6 +504,38 @@ func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
return (*big.Int)(&hex), nil
|
return (*big.Int)(&hex), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type feeHistoryResultMarshaling struct {
|
||||||
|
OldestBlock *hexutil.Big `json:"oldestBlock"`
|
||||||
|
Reward [][]*hexutil.Big `json:"reward,omitempty"`
|
||||||
|
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
|
||||||
|
GasUsedRatio []float64 `json:"gasUsedRatio"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeeHistory retrieves the fee market history.
|
||||||
|
func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) {
|
||||||
|
var res feeHistoryResultMarshaling
|
||||||
|
if err := ec.c.CallContext(ctx, &res, "eth_feeHistory", hexutil.Uint(blockCount), toBlockNumArg(lastBlock), rewardPercentiles); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reward := make([][]*big.Int, len(res.Reward))
|
||||||
|
for i, r := range res.Reward {
|
||||||
|
reward[i] = make([]*big.Int, len(r))
|
||||||
|
for j, r := range r {
|
||||||
|
reward[i][j] = (*big.Int)(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
baseFee := make([]*big.Int, len(res.BaseFee))
|
||||||
|
for i, b := range res.BaseFee {
|
||||||
|
baseFee[i] = (*big.Int)(b)
|
||||||
|
}
|
||||||
|
return ðereum.FeeHistory{
|
||||||
|
OldestBlock: (*big.Int)(res.OldestBlock),
|
||||||
|
Reward: reward,
|
||||||
|
BaseFee: baseFee,
|
||||||
|
GasUsedRatio: res.GasUsedRatio,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
|
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
|
||||||
// the current pending state of the backend blockchain. There is no guarantee that this is
|
// the current pending state of the backend blockchain. There is no guarantee that this is
|
||||||
// the true gas limit requirement as other transactions may be added or removed by miners,
|
// the true gas limit requirement as other transactions may be added or removed by miners,
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
||||||
filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{})
|
filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{})
|
||||||
n.RegisterAPIs([]rpc.API{{
|
n.RegisterAPIs([]rpc.API{{
|
||||||
Namespace: "eth",
|
Namespace: "eth",
|
||||||
Service: filters.NewFilterAPI(filterSystem, false),
|
Service: filters.NewFilterAPI(filterSystem, false, config.BorLogs),
|
||||||
}})
|
}})
|
||||||
|
|
||||||
// Import the test chain.
|
// Import the test chain.
|
||||||
|
|
|
||||||
|
|
@ -385,59 +385,3 @@ func (snap *snapshot) Release() {
|
||||||
|
|
||||||
snap.db = nil
|
snap.db = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// snapshot wraps a batch of key-value entries deep copied from the in-memory
|
|
||||||
// database for implementing the Snapshot interface.
|
|
||||||
type snapshot struct {
|
|
||||||
db map[string][]byte
|
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// newSnapshot initializes the snapshot with the given database instance.
|
|
||||||
func newSnapshot(db *Database) *snapshot {
|
|
||||||
db.lock.RLock()
|
|
||||||
defer db.lock.RUnlock()
|
|
||||||
|
|
||||||
copied := make(map[string][]byte)
|
|
||||||
for key, val := range db.db {
|
|
||||||
copied[key] = common.CopyBytes(val)
|
|
||||||
}
|
|
||||||
return &snapshot{db: copied}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has retrieves if a key is present in the snapshot backing by a key-value
|
|
||||||
// data store.
|
|
||||||
func (snap *snapshot) Has(key []byte) (bool, error) {
|
|
||||||
snap.lock.RLock()
|
|
||||||
defer snap.lock.RUnlock()
|
|
||||||
|
|
||||||
if snap.db == nil {
|
|
||||||
return false, errSnapshotReleased
|
|
||||||
}
|
|
||||||
_, ok := snap.db[string(key)]
|
|
||||||
return ok, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the given key if it's present in the snapshot backing by
|
|
||||||
// key-value data store.
|
|
||||||
func (snap *snapshot) Get(key []byte) ([]byte, error) {
|
|
||||||
snap.lock.RLock()
|
|
||||||
defer snap.lock.RUnlock()
|
|
||||||
|
|
||||||
if snap.db == nil {
|
|
||||||
return nil, errSnapshotReleased
|
|
||||||
}
|
|
||||||
if entry, ok := snap.db[string(key)]; ok {
|
|
||||||
return common.CopyBytes(entry), nil
|
|
||||||
}
|
|
||||||
return nil, errMemorydbNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release releases associated resources. Release should always succeed and can
|
|
||||||
// be called multiple times without causing error.
|
|
||||||
func (snap *snapshot) Release() {
|
|
||||||
snap.lock.Lock()
|
|
||||||
defer snap.lock.Unlock()
|
|
||||||
|
|
||||||
snap.db = nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ var mainnetBor = &Chain{
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
DAOForkBlock: nil,
|
DAOForkBlock: nil,
|
||||||
DAOForkSupport: true,
|
DAOForkSupport: true,
|
||||||
EIP150Hash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
EIP150Block: big.NewInt(0),
|
EIP150Block: big.NewInt(0),
|
||||||
EIP155Block: big.NewInt(0),
|
EIP155Block: big.NewInt(0),
|
||||||
EIP158Block: big.NewInt(0),
|
EIP158Block: big.NewInt(0),
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ var mumbaiTestnet = &Chain{
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
DAOForkBlock: nil,
|
DAOForkBlock: nil,
|
||||||
DAOForkSupport: true,
|
DAOForkSupport: true,
|
||||||
EIP150Hash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
EIP150Block: big.NewInt(0),
|
EIP150Block: big.NewInt(0),
|
||||||
EIP155Block: big.NewInt(0),
|
EIP155Block: big.NewInt(0),
|
||||||
EIP158Block: big.NewInt(0),
|
EIP158Block: big.NewInt(0),
|
||||||
|
|
|
||||||
|
|
@ -897,8 +897,8 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
|
||||||
{
|
{
|
||||||
n.GPO.Blocks = int(c.Gpo.Blocks)
|
n.GPO.Blocks = int(c.Gpo.Blocks)
|
||||||
n.GPO.Percentile = int(c.Gpo.Percentile)
|
n.GPO.Percentile = int(c.Gpo.Percentile)
|
||||||
n.GPO.MaxHeaderHistory = c.Gpo.MaxHeaderHistory
|
n.GPO.MaxHeaderHistory = uint64(c.Gpo.MaxHeaderHistory)
|
||||||
n.GPO.MaxBlockHistory = c.Gpo.MaxBlockHistory
|
n.GPO.MaxBlockHistory = uint64(c.Gpo.MaxBlockHistory)
|
||||||
n.GPO.MaxPrice = c.Gpo.MaxPrice
|
n.GPO.MaxPrice = c.Gpo.MaxPrice
|
||||||
n.GPO.IgnorePrice = c.Gpo.IgnorePrice
|
n.GPO.IgnorePrice = c.Gpo.IgnorePrice
|
||||||
}
|
}
|
||||||
|
|
@ -1022,7 +1022,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
|
||||||
|
|
||||||
// RequiredBlocks
|
// RequiredBlocks
|
||||||
{
|
{
|
||||||
n.PeerRequiredBlocks = map[uint64]common.Hash{}
|
n.RequiredBlocks = map[uint64]common.Hash{}
|
||||||
for k, v := range c.RequiredBlocks {
|
for k, v := range c.RequiredBlocks {
|
||||||
number, err := strconv.ParseUint(k, 0, 64)
|
number, err := strconv.ParseUint(k, 0, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1034,7 +1034,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
|
||||||
return nil, fmt.Errorf("invalid required block hash %s: %v", v, err)
|
return nil, fmt.Errorf("invalid required block hash %s: %v", v, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.PeerRequiredBlocks[number] = hash
|
n.RequiredBlocks[number] = hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package server
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
|
@ -165,6 +167,8 @@ func NewServer(config *Config, opts ...serverOption) (*Server, error) {
|
||||||
// flag to set if we're authorizing consensus here
|
// flag to set if we're authorizing consensus here
|
||||||
authorized := false
|
authorized := false
|
||||||
|
|
||||||
|
var ethCfg *ethconfig.Config
|
||||||
|
|
||||||
// check if personal wallet endpoints are disabled or not
|
// check if personal wallet endpoints are disabled or not
|
||||||
// nolint:nestif
|
// nolint:nestif
|
||||||
if !config.Accounts.DisableBorWallet {
|
if !config.Accounts.DisableBorWallet {
|
||||||
|
|
@ -172,7 +176,7 @@ func NewServer(config *Config, opts ...serverOption) (*Server, error) {
|
||||||
stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
|
stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
|
||||||
|
|
||||||
// register the ethereum backend
|
// register the ethereum backend
|
||||||
ethCfg, err := config.buildEth(stack, stack.AccountManager())
|
ethCfg, err = config.buildEth(stack, stack.AccountManager())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +189,7 @@ func NewServer(config *Config, opts ...serverOption) (*Server, error) {
|
||||||
srv.backend = backend
|
srv.backend = backend
|
||||||
} else {
|
} else {
|
||||||
// register the ethereum backend (with temporary created account manager)
|
// register the ethereum backend (with temporary created account manager)
|
||||||
ethCfg, err := config.buildEth(stack, accountManager)
|
ethCfg, err = config.buildEth(stack, accountManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -244,13 +248,15 @@ func NewServer(config *Config, opts ...serverOption) (*Server, error) {
|
||||||
// set the auth status in backend
|
// set the auth status in backend
|
||||||
srv.backend.SetAuthorized(authorized)
|
srv.backend.SetAuthorized(authorized)
|
||||||
|
|
||||||
|
filterSystem := utils.RegisterFilterAPI(stack, srv.backend.APIBackend, ethCfg)
|
||||||
|
|
||||||
// debug tracing is enabled by default
|
// debug tracing is enabled by default
|
||||||
stack.RegisterAPIs(tracers.APIs(srv.backend.APIBackend))
|
stack.RegisterAPIs(tracers.APIs(srv.backend.APIBackend))
|
||||||
srv.tracerAPI = tracers.NewAPI(srv.backend.APIBackend)
|
srv.tracerAPI = tracers.NewAPI(srv.backend.APIBackend)
|
||||||
|
|
||||||
// graphql is started from another place
|
// graphql is started from another place
|
||||||
if config.JsonRPC.Graphql.Enabled {
|
if config.JsonRPC.Graphql.Enabled {
|
||||||
if err := graphql.New(stack, srv.backend.APIBackend, config.JsonRPC.Graphql.Cors, config.JsonRPC.Graphql.VHost); err != nil {
|
if err := graphql.New(stack, srv.backend.APIBackend, filterSystem, config.JsonRPC.Graphql.Cors, config.JsonRPC.Graphql.VHost); err != nil {
|
||||||
return nil, fmt.Errorf("failed to register the GraphQL service: %v", err)
|
return nil, fmt.Errorf("failed to register the GraphQL service: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -500,7 +506,7 @@ func setupLogger(logLevel string, loggingInfo LoggingConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetLatestBlockNumber() *big.Int {
|
func (s *Server) GetLatestBlockNumber() *big.Int {
|
||||||
return s.backend.BlockChain().CurrentBlock().Number()
|
return s.backend.BlockChain().CurrentBlock().Number
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetGrpcAddr() string {
|
func (s *Server) GetGrpcAddr() string {
|
||||||
|
|
|
||||||
|
|
@ -47,13 +47,13 @@ func TestServer_DeveloperMode(t *testing.T) {
|
||||||
defer CloseMockServer(server)
|
defer CloseMockServer(server)
|
||||||
|
|
||||||
// record the initial block number
|
// record the initial block number
|
||||||
blockNumber := server.backend.BlockChain().CurrentBlock().Header().Number.Int64()
|
blockNumber := server.backend.BlockChain().CurrentBlock().Number.Int64()
|
||||||
|
|
||||||
var i int64 = 0
|
var i int64 = 0
|
||||||
for i = 0; i < 3; i++ {
|
for i = 0; i < 3; i++ {
|
||||||
// We expect the node to mine blocks every `config.Developer.Period` time period
|
// We expect the node to mine blocks every `config.Developer.Period` time period
|
||||||
time.Sleep(time.Duration(config.Developer.Period) * time.Second)
|
time.Sleep(time.Duration(config.Developer.Period) * time.Second)
|
||||||
currBlock := server.backend.BlockChain().CurrentBlock().Header().Number.Int64()
|
currBlock := server.backend.BlockChain().CurrentBlock().Number.Int64()
|
||||||
expected := blockNumber + i + 1
|
expected := blockNumber + i + 1
|
||||||
if res := assert.Equal(t, currBlock, expected); res == false {
|
if res := assert.Equal(t, currBlock, expected); res == false {
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ func (s *Server) Status(ctx context.Context, in *proto.StatusRequest) (*proto.St
|
||||||
|
|
||||||
resp := &proto.StatusResponse{
|
resp := &proto.StatusResponse{
|
||||||
CurrentHeader: headerToProtoHeader(apiBackend.CurrentHeader()),
|
CurrentHeader: headerToProtoHeader(apiBackend.CurrentHeader()),
|
||||||
CurrentBlock: headerToProtoHeader(apiBackend.CurrentBlock().Header()),
|
CurrentBlock: headerToProtoHeader(apiBackend.CurrentBlock()),
|
||||||
NumPeers: int64(len(s.node.Server().PeersInfo())),
|
NumPeers: int64(len(s.node.Server().PeersInfo())),
|
||||||
SyncMode: s.config.SyncMode,
|
SyncMode: s.config.SyncMode,
|
||||||
Syncing: &proto.StatusResponse_Syncing{
|
Syncing: &proto.StatusResponse_Syncing{
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,13 @@ func (c *PruneStateCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pruner, err := pruner.NewPruner(chaindb, node.ResolvePath(""), node.ResolvePath(c.cacheTrieJournal), c.bloomfilterSize)
|
prunerconfig := pruner.Config{
|
||||||
|
Datadir: node.ResolvePath(""),
|
||||||
|
Cachedir: node.ResolvePath(c.cacheTrieJournal),
|
||||||
|
BloomSize: c.bloomfilterSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pruner, err := pruner.NewPruner(chaindb, prunerconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to open snapshot tree", "err", err)
|
log.Error("Failed to open snapshot tree", "err", err)
|
||||||
return 1
|
return 1
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ func Setup(ctx *cli.Context) error {
|
||||||
// This context value ("metrics.addr") represents the utils.MetricsHTTPFlag.Name.
|
// This context value ("metrics.addr") represents the utils.MetricsHTTPFlag.Name.
|
||||||
// It cannot be imported because it will cause a cyclical dependency.
|
// It cannot be imported because it will cause a cyclical dependency.
|
||||||
StartPProf(address, !ctx.IsSet("metrics.addr"))
|
StartPProf(address, !ctx.IsSet("metrics.addr"))
|
||||||
} else if ctx.GlobalIsSet("bor-mumbai") || ctx.GlobalIsSet("bor-mainnet") {
|
} else if ctx.IsSet("bor-mumbai") || ctx.IsSet("bor-mainnet") {
|
||||||
address := fmt.Sprintf("%s:%d", "0.0.0.0", 7071)
|
address := fmt.Sprintf("%s:%d", "0.0.0.0", 7071)
|
||||||
StartPProf(address, !ctx.IsSet("metrics.addr"))
|
StartPProf(address, !ctx.IsSet("metrics.addr"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -343,3 +343,43 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backendMock) Engine() consensus.Engine { return nil }
|
func (b *backendMock) Engine() consensus.Engine { return nil }
|
||||||
|
|
||||||
|
func (b *backendMock) RPCRpcReturnDataLimit() uint64 {
|
||||||
|
return b.RPCRpcReturnDataLimit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
|
||||||
|
return b.SubscribeStateSyncEvent(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
|
||||||
|
return b.GetRootHash(ctx, starBlockNr, endBlockNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
|
||||||
|
return b.GetBorBlockReceipt(ctx, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
|
||||||
|
return b.GetBorBlockLogs(ctx, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) GetBorBlockTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||||
|
return b.GetBorBlockTransaction(ctx, txHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||||
|
return b.GetBorBlockTransactionWithBlockHash(ctx, txHash, blockHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription {
|
||||||
|
return b.SubscribeChain2HeadEvent(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) GetCheckpointWhitelist() map[uint64]common.Hash {
|
||||||
|
return b.GetCheckpointWhitelist()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendMock) PurgeCheckpointWhitelist() {
|
||||||
|
b.PurgeCheckpointWhitelist()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
||||||
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
}
|
}
|
||||||
bc.forker = core.NewForkChoice(bc, nil)
|
bc.forker = core.NewForkChoice(bc, nil, checker)
|
||||||
var err error
|
var err error
|
||||||
bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt)
|
bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -401,24 +401,6 @@ func (lc *LightChain) SetCanonical(header *types.Header) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lc *LightChain) InsertHeader(header *types.Header) error {
|
|
||||||
// Verify the header first before obtaining the lock
|
|
||||||
headers := []*types.Header{header}
|
|
||||||
if _, err := lc.hc.ValidateHeaderChain(headers, 100); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Make sure only one thread manipulates the chain at once
|
|
||||||
lc.chainmu.Lock()
|
|
||||||
defer lc.chainmu.Unlock()
|
|
||||||
|
|
||||||
lc.wg.Add(1)
|
|
||||||
defer lc.wg.Done()
|
|
||||||
|
|
||||||
_, err := lc.hc.WriteHeaders(headers)
|
|
||||||
log.Info("Inserted header", "number", header.Number, "hash", header.Hash())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lc *LightChain) SetChainHead(header *types.Header) error {
|
func (lc *LightChain) SetChainHead(header *types.Header) error {
|
||||||
lc.chainmu.Lock()
|
lc.chainmu.Lock()
|
||||||
defer lc.chainmu.Unlock()
|
defer lc.chainmu.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -276,7 +276,7 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||||
_, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
|
_, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
|
||||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func TestNodeIterator(t *testing.T) {
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
blockchain, _ := core.NewBlockChain(fulldb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := core.NewBlockChain(fulldb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||||
_, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
|
_, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, testChainGen)
|
||||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ func TestTxPool(t *testing.T) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
blockchain, _ := core.NewBlockChain(sdb, nil, gspec, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||||
_, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), poolTestBlocks, txPoolTestChainGen)
|
_, gchain, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), poolTestBlocks, txPoolTestChainGen)
|
||||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package miner
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
|
@ -50,15 +49,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/tests/bor/mocks"
|
"github.com/ethereum/go-ethereum/tests/bor/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// testCode is the testing contract binary code which will initialises some
|
|
||||||
// variables in constructor
|
|
||||||
testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032"
|
|
||||||
|
|
||||||
// testGas is the gas required for contract deployment.
|
|
||||||
testGas = 144109
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
testTxPoolConfig = txpool.DefaultConfig
|
testTxPoolConfig = txpool.DefaultConfig
|
||||||
testTxPoolConfig.Journal = ""
|
testTxPoolConfig.Journal = ""
|
||||||
|
|
@ -92,15 +82,6 @@ func init() {
|
||||||
newTxs = append(newTxs, tx2)
|
newTxs = append(newTxs, tx2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing.
|
|
||||||
type testWorkerBackend struct {
|
|
||||||
db ethdb.Database
|
|
||||||
txPool *txpool.TxPool
|
|
||||||
chain *core.BlockChain
|
|
||||||
genesis *core.Genesis
|
|
||||||
uncleBlock *types.Block
|
|
||||||
}
|
|
||||||
|
|
||||||
// newTestWorker creates a new test worker with the given parameters.
|
// newTestWorker creates a new test worker with the given parameters.
|
||||||
func newTestWorker(t TensingObject, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, noempty bool, delay uint, opcodeDelay uint) (*worker, *testWorkerBackend, func()) {
|
func newTestWorker(t TensingObject, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, noempty bool, delay uint, opcodeDelay uint) (*worker, *testWorkerBackend, func()) {
|
||||||
backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
|
backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
|
||||||
|
|
@ -124,63 +105,6 @@ func newTestWorker(t TensingObject, chainConfig *params.ChainConfig, engine cons
|
||||||
return w, backend, w.close
|
return w, backend, w.close
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestWorkerBackend(t TensingObject, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend {
|
|
||||||
var gspec = &core.Genesis{
|
|
||||||
Config: chainConfig,
|
|
||||||
Alloc: core.GenesisAlloc{TestBankAddress: {Balance: testBankFunds}},
|
|
||||||
}
|
|
||||||
switch e := engine.(type) {
|
|
||||||
case *bor.Bor:
|
|
||||||
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
|
|
||||||
copy(gspec.ExtraData[32:32+common.AddressLength], TestBankAddress.Bytes())
|
|
||||||
e.Authorize(TestBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
|
|
||||||
return crypto.Sign(crypto.Keccak256(data), testBankKey)
|
|
||||||
})
|
|
||||||
case *clique.Clique:
|
|
||||||
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
|
|
||||||
copy(gspec.ExtraData[32:32+common.AddressLength], TestBankAddress.Bytes())
|
|
||||||
e.Authorize(TestBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
|
|
||||||
return crypto.Sign(crypto.Keccak256(data), testBankKey)
|
|
||||||
})
|
|
||||||
case *ethash.Ethash:
|
|
||||||
default:
|
|
||||||
t.Fatalf("unexpected consensus engine type: %T", engine)
|
|
||||||
}
|
|
||||||
chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec, nil, engine, vm.Config{}, nil, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("core.NewBlockChain failed: %v", err)
|
|
||||||
}
|
|
||||||
txpool := txpool.NewTxPool(testTxPoolConfig, chainConfig, chain)
|
|
||||||
|
|
||||||
// Generate a small n-block chain and an uncle block for it
|
|
||||||
var uncle *types.Block
|
|
||||||
if n > 0 {
|
|
||||||
genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, func(i int, gen *core.BlockGen) {
|
|
||||||
gen.SetCoinbase(TestBankAddress)
|
|
||||||
})
|
|
||||||
if _, err := chain.InsertChain(blocks); err != nil {
|
|
||||||
t.Fatalf("failed to insert origin chain: %v", err)
|
|
||||||
}
|
|
||||||
parent := chain.GetBlockByHash(chain.CurrentBlock().ParentHash)
|
|
||||||
blocks, _ = core.GenerateChain(chainConfig, parent, engine, genDb, 1, func(i int, gen *core.BlockGen) {
|
|
||||||
gen.SetCoinbase(testUserAddress)
|
|
||||||
})
|
|
||||||
uncle = blocks[0]
|
|
||||||
} else {
|
|
||||||
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, 1, func(i int, gen *core.BlockGen) {
|
|
||||||
gen.SetCoinbase(testUserAddress)
|
|
||||||
})
|
|
||||||
uncle = blocks[0]
|
|
||||||
}
|
|
||||||
return &testWorkerBackend{
|
|
||||||
db: db,
|
|
||||||
chain: chain,
|
|
||||||
txPool: txpool,
|
|
||||||
genesis: gspec,
|
|
||||||
uncleBlock: uncle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
||||||
func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
|
func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
|
||||||
func (b *testWorkerBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
|
func (b *testWorkerBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
|
||||||
|
|
@ -196,7 +120,7 @@ func (b *testWorkerBackend) newRandomUncle() (*types.Block, error) {
|
||||||
parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash)
|
parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash)
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.db, 1, func(i int, gen *core.BlockGen) {
|
blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.DB, 1, func(i int, gen *core.BlockGen) {
|
||||||
var addr = make([]byte, common.AddressLength)
|
var addr = make([]byte, common.AddressLength)
|
||||||
_, err = rand.Read(addr)
|
_, err = rand.Read(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -302,7 +226,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool, isBor bool) {
|
||||||
defer w.close()
|
defer w.close()
|
||||||
|
|
||||||
// This test chain imports the mined blocks.
|
// This test chain imports the mined blocks.
|
||||||
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, b.genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, b.Genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
// Ignore empty commit here for less noise.
|
// Ignore empty commit here for less noise.
|
||||||
|
|
@ -962,7 +886,7 @@ func BenchmarkBorMining(b *testing.B) {
|
||||||
w, back, _ := newTestWorker(b, chainConfig, engine, db, 0, false, 0, 0)
|
w, back, _ := newTestWorker(b, chainConfig, engine, db, 0, false, 0, 0)
|
||||||
defer w.close()
|
defer w.close()
|
||||||
|
|
||||||
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, back.genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, back.Genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
// Ignore empty commit here for less noise.
|
// Ignore empty commit here for less noise.
|
||||||
|
|
@ -978,7 +902,7 @@ func BenchmarkBorMining(b *testing.B) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
txInBlock := int(back.genesis.GasLimit/totalGas) + 1
|
txInBlock := int(back.Genesis.GasLimit/totalGas) + 1
|
||||||
|
|
||||||
// a bit risky
|
// a bit risky
|
||||||
for i := 0; i < 2*totalBlocks*txInBlock; i++ {
|
for i := 0; i < 2*totalBlocks*txInBlock; i++ {
|
||||||
|
|
@ -1004,7 +928,7 @@ func BenchmarkBorMining(b *testing.B) {
|
||||||
// Start mining!
|
// Start mining!
|
||||||
w.start()
|
w.start()
|
||||||
|
|
||||||
blockPeriod, ok := back.genesis.Config.Bor.Period["0"]
|
blockPeriod, ok := back.Genesis.Config.Bor.Period["0"]
|
||||||
if !ok {
|
if !ok {
|
||||||
blockPeriod = 1
|
blockPeriod = 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,12 @@ package node
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -193,6 +195,8 @@ type Config struct {
|
||||||
// Logger is a custom logger to use with the p2p.Server.
|
// Logger is a custom logger to use with the p2p.Server.
|
||||||
Logger log.Logger `toml:",omitempty"`
|
Logger log.Logger `toml:",omitempty"`
|
||||||
|
|
||||||
|
staticNodesWarning bool
|
||||||
|
trustedNodesWarning bool
|
||||||
oldGethResourceWarning bool
|
oldGethResourceWarning bool
|
||||||
|
|
||||||
// AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC.
|
// AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC.
|
||||||
|
|
@ -360,6 +364,50 @@ func (c *Config) ResolvePath(path string) string {
|
||||||
return filepath.Join(c.instanceDir(), path)
|
return filepath.Join(c.instanceDir(), path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StaticNodes returns a list of node enode URLs configured as static nodes.
|
||||||
|
func (c *Config) StaticNodes() []*enode.Node {
|
||||||
|
return c.parsePersistentNodes(&c.staticNodesWarning, c.ResolvePath(datadirStaticNodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrustedNodes returns a list of node enode URLs configured as trusted nodes.
|
||||||
|
func (c *Config) TrustedNodes() []*enode.Node {
|
||||||
|
return c.parsePersistentNodes(&c.trustedNodesWarning, c.ResolvePath(datadirTrustedNodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
|
||||||
|
// file from within the data directory.
|
||||||
|
func (c *Config) parsePersistentNodes(w *bool, path string) []*enode.Node {
|
||||||
|
// Short circuit if no node config is present
|
||||||
|
if c.DataDir == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.warnOnce(w, "Found deprecated node list file %s, please use the TOML config file instead.", path)
|
||||||
|
|
||||||
|
// Load the nodes from the config file.
|
||||||
|
var nodelist []string
|
||||||
|
if err := common.LoadJSON(path, &nodelist); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Can't load node list file: %v", err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Interpret the list as a discovery node array
|
||||||
|
var nodes []*enode.Node
|
||||||
|
for _, url := range nodelist {
|
||||||
|
if url == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
node, err := enode.Parse(enode.ValidSchemes, url)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) instanceDir() string {
|
func (c *Config) instanceDir() string {
|
||||||
if c.DataDir == "" {
|
if c.DataDir == "" {
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -480,3 +528,20 @@ func getKeyStoreDir(conf *Config) (string, bool, error) {
|
||||||
|
|
||||||
return keydir, isEphemeral, nil
|
return keydir, isEphemeral, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var warnLock sync.Mutex
|
||||||
|
|
||||||
|
func (c *Config) warnOnce(w *bool, format string, args ...interface{}) {
|
||||||
|
warnLock.Lock()
|
||||||
|
defer warnLock.Unlock()
|
||||||
|
|
||||||
|
if *w {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := c.Logger
|
||||||
|
if l == nil {
|
||||||
|
l = log.Root()
|
||||||
|
}
|
||||||
|
l.Warn(fmt.Sprintf(format, args...))
|
||||||
|
*w = true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ func (h *httpServer) enableRPC(apis []rpc.API, config httpConfig) error {
|
||||||
// Create RPC server and handler.
|
// Create RPC server and handler.
|
||||||
srv := rpc.NewServer(config.executionPoolSize, config.executionPoolRequestTimeout)
|
srv := rpc.NewServer(config.executionPoolSize, config.executionPoolRequestTimeout)
|
||||||
srv.SetRPCBatchLimit(h.RPCBatchLimit)
|
srv.SetRPCBatchLimit(h.RPCBatchLimit)
|
||||||
if err := RegisterApis(apis, config.Modules, srv, false); err != nil {
|
if err := RegisterApis(apis, config.Modules, srv); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
h.httpConfig = config
|
h.httpConfig = config
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ func createAndStartServer(t *testing.T, conf *httpConfig, ws bool, wsConf *wsCon
|
||||||
if timeouts == nil {
|
if timeouts == nil {
|
||||||
timeouts = &rpc.DefaultHTTPTimeouts
|
timeouts = &rpc.DefaultHTTPTimeouts
|
||||||
}
|
}
|
||||||
srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), *timeouts)
|
srv := newHTTPServer(testlog.Logger(t, log.LvlDebug), *timeouts, 100)
|
||||||
assert.NoError(t, srv.enableRPC(apis(), *conf))
|
assert.NoError(t, srv.enableRPC(apis(), *conf))
|
||||||
if ws {
|
if ws {
|
||||||
assert.NoError(t, srv.enableWS(nil, *wsConf))
|
assert.NoError(t, srv.enableWS(nil, *wsConf))
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ func main() {
|
||||||
if err := os.WriteFile(canonicalPath, code, 0600); err != nil {
|
if err := os.WriteFile(canonicalPath, code, 0600); err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatal(args ...interface{}) {
|
func fatal(args ...interface{}) {
|
||||||
|
|
|
||||||
|
|
@ -114,10 +114,10 @@ type HTTPTimeouts struct {
|
||||||
// DefaultHTTPTimeouts represents the default timeout values used if further
|
// DefaultHTTPTimeouts represents the default timeout values used if further
|
||||||
// configuration is not provided.
|
// configuration is not provided.
|
||||||
var DefaultHTTPTimeouts = HTTPTimeouts{
|
var DefaultHTTPTimeouts = HTTPTimeouts{
|
||||||
ReadTimeout: 10 * time.Second,
|
ReadTimeout: 10 * time.Second,
|
||||||
ReadHeaderTimeout: 30 * time.Second,
|
ReadHeaderTimeout: 30 * time.Second,
|
||||||
WriteTimeout: 30 * time.Second,
|
WriteTimeout: 30 * time.Second,
|
||||||
IdleTimeout: 120 * time.Second,
|
IdleTimeout: 120 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialHTTP creates a new RPC client that connects to an RPC server over HTTP.
|
// DialHTTP creates a new RPC client that connects to an RPC server over HTTP.
|
||||||
|
|
@ -165,7 +165,6 @@ func newClientTransportHTTP(endpoint string, cfg *clientConfig) reconnectFunc {
|
||||||
|
|
||||||
return func(ctx context.Context) (ServerCodec, error) {
|
return func(ctx context.Context) (ServerCodec, error) {
|
||||||
return hc, nil
|
return hc, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,11 +214,6 @@ func (c *jsonCodec) peerInfo() PeerInfo {
|
||||||
return PeerInfo{Transport: "ipc", RemoteAddr: c.remote}
|
return PeerInfo{Transport: "ipc", RemoteAddr: c.remote}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *jsonCodec) peerInfo() PeerInfo {
|
|
||||||
// This returns "ipc" because all other built-in transports have a separate codec type.
|
|
||||||
return PeerInfo{Transport: "ipc", RemoteAddr: c.remote}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *jsonCodec) remoteAddr() string {
|
func (c *jsonCodec) remoteAddr() string {
|
||||||
return c.remote
|
return c.remote
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"io/ioutil" // nolint: staticcheck
|
"io/ioutil" // nolint: staticcheck
|
||||||
_log "log"
|
_log "log"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -16,7 +17,6 @@ import (
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -778,7 +778,6 @@ func InitGenesisSprintLength(t *testing.T, faucets []*ecdsa.PrivateKey, fileLoca
|
||||||
}
|
}
|
||||||
|
|
||||||
genesis.Config.ChainID = big.NewInt(15001)
|
genesis.Config.ChainID = big.NewInt(15001)
|
||||||
genesis.Config.EIP150Hash = common.Hash{}
|
|
||||||
genesis.Config.Bor.Sprint["0"] = sprintSize
|
genesis.Config.Bor.Sprint["0"] = sprintSize
|
||||||
|
|
||||||
return genesis
|
return genesis
|
||||||
|
|
@ -811,7 +810,7 @@ func InitMinerSprintLength(genesis *core.Genesis, privKey *ecdsa.PrivateKey, wit
|
||||||
SyncMode: downloader.FullSync,
|
SyncMode: downloader.FullSync,
|
||||||
DatabaseCache: 256,
|
DatabaseCache: 256,
|
||||||
DatabaseHandles: 256,
|
DatabaseHandles: 256,
|
||||||
TxPool: core.DefaultTxPoolConfig,
|
TxPool: txpool.DefaultConfig,
|
||||||
GPO: ethconfig.Defaults.GPO,
|
GPO: ethconfig.Defaults.GPO,
|
||||||
Ethash: ethconfig.Defaults.Ethash,
|
Ethash: ethconfig.Defaults.Ethash,
|
||||||
Miner: miner.Config{
|
Miner: miner.Config{
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,6 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
LondonBlock: big.NewInt(0),
|
LondonBlock: big.NewInt(0),
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
ArrowGlacierBlock: big.NewInt(0),
|
||||||
Bor: params.BorUnittestChainConfig.Bor,
|
Bor: params.BorUnittestChainConfig.Bor,
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
},
|
},
|
||||||
"ArrowGlacierToMergeAtDiffC0000": {
|
"ArrowGlacierToMergeAtDiffC0000": {
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
|
|
@ -250,7 +249,7 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
GrayGlacierBlock: big.NewInt(0),
|
GrayGlacierBlock: big.NewInt(0),
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
MergeNetsplitBlock: big.NewInt(0),
|
||||||
TerminalTotalDifficulty: big.NewInt(0xC0000),
|
TerminalTotalDifficulty: big.NewInt(0xC0000),
|
||||||
Bor: params.BorUnittestChainConfig.Bor,
|
Bor: params.BorUnittestChainConfig.Bor,
|
||||||
},
|
},
|
||||||
"GrayGlacier": {
|
"GrayGlacier": {
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
|
|
@ -285,7 +284,7 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
ArrowGlacierBlock: big.NewInt(0),
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
MergeNetsplitBlock: big.NewInt(0),
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
Bor: params.BorUnittestChainConfig.Bor,
|
Bor: params.BorUnittestChainConfig.Bor,
|
||||||
},
|
},
|
||||||
"Shanghai": {
|
"Shanghai": {
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
|
|
@ -304,7 +303,7 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
MergeNetsplitBlock: big.NewInt(0),
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
ShanghaiTime: u64(0),
|
ShanghaiTime: u64(0),
|
||||||
Bor: params.BorUnittestChainConfig.Bor,
|
Bor: params.BorUnittestChainConfig.Bor,
|
||||||
},
|
},
|
||||||
"MergeToShanghaiAtTime15k": {
|
"MergeToShanghaiAtTime15k": {
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
|
|
@ -323,7 +322,7 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
MergeNetsplitBlock: big.NewInt(0),
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
ShanghaiTime: u64(15_000),
|
ShanghaiTime: u64(15_000),
|
||||||
Bor: params.BorUnittestChainConfig.Bor,
|
Bor: params.BorUnittestChainConfig.Bor,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -557,15 +557,6 @@ func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) {
|
||||||
return mustDecodeNode(n, blob), nil
|
return mustDecodeNode(n, blob), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, error) {
|
|
||||||
hash := common.BytesToHash(n)
|
|
||||||
blob, _ := t.db.Node(hash)
|
|
||||||
if len(blob) != 0 {
|
|
||||||
return blob, nil
|
|
||||||
}
|
|
||||||
return nil, &MissingNodeError{NodeHash: hash, Path: prefix}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash returns the root hash of the trie. It does not write to the
|
// Hash returns the root hash of the trie. It does not write to the
|
||||||
// database and can be used even if the trie doesn't have one.
|
// database and can be used even if the trie doesn't have one.
|
||||||
func (t *Trie) Hash() common.Hash {
|
func (t *Trie) Hash() common.Hash {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue