mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Merge branch 'master' into gethclient_tracing_api
This commit is contained in:
commit
570059fc72
87 changed files with 5817 additions and 9059 deletions
|
|
@ -36,6 +36,10 @@ var (
|
||||||
// on a backend that doesn't implement PendingContractCaller.
|
// on a backend that doesn't implement PendingContractCaller.
|
||||||
ErrNoPendingState = errors.New("backend does not support pending state")
|
ErrNoPendingState = errors.New("backend does not support pending state")
|
||||||
|
|
||||||
|
// ErrNoBlockHashState is raised when attempting to perform a block hash action
|
||||||
|
// on a backend that doesn't implement BlockHashContractCaller.
|
||||||
|
ErrNoBlockHashState = errors.New("backend does not support block hash state")
|
||||||
|
|
||||||
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
|
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
|
||||||
// an empty contract behind.
|
// an empty contract behind.
|
||||||
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
|
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
|
||||||
|
|
@ -64,6 +68,17 @@ type PendingContractCaller interface {
|
||||||
PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
|
PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockHashContractCaller defines methods to perform contract calls on a specific block hash.
|
||||||
|
// Call will try to discover this interface when access to a block by hash is requested.
|
||||||
|
// If the backend does not support the block hash state, Call returns ErrNoBlockHashState.
|
||||||
|
type BlockHashContractCaller interface {
|
||||||
|
// CodeAtHash returns the code of the given account in the state at the specified block hash.
|
||||||
|
CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error)
|
||||||
|
|
||||||
|
// CallContractAtHash executes an Ethereum contract all against the state at the specified block hash.
|
||||||
|
CallContractAtHash(ctx context.Context, call ethereum.CallMsg, blockHash common.Hash) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
// ContractTransactor defines the methods needed to allow operating with a contract
|
// ContractTransactor defines the methods needed to allow operating with a contract
|
||||||
// on a write only basis. Besides the transacting method, the remainder are helpers
|
// on a write only basis. Besides the transacting method, the remainder are helpers
|
||||||
// used when the user does not provide some needed values, but rather leaves it up
|
// used when the user does not provide some needed values, but rather leaves it up
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
||||||
|
errBlockHashUnsupported = errors.New("simulatedBackend cannot access blocks by hash other than the latest block")
|
||||||
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
||||||
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
||||||
)
|
)
|
||||||
|
|
@ -202,6 +203,24 @@ func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address,
|
||||||
return stateDB.GetCode(contract), nil
|
return stateDB.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CodeAtHash returns the code associated with a certain account in the blockchain.
|
||||||
|
func (b *SimulatedBackend) CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
header, err := b.headerByHash(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stateDB, err := b.blockchain.StateAt(header.Root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateDB.GetCode(contract), nil
|
||||||
|
}
|
||||||
|
|
||||||
// BalanceAt returns the wei balance of a certain account in the blockchain.
|
// BalanceAt returns the wei balance of a certain account in the blockchain.
|
||||||
func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
|
func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|
@ -320,7 +339,11 @@ func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (
|
||||||
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
return b.headerByHash(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// headerByHash retrieves a header from the database by hash without Lock.
|
||||||
|
func (b *SimulatedBackend) headerByHash(hash common.Hash) (*types.Header, error) {
|
||||||
if hash == b.pendingBlock.Hash() {
|
if hash == b.pendingBlock.Hash() {
|
||||||
return b.pendingBlock.Header(), nil
|
return b.pendingBlock.Header(), nil
|
||||||
}
|
}
|
||||||
|
|
@ -436,6 +459,22 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 {
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 {
|
||||||
return nil, errBlockNumberUnsupported
|
return nil, errBlockNumberUnsupported
|
||||||
}
|
}
|
||||||
|
return b.callContractAtHead(ctx, call)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallContractAtHash executes a contract call on a specific block hash.
|
||||||
|
func (b *SimulatedBackend) CallContractAtHash(ctx context.Context, call ethereum.CallMsg, blockHash common.Hash) ([]byte, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if blockHash != b.blockchain.CurrentBlock().Hash() {
|
||||||
|
return nil, errBlockHashUnsupported
|
||||||
|
}
|
||||||
|
return b.callContractAtHead(ctx, call)
|
||||||
|
}
|
||||||
|
|
||||||
|
// callContractAtHead executes a contract call against the latest block state.
|
||||||
|
func (b *SimulatedBackend) callContractAtHead(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||||
stateDB, err := b.blockchain.State()
|
stateDB, err := b.blockchain.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -586,7 +625,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if failed {
|
if failed {
|
||||||
if result != nil && result.Err != vm.ErrOutOfGas {
|
if result != nil && !errors.Is(result.Err, vm.ErrOutOfGas) {
|
||||||
if len(result.Revert()) > 0 {
|
if len(result.Revert()) > 0 {
|
||||||
return 0, newRevertError(result)
|
return 0, newRevertError(result)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -996,6 +996,43 @@ func TestCodeAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCodeAtHash(t *testing.T) {
|
||||||
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
sim := simTestBackend(testAddr)
|
||||||
|
defer sim.Close()
|
||||||
|
bgCtx := context.Background()
|
||||||
|
code, err := sim.CodeAtHash(bgCtx, testAddr, sim.Blockchain().CurrentHeader().Hash())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
|
}
|
||||||
|
if len(code) != 0 {
|
||||||
|
t.Errorf("got code for account that does not have contract code")
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(abiJSON))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
|
}
|
||||||
|
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||||
|
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockHash := sim.Commit()
|
||||||
|
code, err = sim.CodeAtHash(bgCtx, contractAddr, blockHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
|
}
|
||||||
|
if len(code) == 0 {
|
||||||
|
t.Errorf("did not get code for account that has contract code")
|
||||||
|
}
|
||||||
|
// ensure code received equals code deployed
|
||||||
|
if !bytes.Equal(code, common.FromHex(deployedCode)) {
|
||||||
|
t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
||||||
//
|
//
|
||||||
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
||||||
|
|
@ -1038,7 +1075,7 @@ func TestPendingAndCallContract(t *testing.T) {
|
||||||
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
|
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
blockHash := sim.Commit()
|
||||||
|
|
||||||
// make sure you can call the contract
|
// make sure you can call the contract
|
||||||
res, err = sim.CallContract(bgCtx, ethereum.CallMsg{
|
res, err = sim.CallContract(bgCtx, ethereum.CallMsg{
|
||||||
|
|
@ -1056,6 +1093,23 @@ func TestPendingAndCallContract(t *testing.T) {
|
||||||
if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
|
if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
|
||||||
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
|
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure you can call the contract by hash
|
||||||
|
res, err = sim.CallContractAtHash(bgCtx, ethereum.CallMsg{
|
||||||
|
From: testAddr,
|
||||||
|
To: &addr,
|
||||||
|
Data: input,
|
||||||
|
}, blockHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not call receive method on contract: %v", err)
|
||||||
|
}
|
||||||
|
if len(res) == 0 {
|
||||||
|
t.Errorf("result of contract call was empty: %v", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
|
||||||
|
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test is based on the following contract:
|
// This test is based on the following contract:
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ type CallOpts struct {
|
||||||
Pending bool // Whether to operate on the pending state or the last known one
|
Pending bool // Whether to operate on the pending state or the last known one
|
||||||
From common.Address // Optional the sender address, otherwise the first account is used
|
From common.Address // Optional the sender address, otherwise the first account is used
|
||||||
BlockNumber *big.Int // Optional the block number on which the call should be performed
|
BlockNumber *big.Int // Optional the block number on which the call should be performed
|
||||||
|
BlockHash common.Hash // Optional the block hash on which the call should be performed
|
||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,6 +190,23 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
||||||
return ErrNoCode
|
return ErrNoCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if opts.BlockHash != (common.Hash{}) {
|
||||||
|
bh, ok := c.caller.(BlockHashContractCaller)
|
||||||
|
if !ok {
|
||||||
|
return ErrNoBlockHashState
|
||||||
|
}
|
||||||
|
output, err = bh.CallContractAtHash(ctx, msg, opts.BlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(output) == 0 {
|
||||||
|
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||||
|
if code, err = bh.CodeAtHash(ctx, c.address, opts.BlockHash); err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(code) == 0 {
|
||||||
|
return ErrNoCode
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
|
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,26 @@ func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ether
|
||||||
return mc.pendingCallContractBytes, mc.pendingCallContractErr
|
return mc.pendingCallContractBytes, mc.pendingCallContractErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockBlockHashCaller struct {
|
||||||
|
*mockCaller
|
||||||
|
codeAtHashBytes []byte
|
||||||
|
codeAtHashErr error
|
||||||
|
codeAtHashCalled bool
|
||||||
|
callContractAtHashCalled bool
|
||||||
|
callContractAtHashBytes []byte
|
||||||
|
callContractAtHashErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockBlockHashCaller) CodeAtHash(ctx context.Context, contract common.Address, hash common.Hash) ([]byte, error) {
|
||||||
|
mc.codeAtHashCalled = true
|
||||||
|
return mc.codeAtHashBytes, mc.codeAtHashErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockBlockHashCaller) CallContractAtHash(ctx context.Context, call ethereum.CallMsg, hash common.Hash) ([]byte, error) {
|
||||||
|
mc.callContractAtHashCalled = true
|
||||||
|
return mc.callContractAtHashBytes, mc.callContractAtHashErr
|
||||||
|
}
|
||||||
|
|
||||||
func TestPassingBlockNumber(t *testing.T) {
|
func TestPassingBlockNumber(t *testing.T) {
|
||||||
mc := &mockPendingCaller{
|
mc := &mockPendingCaller{
|
||||||
mockCaller: &mockCaller{
|
mockCaller: &mockCaller{
|
||||||
|
|
@ -400,6 +420,15 @@ func TestCall(t *testing.T) {
|
||||||
Pending: true,
|
Pending: true,
|
||||||
},
|
},
|
||||||
method: method,
|
method: method,
|
||||||
|
}, {
|
||||||
|
name: "ok hash",
|
||||||
|
mc: &mockBlockHashCaller{
|
||||||
|
codeAtHashBytes: []byte{0},
|
||||||
|
},
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
BlockHash: common.Hash{0xaa},
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
}, {
|
}, {
|
||||||
name: "pack error, no method",
|
name: "pack error, no method",
|
||||||
mc: new(mockCaller),
|
mc: new(mockCaller),
|
||||||
|
|
@ -413,6 +442,14 @@ func TestCall(t *testing.T) {
|
||||||
},
|
},
|
||||||
method: method,
|
method: method,
|
||||||
wantErrExact: bind.ErrNoPendingState,
|
wantErrExact: bind.ErrNoPendingState,
|
||||||
|
}, {
|
||||||
|
name: "interface error, blockHash but not a BlockHashContractCaller",
|
||||||
|
mc: new(mockCaller),
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
BlockHash: common.Hash{0xaa},
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErrExact: bind.ErrNoBlockHashState,
|
||||||
}, {
|
}, {
|
||||||
name: "pending call canceled",
|
name: "pending call canceled",
|
||||||
mc: &mockPendingCaller{
|
mc: &mockPendingCaller{
|
||||||
|
|
@ -460,6 +497,34 @@ func TestCall(t *testing.T) {
|
||||||
mc: new(mockCaller),
|
mc: new(mockCaller),
|
||||||
method: method,
|
method: method,
|
||||||
wantErrExact: bind.ErrNoCode,
|
wantErrExact: bind.ErrNoCode,
|
||||||
|
}, {
|
||||||
|
name: "call contract at hash error",
|
||||||
|
mc: &mockBlockHashCaller{
|
||||||
|
callContractAtHashErr: context.DeadlineExceeded,
|
||||||
|
},
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
BlockHash: common.Hash{0xaa},
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErrExact: context.DeadlineExceeded,
|
||||||
|
}, {
|
||||||
|
name: "code at error",
|
||||||
|
mc: &mockBlockHashCaller{
|
||||||
|
codeAtHashErr: errors.New(""),
|
||||||
|
},
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
BlockHash: common.Hash{0xaa},
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
name: "no code at hash",
|
||||||
|
mc: new(mockBlockHashCaller),
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
BlockHash: common.Hash{0xaa},
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErrExact: bind.ErrNoCode,
|
||||||
}, {
|
}, {
|
||||||
name: "unpack error missing arg",
|
name: "unpack error missing arg",
|
||||||
mc: &mockCaller{
|
mc: &mockCaller{
|
||||||
|
|
|
||||||
|
|
@ -54,4 +54,4 @@ for:
|
||||||
- go run build/ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||||
- go run build/ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
- go run build/ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||||
test_script:
|
test_script:
|
||||||
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
|
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -short
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# This file contains sha256 checksums of optional build dependencies.
|
# This file contains sha256 checksums of optional build dependencies.
|
||||||
|
|
||||||
# version:spec-tests 1.0.2
|
# version:spec-tests 1.0.6
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases
|
# https://github.com/ethereum/execution-spec-tests/releases
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v1.0.2/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v1.0.6/
|
||||||
24bac679f3a2d8240d8e08e7f6a70b70c2dabf673317d924cf1d1887b9fe1f81 fixtures.tar.gz
|
485af7b66cf41eb3a8c1bd46632913b8eb95995df867cf665617bbc9b4beedd1 fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.21.3
|
# version:golang 1.21.3
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,7 @@ func doTest(cmdline []string) {
|
||||||
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
||||||
verbose = flag.Bool("v", false, "Whether to log verbosely")
|
verbose = flag.Bool("v", false, "Whether to log verbosely")
|
||||||
race = flag.Bool("race", false, "Execute the race detector")
|
race = flag.Bool("race", false, "Execute the race detector")
|
||||||
|
short = flag.Bool("short", false, "Pass the 'short'-flag to go test")
|
||||||
cachedir = flag.String("cachedir", "./build/cache", "directory for caching downloads")
|
cachedir = flag.String("cachedir", "./build/cache", "directory for caching downloads")
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
|
@ -318,6 +319,9 @@ func doTest(cmdline []string) {
|
||||||
if *race {
|
if *race {
|
||||||
gotest.Args = append(gotest.Args, "-race")
|
gotest.Args = append(gotest.Args, "-race")
|
||||||
}
|
}
|
||||||
|
if *short {
|
||||||
|
gotest.Args = append(gotest.Args, "-short")
|
||||||
|
}
|
||||||
|
|
||||||
packages := []string{"./..."}
|
packages := []string{"./..."}
|
||||||
if len(flag.CommandLine.Args()) > 0 {
|
if len(flag.CommandLine.Args()) > 0 {
|
||||||
|
|
@ -334,7 +338,7 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
ext := ".tar.gz"
|
ext := ".tar.gz"
|
||||||
base := "fixtures" // TODO(MariusVanDerWijden) rename once the version becomes part of the filename
|
base := "fixtures_develop" // TODO(MariusVanDerWijden) rename once the version becomes part of the filename
|
||||||
url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/v%s/%s%s", executionSpecTestsVersion, base, ext)
|
url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/v%s/%s%s", executionSpecTestsVersion, base, ext)
|
||||||
archivePath := filepath.Join(cachedir, base+ext)
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -60,9 +61,16 @@ func blockTestCmd(ctx *cli.Context) error {
|
||||||
if err = json.Unmarshal(src, &tests); err != nil {
|
if err = json.Unmarshal(src, &tests); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
// run them in order
|
||||||
|
var keys []string
|
||||||
|
for key := range tests {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, name := range keys {
|
||||||
|
test := tests[name]
|
||||||
if err := test.Run(false, rawdb.HashScheme, tracer); err != nil {
|
if err := test.Run(false, rawdb.HashScheme, tracer); err != nil {
|
||||||
return fmt.Errorf("test %v: %w", i, err)
|
return fmt.Errorf("test %v: %w", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
2
cmd/evm/testdata/8/readme.md
vendored
2
cmd/evm/testdata/8/readme.md
vendored
|
|
@ -32,7 +32,7 @@ dir=./testdata/8 && ./evm t8n --state.fork=Berlin --input.alloc=$dir/alloc.json
|
||||||
{"pc":4,"op":84,"gas":"0x48456","gasCost":"0x64","memSize":0,"stack":["0x3"],"depth":1,"refund":0,"opName":"SLOAD"}
|
{"pc":4,"op":84,"gas":"0x48456","gasCost":"0x64","memSize":0,"stack":["0x3"],"depth":1,"refund":0,"opName":"SLOAD"}
|
||||||
```
|
```
|
||||||
|
|
||||||
Simlarly, we can provide the input transactions via `stdin` instead of as file:
|
Similarly, we can provide the input transactions via `stdin` instead of as file:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ dir=./testdata/8 \
|
$ dir=./testdata/8 \
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
if head.Number.Uint64() == 0 {
|
if head.Number.Uint64() == 0 {
|
||||||
// The genesis state is missing, which is only possible in the path-based
|
// The genesis state is missing, which is only possible in the path-based
|
||||||
// scheme. This situation occurs when the initial state sync is not finished
|
// scheme. This situation occurs when the initial state sync is not finished
|
||||||
// yet, or the chain head is rewound below the pivot point. In both scenario,
|
// yet, or the chain head is rewound below the pivot point. In both scenarios,
|
||||||
// there is no possible recovery approach except for rerunning a snap sync.
|
// there is no possible recovery approach except for rerunning a snap sync.
|
||||||
// Do nothing here until the state syncer picks it up.
|
// Do nothing here until the state syncer picks it up.
|
||||||
log.Info("Genesis state is missing, wait state sync")
|
log.Info("Genesis state is missing, wait state sync")
|
||||||
|
|
@ -666,9 +666,8 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||||
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
|
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
|
||||||
newHeadBlock = bc.genesisBlock
|
newHeadBlock = bc.genesisBlock
|
||||||
} else {
|
} else {
|
||||||
// Block exists, keep rewinding until we find one with state,
|
// Block exists. Keep rewinding until either we find one with state
|
||||||
// keeping rewinding until we exceed the optional threshold
|
// or until we exceed the optional threshold root hash
|
||||||
// root hash
|
|
||||||
beyondRoot := (root == common.Hash{}) // Flag whether we're beyond the requested root (no root, always true)
|
beyondRoot := (root == common.Hash{}) // Flag whether we're beyond the requested root (no root, always true)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
@ -989,6 +988,7 @@ func (bc *BlockChain) Stop() {
|
||||||
if snapBase, err = bc.snaps.Journal(bc.CurrentBlock().Root); err != nil {
|
if snapBase, err = bc.snaps.Journal(bc.CurrentBlock().Root); err != nil {
|
||||||
log.Error("Failed to journal state snapshot", "err", err)
|
log.Error("Failed to journal state snapshot", "err", err)
|
||||||
}
|
}
|
||||||
|
bc.snaps.Release()
|
||||||
}
|
}
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
if bc.triedb.Scheme() == rawdb.PathScheme {
|
||||||
// Ensure that the in-memory trie nodes are journaled to disk properly.
|
// Ensure that the in-memory trie nodes are journaled to disk properly.
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// newCanonical creates a chain database, and injects a deterministic canonical
|
// newCanonical creates a chain database, and injects a deterministic canonical
|
||||||
// chain. Depending on the full flag, if creates either a full block chain or a
|
// chain. Depending on the full flag, it creates either a full block chain or a
|
||||||
// header only chain. The database and genesis specification for block generation
|
// header only chain. The database and genesis specification for block generation
|
||||||
// are also returned in case more test blocks are needed later.
|
// are also returned in case more test blocks are needed later.
|
||||||
func newCanonical(engine consensus.Engine, n int, full bool, scheme string) (ethdb.Database, *Genesis, *BlockChain, error) {
|
func newCanonical(engine consensus.Engine, n int, full bool, scheme string) (ethdb.Database, *Genesis, *BlockChain, error) {
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,17 @@ func (b *BlockGen) SetPoS() {
|
||||||
b.header.Difficulty = new(big.Int)
|
b.header.Difficulty = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetParentBeaconRoot sets the parent beacon root field of the generated
|
||||||
|
// block.
|
||||||
|
func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
|
||||||
|
b.header.ParentBeaconRoot = &root
|
||||||
|
var (
|
||||||
|
blockContext = NewEVMBlockContext(b.header, nil, &b.header.Coinbase)
|
||||||
|
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.config, vm.Config{})
|
||||||
|
)
|
||||||
|
ProcessBeaconBlockRoot(root, vmenv, b.statedb)
|
||||||
|
}
|
||||||
|
|
||||||
// addTx adds a transaction to the generated block. If no coinbase has
|
// addTx adds a transaction to the generated block. If no coinbase has
|
||||||
// been set, the block's coinbase is set to the zero address.
|
// been set, the block's coinbase is set to the zero address.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateWithdrawalChain(t *testing.T) {
|
func TestGeneratePOSChain(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
keyHex = "9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c"
|
keyHex = "9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c"
|
||||||
key, _ = crypto.HexToECDSA(keyHex)
|
key, _ = crypto.HexToECDSA(keyHex)
|
||||||
|
|
@ -41,9 +41,13 @@ func TestGenerateWithdrawalChain(t *testing.T) {
|
||||||
bb = common.Address{0xbb}
|
bb = common.Address{0xbb}
|
||||||
funds = big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(params.Ether))
|
funds = big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(params.Ether))
|
||||||
config = *params.AllEthashProtocolChanges
|
config = *params.AllEthashProtocolChanges
|
||||||
|
asm4788 = common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: &config,
|
Config: &config,
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
Alloc: GenesisAlloc{
|
||||||
|
address: {Balance: funds},
|
||||||
|
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: asm4788},
|
||||||
|
},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Difficulty: common.Big1,
|
Difficulty: common.Big1,
|
||||||
GasLimit: 5_000_000,
|
GasLimit: 5_000_000,
|
||||||
|
|
@ -56,6 +60,7 @@ func TestGenerateWithdrawalChain(t *testing.T) {
|
||||||
config.TerminalTotalDifficultyPassed = true
|
config.TerminalTotalDifficultyPassed = true
|
||||||
config.TerminalTotalDifficulty = common.Big0
|
config.TerminalTotalDifficulty = common.Big0
|
||||||
config.ShanghaiTime = u64(0)
|
config.ShanghaiTime = u64(0)
|
||||||
|
config.CancunTime = u64(0)
|
||||||
|
|
||||||
// init 0xaa with some storage elements
|
// init 0xaa with some storage elements
|
||||||
storage := make(map[common.Hash]common.Hash)
|
storage := make(map[common.Hash]common.Hash)
|
||||||
|
|
@ -78,6 +83,7 @@ func TestGenerateWithdrawalChain(t *testing.T) {
|
||||||
genesis := gspec.MustCommit(gendb, trie.NewDatabase(gendb, trie.HashDefaults))
|
genesis := gspec.MustCommit(gendb, trie.NewDatabase(gendb, trie.HashDefaults))
|
||||||
|
|
||||||
chain, _ := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
|
||||||
|
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
|
||||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(address), address, big.NewInt(1000), params.TxGas, new(big.Int).Add(gen.BaseFee(), common.Big1), nil), signer, key)
|
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(address), address, big.NewInt(1000), params.TxGas, new(big.Int).Add(gen.BaseFee(), common.Big1), nil), signer, key)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
|
|
@ -125,6 +131,8 @@ func TestGenerateWithdrawalChain(t *testing.T) {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
t.Fatalf("block %d not found", i)
|
t.Fatalf("block %d not found", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify withdrawals.
|
||||||
if len(block.Withdrawals()) == 0 {
|
if len(block.Withdrawals()) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -134,6 +142,18 @@ func TestGenerateWithdrawalChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
withdrawalIndex += 1
|
withdrawalIndex += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify parent beacon root.
|
||||||
|
want := common.Hash{byte(i)}
|
||||||
|
if got := block.BeaconRoot(); *got != want {
|
||||||
|
t.Fatalf("block %d, wrong parent beacon root: got %s, want %s", i, got, want)
|
||||||
|
}
|
||||||
|
state, _ := blockchain.State()
|
||||||
|
idx := block.Time()%8191 + 8191
|
||||||
|
got := state.GetState(params.BeaconRootsStorageAddress, common.BigToHash(new(big.Int).SetUint64(idx)))
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("block %d, wrong parent beacon root in state: got %s, want %s", i, got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const tmpSuffix = ".tmp"
|
const tmpSuffix = ".tmp"
|
||||||
|
|
@ -224,6 +225,7 @@ func cleanup(path string) error {
|
||||||
}
|
}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if name == filepath.Base(path)+tmpSuffix {
|
if name == filepath.Base(path)+tmpSuffix {
|
||||||
|
log.Info("Removed leftover freezer directory", "name", name)
|
||||||
return os.RemoveAll(filepath.Join(parent, name))
|
return os.RemoveAll(filepath.Join(parent, name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,12 @@ func (t *freezerTable) repair() error {
|
||||||
t.index.ReadAt(buffer, offsetsSize-indexEntrySize)
|
t.index.ReadAt(buffer, offsetsSize-indexEntrySize)
|
||||||
lastIndex.unmarshalBinary(buffer)
|
lastIndex.unmarshalBinary(buffer)
|
||||||
}
|
}
|
||||||
|
// Print an error log if the index is corrupted due to an incorrect
|
||||||
|
// last index item. While it is theoretically possible to have a zero offset
|
||||||
|
// by storing all zero-size items, it is highly unlikely to occur in practice.
|
||||||
|
if lastIndex.offset == 0 && offsetsSize%indexEntrySize > 1 {
|
||||||
|
log.Error("Corrupted index file detected", "lastOffset", lastIndex.offset, "items", offsetsSize%indexEntrySize-1)
|
||||||
|
}
|
||||||
if t.readonly {
|
if t.readonly {
|
||||||
t.head, err = t.openFile(lastIndex.filenum, openFreezerFileForReadOnly)
|
t.head, err = t.openFile(lastIndex.filenum, openFreezerFileForReadOnly)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -349,7 +355,7 @@ func (t *freezerTable) repair() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if verbose {
|
if verbose {
|
||||||
t.logger.Info("Chain freezer table opened", "items", t.items.Load(), "size", t.headBytes)
|
t.logger.Info("Chain freezer table opened", "items", t.items.Load(), "deleted", t.itemOffset.Load(), "hidden", t.itemHidden.Load(), "tailId", t.tailId, "headId", t.headId, "size", t.headBytes)
|
||||||
} else {
|
} else {
|
||||||
t.logger.Debug("Chain freezer table opened", "items", t.items.Load(), "size", common.StorageSize(t.headBytes))
|
t.logger.Debug("Chain freezer table opened", "items", t.items.Load(), "size", common.StorageSize(t.headBytes))
|
||||||
}
|
}
|
||||||
|
|
@ -522,6 +528,10 @@ func (t *freezerTable) truncateTail(items uint64) error {
|
||||||
if err := t.meta.Sync(); err != nil {
|
if err := t.meta.Sync(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Close the index file before shorten it.
|
||||||
|
if err := t.index.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Truncate the deleted index entries from the index file.
|
// Truncate the deleted index entries from the index file.
|
||||||
err = copyFrom(t.index.Name(), t.index.Name(), indexEntrySize*(newDeleted-deleted+1), func(f *os.File) error {
|
err = copyFrom(t.index.Name(), t.index.Name(), indexEntrySize*(newDeleted-deleted+1), func(f *os.File) error {
|
||||||
tailIndex := indexEntry{
|
tailIndex := indexEntry{
|
||||||
|
|
@ -535,13 +545,14 @@ func (t *freezerTable) truncateTail(items uint64) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Reopen the modified index file to load the changes
|
// Reopen the modified index file to load the changes
|
||||||
if err := t.index.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t.index, err = openFreezerFileForAppend(t.index.Name())
|
t.index, err = openFreezerFileForAppend(t.index.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Sync the file to ensure changes are flushed to disk
|
||||||
|
if err := t.index.Sync(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Release any files before the current tail
|
// Release any files before the current tail
|
||||||
t.tailId = newTailId
|
t.tailId = newTailId
|
||||||
t.itemOffset.Store(newDeleted)
|
t.itemOffset.Store(newDeleted)
|
||||||
|
|
@ -774,7 +785,7 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
|
||||||
return fmt.Errorf("missing data file %d", fileId)
|
return fmt.Errorf("missing data file %d", fileId)
|
||||||
}
|
}
|
||||||
if _, err := dataFile.ReadAt(output[len(output)-length:], int64(start)); err != nil {
|
if _, err := dataFile.ReadAt(output[len(output)-length:], int64(start)); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w, fileid: %d, start: %d, length: %d", err, fileId, start, length)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,12 +125,12 @@ func prune(snaptree *snapshot.Tree, root common.Hash, maindb ethdb.Database, sta
|
||||||
// dangling node is the state root is super low. So the dangling nodes in
|
// dangling node is the state root is super low. So the dangling nodes in
|
||||||
// theory will never ever be visited again.
|
// theory will never ever be visited again.
|
||||||
var (
|
var (
|
||||||
count int
|
skipped, count int
|
||||||
size common.StorageSize
|
size common.StorageSize
|
||||||
pstart = time.Now()
|
pstart = time.Now()
|
||||||
logged = time.Now()
|
logged = time.Now()
|
||||||
batch = maindb.NewBatch()
|
batch = maindb.NewBatch()
|
||||||
iter = maindb.NewIterator(nil, nil)
|
iter = maindb.NewIterator(nil, nil)
|
||||||
)
|
)
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
key := iter.Key()
|
key := iter.Key()
|
||||||
|
|
@ -149,6 +149,7 @@ func prune(snaptree *snapshot.Tree, root common.Hash, maindb ethdb.Database, sta
|
||||||
log.Debug("Forcibly delete the middle state roots", "hash", common.BytesToHash(checkKey))
|
log.Debug("Forcibly delete the middle state roots", "hash", common.BytesToHash(checkKey))
|
||||||
} else {
|
} else {
|
||||||
if stateBloom.Contain(checkKey) {
|
if stateBloom.Contain(checkKey) {
|
||||||
|
skipped += 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +166,7 @@ func prune(snaptree *snapshot.Tree, root common.Hash, maindb ethdb.Database, sta
|
||||||
eta = time.Duration(left/speed) * time.Millisecond
|
eta = time.Duration(left/speed) * time.Millisecond
|
||||||
}
|
}
|
||||||
if time.Since(logged) > 8*time.Second {
|
if time.Since(logged) > 8*time.Second {
|
||||||
log.Info("Pruning state data", "nodes", count, "size", size,
|
log.Info("Pruning state data", "nodes", count, "skipped", skipped, "size", size,
|
||||||
"elapsed", common.PrettyDuration(time.Since(pstart)), "eta", common.PrettyDuration(eta))
|
"elapsed", common.PrettyDuration(time.Since(pstart)), "eta", common.PrettyDuration(eta))
|
||||||
logged = time.Now()
|
logged = time.Now()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -362,21 +362,15 @@ func generateTrieRoot(db ethdb.KeyValueWriter, scheme string, it Iterator, accou
|
||||||
}
|
}
|
||||||
|
|
||||||
func stackTrieGenerate(db ethdb.KeyValueWriter, scheme string, owner common.Hash, in chan trieKV, out chan common.Hash) {
|
func stackTrieGenerate(db ethdb.KeyValueWriter, scheme string, owner common.Hash, in chan trieKV, out chan common.Hash) {
|
||||||
var nodeWriter trie.NodeWriteFunc
|
options := trie.NewStackTrieOptions()
|
||||||
if db != nil {
|
if db != nil {
|
||||||
nodeWriter = func(path []byte, hash common.Hash, blob []byte) {
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
rawdb.WriteTrieNode(db, owner, path, hash, blob, scheme)
|
rawdb.WriteTrieNode(db, owner, path, hash, blob, scheme)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
t := trie.NewStackTrie(nodeWriter)
|
t := trie.NewStackTrie(options)
|
||||||
for leaf := range in {
|
for leaf := range in {
|
||||||
t.Update(leaf.key[:], leaf.value)
|
t.Update(leaf.key[:], leaf.value)
|
||||||
}
|
}
|
||||||
var root common.Hash
|
out <- t.Commit()
|
||||||
if db == nil {
|
|
||||||
root = t.Hash()
|
|
||||||
} else {
|
|
||||||
root, _ = t.Commit()
|
|
||||||
}
|
|
||||||
out <- root
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,16 @@ type diskLayer struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Release releases underlying resources; specifically the fastcache requires
|
||||||
|
// Reset() in order to not leak memory.
|
||||||
|
// OBS: It does not invoke Close on the diskdb
|
||||||
|
func (dl *diskLayer) Release() error {
|
||||||
|
if dl.cache != nil {
|
||||||
|
dl.cache.Reset()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Root returns root hash for which this snapshot was made.
|
// Root returns root hash for which this snapshot was made.
|
||||||
func (dl *diskLayer) Root() common.Hash {
|
func (dl *diskLayer) Root() common.Hash {
|
||||||
return dl.root
|
return dl.root
|
||||||
|
|
|
||||||
|
|
@ -656,6 +656,13 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Release releases resources
|
||||||
|
func (t *Tree) Release() {
|
||||||
|
if dl := t.disklayer(); dl != nil {
|
||||||
|
dl.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Journal commits an entire diff hierarchy to disk into a single journal entry.
|
// Journal commits an entire diff hierarchy to disk into a single journal entry.
|
||||||
// This is meant to be used during shutdown to persist the snapshot without
|
// This is meant to be used during shutdown to persist the snapshot without
|
||||||
// flattening everything down (bad for reorgs).
|
// flattening everything down (bad for reorgs).
|
||||||
|
|
|
||||||
|
|
@ -964,10 +964,12 @@ func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (boo
|
||||||
nodes = trienode.NewNodeSet(addrHash)
|
nodes = trienode.NewNodeSet(addrHash)
|
||||||
slots = make(map[common.Hash][]byte)
|
slots = make(map[common.Hash][]byte)
|
||||||
)
|
)
|
||||||
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
options := trie.NewStackTrieOptions()
|
||||||
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
nodes.AddNode(path, trienode.NewDeleted())
|
nodes.AddNode(path, trienode.NewDeleted())
|
||||||
size += common.StorageSize(len(path))
|
size += common.StorageSize(len(path))
|
||||||
})
|
})
|
||||||
|
stack := trie.NewStackTrie(options)
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
if size > storageDeleteLimit {
|
if size > storageDeleteLimit {
|
||||||
return true, size, nil, nil, nil
|
return true, size, nil, nil, nil
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrAlreadyKnown is returned if the transactions is already contained
|
|
||||||
// within the pool.
|
|
||||||
ErrAlreadyKnown = errors.New("already known")
|
|
||||||
|
|
||||||
// ErrTxPoolOverflow is returned if the transaction pool is full and can't accept
|
// ErrTxPoolOverflow is returned if the transaction pool is full and can't accept
|
||||||
// another remote transaction.
|
// another remote transaction.
|
||||||
ErrTxPoolOverflow = errors.New("txpool is full")
|
ErrTxPoolOverflow = errors.New("txpool is full")
|
||||||
|
|
@ -660,7 +656,7 @@ func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, e
|
||||||
if pool.all.Get(hash) != nil {
|
if pool.all.Get(hash) != nil {
|
||||||
log.Trace("Discarding already known transaction", "hash", hash)
|
log.Trace("Discarding already known transaction", "hash", hash)
|
||||||
knownTxMeter.Mark(1)
|
knownTxMeter.Mark(1)
|
||||||
return false, ErrAlreadyKnown
|
return false, txpool.ErrAlreadyKnown
|
||||||
}
|
}
|
||||||
// Make the local flag. If it's from local source or it's from the network but
|
// Make the local flag. If it's from local source or it's from the network but
|
||||||
// the sender is marked as local previously, treat it as the local transaction.
|
// the sender is marked as local previously, treat it as the local transaction.
|
||||||
|
|
@ -971,7 +967,7 @@ func (pool *LegacyPool) Add(txs []*types.Transaction, local, sync bool) []error
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
// If the transaction is known, pre-set the error slot
|
// If the transaction is known, pre-set the error slot
|
||||||
if pool.all.Get(tx.Hash()) != nil {
|
if pool.all.Get(tx.Hash()) != nil {
|
||||||
errs[i] = ErrAlreadyKnown
|
errs[i] = txpool.ErrAlreadyKnown
|
||||||
knownTxMeter.Mark(1)
|
knownTxMeter.Mark(1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Input": "01d18459b334ffe8e2226eef1db874fda6db2bdd9357268b39220af2d59464fb564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a1978a0d595c823c05947b1156175e72634a377808384256e9921ebf72181890be2d6b58d4a73a880541d1656875654806942307f266e636553e94006d11423f2688945ff3bdf515859eba1005c1a7708d620a94d91a1c0c285f9584e75ec2f82a",
|
"Input": "01e798154708fe7789429634053cbf9f99b619f9f084048927333fce637f549b564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a18f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7873033e038326e87ed3e1276fd140253fa08e9fc25fb2d9a98527fc22a2c9612fbeafdad446cbc7bcdbdcd780af2c16a",
|
||||||
"Expected": "000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001",
|
"Expected": "000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001",
|
||||||
"Name": "pointEvaluation1",
|
"Name": "pointEvaluation1",
|
||||||
"Gas": 50000,
|
"Gas": 50000,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,24 @@
|
||||||
//go:build gofuzz
|
// Only enable fuzzer on platforms with AVX enabled
|
||||||
// +build gofuzz
|
//go:build go1.7 && amd64 && !gccgo && !appengine
|
||||||
|
// +build go1.7,amd64,!gccgo,!appengine
|
||||||
|
|
||||||
package blake2b
|
package blake2b
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func fuzz(data []byte) {
|
||||||
// Make sure the data confirms to the input model
|
// Make sure the data confirms to the input model
|
||||||
if len(data) != 211 {
|
if len(data) != 211 {
|
||||||
return 0
|
return
|
||||||
}
|
}
|
||||||
// Parse everything and call all the implementations
|
// Parse everything and call all the implementations
|
||||||
var (
|
var (
|
||||||
|
|
@ -21,6 +29,7 @@ func Fuzz(data []byte) int {
|
||||||
t [2]uint64
|
t [2]uint64
|
||||||
f uint64
|
f uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
offset := 2 + i*8
|
offset := 2 + i*8
|
||||||
h[i] = binary.LittleEndian.Uint64(data[offset : offset+8])
|
h[i] = binary.LittleEndian.Uint64(data[offset : offset+8])
|
||||||
|
|
@ -35,24 +44,32 @@ func Fuzz(data []byte) int {
|
||||||
if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1
|
if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1
|
||||||
f = 0xFFFFFFFFFFFFFFFF
|
f = 0xFFFFFFFFFFFFFFFF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the blake2b compression on all instruction sets and cross reference
|
// Run the blake2b compression on all instruction sets and cross reference
|
||||||
want := h
|
want := h
|
||||||
fGeneric(&want, &m, t[0], t[1], f, uint64(rounds))
|
fGeneric(&want, &m, t[0], t[1], f, uint64(rounds))
|
||||||
|
|
||||||
have := h
|
have := h
|
||||||
fSSE4(&have, &m, t[0], t[1], f, uint64(rounds))
|
if useSSE4 {
|
||||||
if have != want {
|
fSSE4(&have, &m, t[0], t[1], f, uint64(rounds))
|
||||||
panic("SSE4 mismatches generic algo")
|
if have != want {
|
||||||
|
panic("SSE4 mismatches generic algo")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
have = h
|
|
||||||
fAVX(&have, &m, t[0], t[1], f, uint64(rounds))
|
if useAVX {
|
||||||
if have != want {
|
have = h
|
||||||
panic("AVX mismatches generic algo")
|
fAVX(&have, &m, t[0], t[1], f, uint64(rounds))
|
||||||
|
if have != want {
|
||||||
|
panic("AVX mismatches generic algo")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
have = h
|
|
||||||
fAVX2(&have, &m, t[0], t[1], f, uint64(rounds))
|
if useAVX2 {
|
||||||
if have != want {
|
have = h
|
||||||
panic("AVX2 mismatches generic algo")
|
fAVX2(&have, &m, t[0], t[1], f, uint64(rounds))
|
||||||
|
if have != want {
|
||||||
|
panic("AVX2 mismatches generic algo")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -61,7 +61,7 @@ const (
|
||||||
maxTxUnderpricedSetSize = 32768
|
maxTxUnderpricedSetSize = 32768
|
||||||
|
|
||||||
// maxTxUnderpricedTimeout is the max time a transaction should be stuck in the underpriced set.
|
// maxTxUnderpricedTimeout is the max time a transaction should be stuck in the underpriced set.
|
||||||
maxTxUnderpricedTimeout = int64(5 * time.Minute)
|
maxTxUnderpricedTimeout = 5 * time.Minute
|
||||||
|
|
||||||
// txArriveTimeout is the time allowance before an announced transaction is
|
// txArriveTimeout is the time allowance before an announced transaction is
|
||||||
// explicitly requested.
|
// explicitly requested.
|
||||||
|
|
@ -167,7 +167,7 @@ type TxFetcher struct {
|
||||||
drop chan *txDrop
|
drop chan *txDrop
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
|
|
||||||
underpriced *lru.Cache[common.Hash, int64] // Transactions discarded as too cheap (don't re-fetch)
|
underpriced *lru.Cache[common.Hash, time.Time] // Transactions discarded as too cheap (don't re-fetch)
|
||||||
|
|
||||||
// Stage 1: Waiting lists for newly discovered transactions that might be
|
// Stage 1: Waiting lists for newly discovered transactions that might be
|
||||||
// broadcast without needing explicit request/reply round trips.
|
// broadcast without needing explicit request/reply round trips.
|
||||||
|
|
@ -222,7 +222,7 @@ func NewTxFetcherForTests(
|
||||||
fetching: make(map[common.Hash]string),
|
fetching: make(map[common.Hash]string),
|
||||||
requests: make(map[string]*txRequest),
|
requests: make(map[string]*txRequest),
|
||||||
alternates: make(map[common.Hash]map[string]struct{}),
|
alternates: make(map[common.Hash]map[string]struct{}),
|
||||||
underpriced: lru.NewCache[common.Hash, int64](maxTxUnderpricedSetSize),
|
underpriced: lru.NewCache[common.Hash, time.Time](maxTxUnderpricedSetSize),
|
||||||
hasTx: hasTx,
|
hasTx: hasTx,
|
||||||
addTxs: addTxs,
|
addTxs: addTxs,
|
||||||
fetchTxs: fetchTxs,
|
fetchTxs: fetchTxs,
|
||||||
|
|
@ -284,7 +284,7 @@ func (f *TxFetcher) Notify(peer string, types []byte, sizes []uint32, hashes []c
|
||||||
// isKnownUnderpriced reports whether a transaction hash was recently found to be underpriced.
|
// isKnownUnderpriced reports whether a transaction hash was recently found to be underpriced.
|
||||||
func (f *TxFetcher) isKnownUnderpriced(hash common.Hash) bool {
|
func (f *TxFetcher) isKnownUnderpriced(hash common.Hash) bool {
|
||||||
prevTime, ok := f.underpriced.Peek(hash)
|
prevTime, ok := f.underpriced.Peek(hash)
|
||||||
if ok && prevTime+maxTxUnderpricedTimeout < time.Now().Unix() {
|
if ok && prevTime.Before(time.Now().Add(-maxTxUnderpricedTimeout)) {
|
||||||
f.underpriced.Remove(hash)
|
f.underpriced.Remove(hash)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -335,7 +335,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
// Avoid re-request this transaction when we receive another
|
// Avoid re-request this transaction when we receive another
|
||||||
// announcement.
|
// announcement.
|
||||||
if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) {
|
if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) {
|
||||||
f.underpriced.Add(batch[j].Hash(), batch[j].Time().Unix())
|
f.underpriced.Add(batch[j].Hash(), batch[j].Time())
|
||||||
}
|
}
|
||||||
// Track a few interesting failure types
|
// Track a few interesting failure types
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -363,7 +363,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
// If 'other reject' is >25% of the deliveries in any batch, sleep a bit.
|
// If 'other reject' is >25% of the deliveries in any batch, sleep a bit.
|
||||||
if otherreject > 128/4 {
|
if otherreject > 128/4 {
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
log.Warn("Peer delivering stale transactions", "peer", peer, "rejected", otherreject)
|
log.Debug("Peer delivering stale transactions", "peer", peer, "rejected", otherreject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|
@ -593,8 +593,9 @@ func (f *TxFetcher) loop() {
|
||||||
log.Warn("Announced transaction type mismatch", "peer", peer, "tx", hash, "type", delivery.metas[i].kind, "ann", meta.kind)
|
log.Warn("Announced transaction type mismatch", "peer", peer, "tx", hash, "type", delivery.metas[i].kind, "ann", meta.kind)
|
||||||
f.dropPeer(peer)
|
f.dropPeer(peer)
|
||||||
} else if delivery.metas[i].size != meta.size {
|
} else if delivery.metas[i].size != meta.size {
|
||||||
log.Warn("Announced transaction size mismatch", "peer", peer, "tx", hash, "size", delivery.metas[i].size, "ann", meta.size)
|
|
||||||
if math.Abs(float64(delivery.metas[i].size)-float64(meta.size)) > 8 {
|
if math.Abs(float64(delivery.metas[i].size)-float64(meta.size)) > 8 {
|
||||||
|
log.Warn("Announced transaction size mismatch", "peer", peer, "tx", hash, "size", delivery.metas[i].size, "ann", meta.size)
|
||||||
|
|
||||||
// Normally we should drop a peer considering this is a protocol violation.
|
// Normally we should drop a peer considering this is a protocol violation.
|
||||||
// However, due to the RLP vs consensus format messyness, allow a few bytes
|
// However, due to the RLP vs consensus format messyness, allow a few bytes
|
||||||
// wiggle-room where we only warn, but don't drop.
|
// wiggle-room where we only warn, but don't drop.
|
||||||
|
|
@ -618,8 +619,9 @@ func (f *TxFetcher) loop() {
|
||||||
log.Warn("Announced transaction type mismatch", "peer", peer, "tx", hash, "type", delivery.metas[i].kind, "ann", meta.kind)
|
log.Warn("Announced transaction type mismatch", "peer", peer, "tx", hash, "type", delivery.metas[i].kind, "ann", meta.kind)
|
||||||
f.dropPeer(peer)
|
f.dropPeer(peer)
|
||||||
} else if delivery.metas[i].size != meta.size {
|
} else if delivery.metas[i].size != meta.size {
|
||||||
log.Warn("Announced transaction size mismatch", "peer", peer, "tx", hash, "size", delivery.metas[i].size, "ann", meta.size)
|
|
||||||
if math.Abs(float64(delivery.metas[i].size)-float64(meta.size)) > 8 {
|
if math.Abs(float64(delivery.metas[i].size)-float64(meta.size)) > 8 {
|
||||||
|
log.Warn("Announced transaction size mismatch", "peer", peer, "tx", hash, "size", delivery.metas[i].size, "ann", meta.size)
|
||||||
|
|
||||||
// Normally we should drop a peer considering this is a protocol violation.
|
// Normally we should drop a peer considering this is a protocol violation.
|
||||||
// However, due to the RLP vs consensus format messyness, allow a few bytes
|
// However, due to the RLP vs consensus format messyness, allow a few bytes
|
||||||
// wiggle-room where we only warn, but don't drop.
|
// wiggle-room where we only warn, but don't drop.
|
||||||
|
|
|
||||||
|
|
@ -1993,3 +1993,38 @@ func containsHash(slice []common.Hash, hash common.Hash) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that a transaction is forgotten after the timeout.
|
||||||
|
func TestTransactionForgotten(t *testing.T) {
|
||||||
|
fetcher := NewTxFetcher(
|
||||||
|
func(common.Hash) bool { return false },
|
||||||
|
func(txs []*types.Transaction) []error {
|
||||||
|
errs := make([]error, len(txs))
|
||||||
|
for i := 0; i < len(errs); i++ {
|
||||||
|
errs[i] = txpool.ErrUnderpriced
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
},
|
||||||
|
func(string, []common.Hash) error { return nil },
|
||||||
|
func(string) {},
|
||||||
|
)
|
||||||
|
fetcher.Start()
|
||||||
|
defer fetcher.Stop()
|
||||||
|
// Create one TX which is 5 minutes old, and one which is recent
|
||||||
|
tx1 := types.NewTx(&types.LegacyTx{Nonce: 0})
|
||||||
|
tx1.SetTime(time.Now().Add(-maxTxUnderpricedTimeout - 1*time.Second))
|
||||||
|
tx2 := types.NewTx(&types.LegacyTx{Nonce: 1})
|
||||||
|
|
||||||
|
// Enqueue both in the fetcher. They will be immediately tagged as underpriced
|
||||||
|
if err := fetcher.Enqueue("asdf", []*types.Transaction{tx1, tx2}, false); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// isKnownUnderpriced should trigger removal of the first tx (no longer be known underpriced)
|
||||||
|
if fetcher.isKnownUnderpriced(tx1.Hash()) {
|
||||||
|
t.Fatal("transaction should be forgotten by now")
|
||||||
|
}
|
||||||
|
// isKnownUnderpriced should not trigger removal of the second
|
||||||
|
if !fetcher.isKnownUnderpriced(tx2.Hash()) {
|
||||||
|
t.Fatal("transaction should be known underpriced")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -915,10 +915,14 @@ func TestPendingTxFilterDeadlock(t *testing.T) {
|
||||||
|
|
||||||
// Create a bunch of filters that will
|
// Create a bunch of filters that will
|
||||||
// timeout either in 100ms or 200ms
|
// timeout either in 100ms or 200ms
|
||||||
fids := make([]rpc.ID, 20)
|
subs := make([]*Subscription, 20)
|
||||||
for i := 0; i < len(fids); i++ {
|
for i := 0; i < len(subs); i++ {
|
||||||
fid := api.NewPendingTransactionFilter(nil)
|
fid := api.NewPendingTransactionFilter(nil)
|
||||||
fids[i] = fid
|
f, ok := api.filters[fid]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Filter %s should exist", fid)
|
||||||
|
}
|
||||||
|
subs[i] = f.s
|
||||||
// Wait for at least one tx to arrive in filter
|
// Wait for at least one tx to arrive in filter
|
||||||
for {
|
for {
|
||||||
hashes, err := api.GetFilterChanges(fid)
|
hashes, err := api.GetFilterChanges(fid)
|
||||||
|
|
@ -932,21 +936,13 @@ func TestPendingTxFilterDeadlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until filters have timed out
|
// Wait until filters have timed out and have been uninstalled.
|
||||||
time.Sleep(3 * timeout)
|
for _, sub := range subs {
|
||||||
|
select {
|
||||||
// If tx loop doesn't consume `done` after a second
|
case <-sub.Err():
|
||||||
// it's hanging.
|
case <-time.After(1 * time.Second):
|
||||||
select {
|
t.Fatalf("Filter timeout is hanging")
|
||||||
case done <- struct{}{}:
|
|
||||||
// Check that all filters have been uninstalled
|
|
||||||
for _, fid := range fids {
|
|
||||||
if _, err := api.GetFilterChanges(fid); err == nil {
|
|
||||||
t.Errorf("Filter %s should have been uninstalled\n", fid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case <-time.After(1 * time.Second):
|
|
||||||
t.Error("Tx sending loop hangs")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,32 @@ var (
|
||||||
|
|
||||||
IngressRegistrationErrorMeter = metrics.NewRegisteredMeter(ingressRegistrationErrorName, nil)
|
IngressRegistrationErrorMeter = metrics.NewRegisteredMeter(ingressRegistrationErrorName, nil)
|
||||||
EgressRegistrationErrorMeter = metrics.NewRegisteredMeter(egressRegistrationErrorName, nil)
|
EgressRegistrationErrorMeter = metrics.NewRegisteredMeter(egressRegistrationErrorName, nil)
|
||||||
|
|
||||||
|
// deletionGauge is the metric to track how many trie node deletions
|
||||||
|
// are performed in total during the sync process.
|
||||||
|
deletionGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/delete", nil)
|
||||||
|
|
||||||
|
// lookupGauge is the metric to track how many trie node lookups are
|
||||||
|
// performed to determine if node needs to be deleted.
|
||||||
|
lookupGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/lookup", nil)
|
||||||
|
|
||||||
|
// boundaryAccountNodesGauge is the metric to track how many boundary trie
|
||||||
|
// nodes in account trie are met.
|
||||||
|
boundaryAccountNodesGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/boundary/account", nil)
|
||||||
|
|
||||||
|
// boundaryAccountNodesGauge is the metric to track how many boundary trie
|
||||||
|
// nodes in storage tries are met.
|
||||||
|
boundaryStorageNodesGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/boundary/storage", nil)
|
||||||
|
|
||||||
|
// smallStorageGauge is the metric to track how many storages are small enough
|
||||||
|
// to retrieved in one or two request.
|
||||||
|
smallStorageGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/small", nil)
|
||||||
|
|
||||||
|
// largeStorageGauge is the metric to track how many storages are large enough
|
||||||
|
// to retrieved concurrently.
|
||||||
|
largeStorageGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/large", nil)
|
||||||
|
|
||||||
|
// skipStorageHealingGauge is the metric to track how many storages are retrieved
|
||||||
|
// in multiple requests but healing is not necessary.
|
||||||
|
skipStorageHealingGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/noheal", nil)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -716,6 +716,19 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanPath is used to remove the dangling nodes in the stackTrie.
|
||||||
|
func (s *Syncer) cleanPath(batch ethdb.Batch, owner common.Hash, path []byte) {
|
||||||
|
if owner == (common.Hash{}) && rawdb.ExistsAccountTrieNode(s.db, path) {
|
||||||
|
rawdb.DeleteAccountTrieNode(batch, path)
|
||||||
|
deletionGauge.Inc(1)
|
||||||
|
}
|
||||||
|
if owner != (common.Hash{}) && rawdb.ExistsStorageTrieNode(s.db, owner, path) {
|
||||||
|
rawdb.DeleteStorageTrieNode(batch, owner, path)
|
||||||
|
deletionGauge.Inc(1)
|
||||||
|
}
|
||||||
|
lookupGauge.Inc(1)
|
||||||
|
}
|
||||||
|
|
||||||
// loadSyncStatus retrieves a previously aborted sync status from the database,
|
// loadSyncStatus retrieves a previously aborted sync status from the database,
|
||||||
// or generates a fresh one if none is available.
|
// or generates a fresh one if none is available.
|
||||||
func (s *Syncer) loadSyncStatus() {
|
func (s *Syncer) loadSyncStatus() {
|
||||||
|
|
@ -738,9 +751,22 @@ func (s *Syncer) loadSyncStatus() {
|
||||||
s.accountBytes += common.StorageSize(len(key) + len(value))
|
s.accountBytes += common.StorageSize(len(key) + len(value))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
task.genTrie = trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) {
|
options := trie.NewStackTrieOptions()
|
||||||
rawdb.WriteTrieNode(task.genBatch, common.Hash{}, path, hash, val, s.scheme)
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
rawdb.WriteTrieNode(task.genBatch, common.Hash{}, path, hash, blob, s.scheme)
|
||||||
})
|
})
|
||||||
|
if s.scheme == rawdb.PathScheme {
|
||||||
|
// Configure the dangling node cleaner and also filter out boundary nodes
|
||||||
|
// only in the context of the path scheme. Deletion is forbidden in the
|
||||||
|
// hash scheme, as it can disrupt state completeness.
|
||||||
|
options = options.WithCleaner(func(path []byte) {
|
||||||
|
s.cleanPath(task.genBatch, common.Hash{}, path)
|
||||||
|
})
|
||||||
|
// Skip the left boundary if it's not the first range.
|
||||||
|
// Skip the right boundary if it's not the last range.
|
||||||
|
options = options.WithSkipBoundary(task.Next != (common.Hash{}), task.Last != common.MaxHash, boundaryAccountNodesGauge)
|
||||||
|
}
|
||||||
|
task.genTrie = trie.NewStackTrie(options)
|
||||||
for accountHash, subtasks := range task.SubTasks {
|
for accountHash, subtasks := range task.SubTasks {
|
||||||
for _, subtask := range subtasks {
|
for _, subtask := range subtasks {
|
||||||
subtask := subtask // closure for subtask.genBatch in the stacktrie writer callback
|
subtask := subtask // closure for subtask.genBatch in the stacktrie writer callback
|
||||||
|
|
@ -752,9 +778,22 @@ func (s *Syncer) loadSyncStatus() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
owner := accountHash // local assignment for stacktrie writer closure
|
owner := accountHash // local assignment for stacktrie writer closure
|
||||||
subtask.genTrie = trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) {
|
options := trie.NewStackTrieOptions()
|
||||||
rawdb.WriteTrieNode(subtask.genBatch, owner, path, hash, val, s.scheme)
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
rawdb.WriteTrieNode(subtask.genBatch, owner, path, hash, blob, s.scheme)
|
||||||
})
|
})
|
||||||
|
if s.scheme == rawdb.PathScheme {
|
||||||
|
// Configure the dangling node cleaner and also filter out boundary nodes
|
||||||
|
// only in the context of the path scheme. Deletion is forbidden in the
|
||||||
|
// hash scheme, as it can disrupt state completeness.
|
||||||
|
options = options.WithCleaner(func(path []byte) {
|
||||||
|
s.cleanPath(subtask.genBatch, owner, path)
|
||||||
|
})
|
||||||
|
// Skip the left boundary if it's not the first range.
|
||||||
|
// Skip the right boundary if it's not the last range.
|
||||||
|
options = options.WithSkipBoundary(subtask.Next != common.Hash{}, subtask.Last != common.MaxHash, boundaryStorageNodesGauge)
|
||||||
|
}
|
||||||
|
subtask.genTrie = trie.NewStackTrie(options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -806,14 +845,27 @@ func (s *Syncer) loadSyncStatus() {
|
||||||
s.accountBytes += common.StorageSize(len(key) + len(value))
|
s.accountBytes += common.StorageSize(len(key) + len(value))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
options := trie.NewStackTrieOptions()
|
||||||
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
rawdb.WriteTrieNode(batch, common.Hash{}, path, hash, blob, s.scheme)
|
||||||
|
})
|
||||||
|
if s.scheme == rawdb.PathScheme {
|
||||||
|
// Configure the dangling node cleaner and also filter out boundary nodes
|
||||||
|
// only in the context of the path scheme. Deletion is forbidden in the
|
||||||
|
// hash scheme, as it can disrupt state completeness.
|
||||||
|
options = options.WithCleaner(func(path []byte) {
|
||||||
|
s.cleanPath(batch, common.Hash{}, path)
|
||||||
|
})
|
||||||
|
// Skip the left boundary if it's not the first range.
|
||||||
|
// Skip the right boundary if it's not the last range.
|
||||||
|
options = options.WithSkipBoundary(next != common.Hash{}, last != common.MaxHash, boundaryAccountNodesGauge)
|
||||||
|
}
|
||||||
s.tasks = append(s.tasks, &accountTask{
|
s.tasks = append(s.tasks, &accountTask{
|
||||||
Next: next,
|
Next: next,
|
||||||
Last: last,
|
Last: last,
|
||||||
SubTasks: make(map[common.Hash][]*storageTask),
|
SubTasks: make(map[common.Hash][]*storageTask),
|
||||||
genBatch: batch,
|
genBatch: batch,
|
||||||
genTrie: trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) {
|
genTrie: trie.NewStackTrie(options),
|
||||||
rawdb.WriteTrieNode(batch, common.Hash{}, path, hash, val, s.scheme)
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
log.Debug("Created account sync task", "from", next, "last", last)
|
log.Debug("Created account sync task", "from", next, "last", last)
|
||||||
next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1))
|
next = common.BigToHash(new(big.Int).Add(last.Big(), common.Big1))
|
||||||
|
|
@ -1962,6 +2014,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
if res.subTask == nil && res.mainTask.needState[j] && (i < len(res.hashes)-1 || !res.cont) {
|
if res.subTask == nil && res.mainTask.needState[j] && (i < len(res.hashes)-1 || !res.cont) {
|
||||||
res.mainTask.needState[j] = false
|
res.mainTask.needState[j] = false
|
||||||
res.mainTask.pend--
|
res.mainTask.pend--
|
||||||
|
smallStorageGauge.Inc(1)
|
||||||
}
|
}
|
||||||
// If the last contract was chunked, mark it as needing healing
|
// If the last contract was chunked, mark it as needing healing
|
||||||
// to avoid writing it out to disk prematurely.
|
// to avoid writing it out to disk prematurely.
|
||||||
|
|
@ -1997,7 +2050,11 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
log.Debug("Chunked large contract", "initiators", len(keys), "tail", lastKey, "chunks", chunks)
|
log.Debug("Chunked large contract", "initiators", len(keys), "tail", lastKey, "chunks", chunks)
|
||||||
}
|
}
|
||||||
r := newHashRange(lastKey, chunks)
|
r := newHashRange(lastKey, chunks)
|
||||||
|
if chunks == 1 {
|
||||||
|
smallStorageGauge.Inc(1)
|
||||||
|
} else {
|
||||||
|
largeStorageGauge.Inc(1)
|
||||||
|
}
|
||||||
// Our first task is the one that was just filled by this response.
|
// Our first task is the one that was just filled by this response.
|
||||||
batch := ethdb.HookedBatch{
|
batch := ethdb.HookedBatch{
|
||||||
Batch: s.db.NewBatch(),
|
Batch: s.db.NewBatch(),
|
||||||
|
|
@ -2006,14 +2063,24 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
owner := account // local assignment for stacktrie writer closure
|
owner := account // local assignment for stacktrie writer closure
|
||||||
|
options := trie.NewStackTrieOptions()
|
||||||
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
rawdb.WriteTrieNode(batch, owner, path, hash, blob, s.scheme)
|
||||||
|
})
|
||||||
|
if s.scheme == rawdb.PathScheme {
|
||||||
|
options = options.WithCleaner(func(path []byte) {
|
||||||
|
s.cleanPath(batch, owner, path)
|
||||||
|
})
|
||||||
|
// Keep the left boundary as it's the first range.
|
||||||
|
// Skip the right boundary if it's not the last range.
|
||||||
|
options = options.WithSkipBoundary(false, r.End() != common.MaxHash, boundaryStorageNodesGauge)
|
||||||
|
}
|
||||||
tasks = append(tasks, &storageTask{
|
tasks = append(tasks, &storageTask{
|
||||||
Next: common.Hash{},
|
Next: common.Hash{},
|
||||||
Last: r.End(),
|
Last: r.End(),
|
||||||
root: acc.Root,
|
root: acc.Root,
|
||||||
genBatch: batch,
|
genBatch: batch,
|
||||||
genTrie: trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) {
|
genTrie: trie.NewStackTrie(options),
|
||||||
rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
for r.Next() {
|
for r.Next() {
|
||||||
batch := ethdb.HookedBatch{
|
batch := ethdb.HookedBatch{
|
||||||
|
|
@ -2022,14 +2089,27 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
s.storageBytes += common.StorageSize(len(key) + len(value))
|
s.storageBytes += common.StorageSize(len(key) + len(value))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
options := trie.NewStackTrieOptions()
|
||||||
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
rawdb.WriteTrieNode(batch, owner, path, hash, blob, s.scheme)
|
||||||
|
})
|
||||||
|
if s.scheme == rawdb.PathScheme {
|
||||||
|
// Configure the dangling node cleaner and also filter out boundary nodes
|
||||||
|
// only in the context of the path scheme. Deletion is forbidden in the
|
||||||
|
// hash scheme, as it can disrupt state completeness.
|
||||||
|
options = options.WithCleaner(func(path []byte) {
|
||||||
|
s.cleanPath(batch, owner, path)
|
||||||
|
})
|
||||||
|
// Skip the left boundary as it's not the first range
|
||||||
|
// Skip the right boundary if it's not the last range.
|
||||||
|
options = options.WithSkipBoundary(true, r.End() != common.MaxHash, boundaryStorageNodesGauge)
|
||||||
|
}
|
||||||
tasks = append(tasks, &storageTask{
|
tasks = append(tasks, &storageTask{
|
||||||
Next: r.Start(),
|
Next: r.Start(),
|
||||||
Last: r.End(),
|
Last: r.End(),
|
||||||
root: acc.Root,
|
root: acc.Root,
|
||||||
genBatch: batch,
|
genBatch: batch,
|
||||||
genTrie: trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) {
|
genTrie: trie.NewStackTrie(options),
|
||||||
rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, task := range tasks {
|
for _, task := range tasks {
|
||||||
|
|
@ -2075,9 +2155,22 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
|
|
||||||
if i < len(res.hashes)-1 || res.subTask == nil {
|
if i < len(res.hashes)-1 || res.subTask == nil {
|
||||||
// no need to make local reassignment of account: this closure does not outlive the loop
|
// no need to make local reassignment of account: this closure does not outlive the loop
|
||||||
tr := trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) {
|
options := trie.NewStackTrieOptions()
|
||||||
rawdb.WriteTrieNode(batch, account, path, hash, val, s.scheme)
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
rawdb.WriteTrieNode(batch, account, path, hash, blob, s.scheme)
|
||||||
})
|
})
|
||||||
|
if s.scheme == rawdb.PathScheme {
|
||||||
|
// Configure the dangling node cleaner only in the context of the
|
||||||
|
// path scheme. Deletion is forbidden in the hash scheme, as it can
|
||||||
|
// disrupt state completeness.
|
||||||
|
//
|
||||||
|
// Notably, boundary nodes can be also kept because the whole storage
|
||||||
|
// trie is complete.
|
||||||
|
options = options.WithCleaner(func(path []byte) {
|
||||||
|
s.cleanPath(batch, account, path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
tr := trie.NewStackTrie(options)
|
||||||
for j := 0; j < len(res.hashes[i]); j++ {
|
for j := 0; j < len(res.hashes[i]); j++ {
|
||||||
tr.Update(res.hashes[i][j][:], res.slots[i][j])
|
tr.Update(res.hashes[i][j][:], res.slots[i][j])
|
||||||
}
|
}
|
||||||
|
|
@ -2099,18 +2192,25 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
|
||||||
// Large contracts could have generated new trie nodes, flush them to disk
|
// Large contracts could have generated new trie nodes, flush them to disk
|
||||||
if res.subTask != nil {
|
if res.subTask != nil {
|
||||||
if res.subTask.done {
|
if res.subTask.done {
|
||||||
if root, err := res.subTask.genTrie.Commit(); err != nil {
|
root := res.subTask.genTrie.Commit()
|
||||||
log.Error("Failed to commit stack slots", "err", err)
|
if err := res.subTask.genBatch.Write(); err != nil {
|
||||||
} else if root == res.subTask.root {
|
log.Error("Failed to persist stack slots", "err", err)
|
||||||
// If the chunk's root is an overflown but full delivery, clear the heal request
|
}
|
||||||
|
res.subTask.genBatch.Reset()
|
||||||
|
|
||||||
|
// If the chunk's root is an overflown but full delivery,
|
||||||
|
// clear the heal request.
|
||||||
|
accountHash := res.accounts[len(res.accounts)-1]
|
||||||
|
if root == res.subTask.root && rawdb.HasStorageTrieNode(s.db, accountHash, nil, root) {
|
||||||
for i, account := range res.mainTask.res.hashes {
|
for i, account := range res.mainTask.res.hashes {
|
||||||
if account == res.accounts[len(res.accounts)-1] {
|
if account == accountHash {
|
||||||
res.mainTask.needHeal[i] = false
|
res.mainTask.needHeal[i] = false
|
||||||
|
skipStorageHealingGauge.Inc(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if res.subTask.genBatch.ValueSize() > ethdb.IdealBatchSize || res.subTask.done {
|
if res.subTask.genBatch.ValueSize() > ethdb.IdealBatchSize {
|
||||||
if err := res.subTask.genBatch.Write(); err != nil {
|
if err := res.subTask.genBatch.Write(); err != nil {
|
||||||
log.Error("Failed to persist stack slots", "err", err)
|
log.Error("Failed to persist stack slots", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2317,9 +2417,7 @@ func (s *Syncer) forwardAccountTask(task *accountTask) {
|
||||||
// flush after finalizing task.done. It's fine even if we crash and lose this
|
// flush after finalizing task.done. It's fine even if we crash and lose this
|
||||||
// write as it will only cause more data to be downloaded during heal.
|
// write as it will only cause more data to be downloaded during heal.
|
||||||
if task.done {
|
if task.done {
|
||||||
if _, err := task.genTrie.Commit(); err != nil {
|
task.genTrie.Commit()
|
||||||
log.Error("Failed to commit stack account", "err", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if task.genBatch.ValueSize() > ethdb.IdealBatchSize || task.done {
|
if task.genBatch.ValueSize() > ethdb.IdealBatchSize || task.done {
|
||||||
if err := task.genBatch.Write(); err != nil {
|
if err := task.genBatch.Write(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,13 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNu
|
||||||
return (*big.Int)(&result), err
|
return (*big.Int)(&result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BalanceAtHash returns the wei balance of the given account.
|
||||||
|
func (ec *Client) BalanceAtHash(ctx context.Context, account common.Address, blockHash common.Hash) (*big.Int, error) {
|
||||||
|
var result hexutil.Big
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, rpc.BlockNumberOrHashWithHash(blockHash, false))
|
||||||
|
return (*big.Int)(&result), err
|
||||||
|
}
|
||||||
|
|
||||||
// StorageAt returns the value of key in the contract storage of the given account.
|
// StorageAt returns the value of key in the contract storage of the given account.
|
||||||
// The block number can be nil, in which case the value is taken from the latest known block.
|
// The block number can be nil, in which case the value is taken from the latest known block.
|
||||||
func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
|
@ -378,6 +385,13 @@ func (ec *Client) StorageAt(ctx context.Context, account common.Address, key com
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StorageAtHash returns the value of key in the contract storage of the given account.
|
||||||
|
func (ec *Client) StorageAtHash(ctx context.Context, account common.Address, key common.Hash, blockHash common.Hash) ([]byte, error) {
|
||||||
|
var result hexutil.Bytes
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, rpc.BlockNumberOrHashWithHash(blockHash, false))
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
// CodeAt returns the contract code of the given account.
|
// CodeAt returns the contract code of the given account.
|
||||||
// The block number can be nil, in which case the code is taken from the latest known block.
|
// The block number can be nil, in which case the code is taken from the latest known block.
|
||||||
func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
|
@ -386,6 +400,13 @@ func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumbe
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CodeAtHash returns the contract code of the given account.
|
||||||
|
func (ec *Client) CodeAtHash(ctx context.Context, account common.Address, blockHash common.Hash) ([]byte, error) {
|
||||||
|
var result hexutil.Bytes
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, rpc.BlockNumberOrHashWithHash(blockHash, false))
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
// NonceAt returns the account nonce of the given account.
|
// NonceAt returns the account nonce of the given account.
|
||||||
// The block number can be nil, in which case the nonce is taken from the latest known block.
|
// The block number can be nil, in which case the nonce is taken from the latest known block.
|
||||||
func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||||
|
|
@ -394,6 +415,13 @@ func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumb
|
||||||
return uint64(result), err
|
return uint64(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NonceAtHash returns the account nonce of the given account.
|
||||||
|
func (ec *Client) NonceAtHash(ctx context.Context, account common.Address, blockHash common.Hash) (uint64, error) {
|
||||||
|
var result hexutil.Uint64
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, rpc.BlockNumberOrHashWithHash(blockHash, false))
|
||||||
|
return uint64(result), err
|
||||||
|
}
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
|
|
||||||
// FilterLogs executes a filter query.
|
// FilterLogs executes a filter query.
|
||||||
|
|
|
||||||
|
|
@ -393,6 +393,7 @@ func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) {
|
||||||
// Test tx in block interrupted.
|
// Test tx in block interrupted.
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
cancel()
|
cancel()
|
||||||
|
<-ctx.Done() // Ensure the close of the Done channel
|
||||||
tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
|
tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
|
||||||
if tx != nil {
|
if tx != nil {
|
||||||
t.Fatal("transaction should be nil")
|
t.Fatal("transaction should be nil")
|
||||||
|
|
@ -583,6 +584,11 @@ func testCallContract(t *testing.T, client *rpc.Client) {
|
||||||
func testAtFunctions(t *testing.T, client *rpc.Client) {
|
func testAtFunctions(t *testing.T, client *rpc.Client) {
|
||||||
ec := NewClient(client)
|
ec := NewClient(client)
|
||||||
|
|
||||||
|
block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BlockByNumber error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// send a transaction for some interesting pending status
|
// send a transaction for some interesting pending status
|
||||||
sendTransaction(ec)
|
sendTransaction(ec)
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
@ -600,6 +606,13 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
hashBalance, err := ec.BalanceAtHash(context.Background(), testAddr, block.Hash())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if balance.Cmp(hashBalance) == 0 {
|
||||||
|
t.Fatalf("unexpected balance at hash: %v %v", balance, hashBalance)
|
||||||
|
}
|
||||||
penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr)
|
penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
|
@ -612,6 +625,13 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
hashNonce, err := ec.NonceAtHash(context.Background(), testAddr, block.Hash())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if hashNonce == nonce {
|
||||||
|
t.Fatalf("unexpected nonce at hash: %v %v", nonce, hashNonce)
|
||||||
|
}
|
||||||
penNonce, err := ec.PendingNonceAt(context.Background(), testAddr)
|
penNonce, err := ec.PendingNonceAt(context.Background(), testAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
|
@ -624,6 +644,13 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
hashStorage, err := ec.StorageAtHash(context.Background(), testAddr, common.Hash{}, block.Hash())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(storage, hashStorage) {
|
||||||
|
t.Fatalf("unexpected storage at hash: %v %v", storage, hashStorage)
|
||||||
|
}
|
||||||
penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{})
|
penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
|
@ -636,6 +663,13 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
hashCode, err := ec.CodeAtHash(context.Background(), common.Address{}, block.Hash())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(code, hashCode) {
|
||||||
|
t.Fatalf("unexpected code at hash: %v %v", code, hashCode)
|
||||||
|
}
|
||||||
penCode, err := ec.PendingCodeAt(context.Background(), testAddr)
|
penCode, err := ec.PendingCodeAt(context.Background(), testAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
|
@ -666,6 +700,7 @@ func testTransactionSender(t *testing.T, client *rpc.Client) {
|
||||||
// TransactionSender. Ensure the server is not asked by canceling the context here.
|
// TransactionSender. Ensure the server is not asked by canceling the context here.
|
||||||
canceledCtx, cancel := context.WithCancel(context.Background())
|
canceledCtx, cancel := context.WithCancel(context.Background())
|
||||||
cancel()
|
cancel()
|
||||||
|
<-canceledCtx.Done() // Ensure the close of the Done channel
|
||||||
sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
|
sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
||||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
testContract = common.HexToAddress("0xbeef")
|
||||||
testSlot = common.HexToHash("0xdeadbeef")
|
testSlot = common.HexToHash("0xdeadbeef")
|
||||||
testValue = crypto.Keccak256Hash(testSlot[:])
|
testValue = crypto.Keccak256Hash(testSlot[:])
|
||||||
testBalance = big.NewInt(2e15)
|
testBalance = big.NewInt(2e15)
|
||||||
|
|
@ -89,8 +91,9 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
||||||
|
|
||||||
func generateTestChain() (*core.Genesis, []*types.Block) {
|
func generateTestChain() (*core.Genesis, []*types.Block) {
|
||||||
genesis := &core.Genesis{
|
genesis := &core.Genesis{
|
||||||
Config: params.AllEthashProtocolChanges,
|
Config: params.AllEthashProtocolChanges,
|
||||||
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance, Storage: map[common.Hash]common.Hash{testSlot: testValue}}},
|
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance, Storage: map[common.Hash]common.Hash{testSlot: testValue}},
|
||||||
|
testContract: {Nonce: 1, Code: []byte{0x13, 0x37}}},
|
||||||
ExtraData: []byte("test genesis"),
|
ExtraData: []byte("test genesis"),
|
||||||
Timestamp: 9000,
|
Timestamp: 9000,
|
||||||
}
|
}
|
||||||
|
|
@ -128,8 +131,11 @@ func TestGethClient(t *testing.T) {
|
||||||
test func(t *testing.T)
|
test func(t *testing.T)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"TestGetProof",
|
"TestGetProof1",
|
||||||
func(t *testing.T) { testGetProof(t, client) },
|
func(t *testing.T) { testGetProof(t, client, testAddr) },
|
||||||
|
}, {
|
||||||
|
"TestGetProof2",
|
||||||
|
func(t *testing.T) { testGetProof(t, client, testContract) },
|
||||||
}, {
|
}, {
|
||||||
"TestGetProofCanonicalizeKeys",
|
"TestGetProofCanonicalizeKeys",
|
||||||
func(t *testing.T) { testGetProofCanonicalizeKeys(t, client) },
|
func(t *testing.T) { testGetProofCanonicalizeKeys(t, client) },
|
||||||
|
|
@ -244,38 +250,41 @@ func testAccessList(t *testing.T, client *rpc.Client) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetProof(t *testing.T, client *rpc.Client) {
|
func testGetProof(t *testing.T, client *rpc.Client, addr common.Address) {
|
||||||
ec := New(client)
|
ec := New(client)
|
||||||
ethcl := ethclient.NewClient(client)
|
ethcl := ethclient.NewClient(client)
|
||||||
result, err := ec.GetProof(context.Background(), testAddr, []string{testSlot.String()}, nil)
|
result, err := ec.GetProof(context.Background(), addr, []string{testSlot.String()}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(result.Address[:], testAddr[:]) {
|
if result.Address != addr {
|
||||||
t.Fatalf("unexpected address, want: %v got: %v", testAddr, result.Address)
|
t.Fatalf("unexpected address, have: %v want: %v", result.Address, addr)
|
||||||
}
|
}
|
||||||
// test nonce
|
// test nonce
|
||||||
nonce, _ := ethcl.NonceAt(context.Background(), result.Address, nil)
|
if nonce, _ := ethcl.NonceAt(context.Background(), addr, nil); result.Nonce != nonce {
|
||||||
if result.Nonce != nonce {
|
|
||||||
t.Fatalf("invalid nonce, want: %v got: %v", nonce, result.Nonce)
|
t.Fatalf("invalid nonce, want: %v got: %v", nonce, result.Nonce)
|
||||||
}
|
}
|
||||||
// test balance
|
// test balance
|
||||||
balance, _ := ethcl.BalanceAt(context.Background(), result.Address, nil)
|
if balance, _ := ethcl.BalanceAt(context.Background(), addr, nil); result.Balance.Cmp(balance) != 0 {
|
||||||
if result.Balance.Cmp(balance) != 0 {
|
|
||||||
t.Fatalf("invalid balance, want: %v got: %v", balance, result.Balance)
|
t.Fatalf("invalid balance, want: %v got: %v", balance, result.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test storage
|
// test storage
|
||||||
if len(result.StorageProof) != 1 {
|
if len(result.StorageProof) != 1 {
|
||||||
t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(result.StorageProof))
|
t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(result.StorageProof))
|
||||||
}
|
}
|
||||||
proof := result.StorageProof[0]
|
for _, proof := range result.StorageProof {
|
||||||
slotValue, _ := ethcl.StorageAt(context.Background(), testAddr, testSlot, nil)
|
if proof.Key != testSlot.String() {
|
||||||
if !bytes.Equal(slotValue, proof.Value.Bytes()) {
|
t.Fatalf("invalid storage proof key, want: %q, got: %q", testSlot.String(), proof.Key)
|
||||||
t.Fatalf("invalid storage proof value, want: %v, got: %v", slotValue, proof.Value.Bytes())
|
}
|
||||||
|
slotValue, _ := ethcl.StorageAt(context.Background(), addr, common.HexToHash(proof.Key), nil)
|
||||||
|
if have, want := common.BigToHash(proof.Value), common.BytesToHash(slotValue); have != want {
|
||||||
|
t.Fatalf("addr %x, invalid storage proof value: have: %v, want: %v", addr, have, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if proof.Key != testSlot.String() {
|
// test code
|
||||||
t.Fatalf("invalid storage proof key, want: %q, got: %q", testSlot.String(), proof.Key)
|
code, _ := ethcl.CodeAt(context.Background(), addr, nil)
|
||||||
|
if have, want := result.CodeHash, crypto.Keccak256Hash(code); have != want {
|
||||||
|
t.Fatalf("codehash wrong, have %v want %v ", have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,12 +76,18 @@ type backend interface {
|
||||||
// reporting to ethstats
|
// reporting to ethstats
|
||||||
type fullNodeBackend interface {
|
type fullNodeBackend interface {
|
||||||
backend
|
backend
|
||||||
Miner() *miner.Miner
|
|
||||||
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
||||||
CurrentBlock() *types.Block
|
CurrentBlock() *types.Header
|
||||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// miningNodeBackend encompasses the functionality necessary for a mining node
|
||||||
|
// reporting to ethstats
|
||||||
|
type miningNodeBackend interface {
|
||||||
|
fullNodeBackend
|
||||||
|
Miner() *miner.Miner
|
||||||
|
}
|
||||||
|
|
||||||
// Service implements an Ethereum netstats reporting daemon that pushes local
|
// Service implements an Ethereum netstats reporting daemon that pushes local
|
||||||
// chain statistics up to a monitoring server.
|
// chain statistics up to a monitoring server.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
|
@ -634,7 +640,8 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats {
|
||||||
fullBackend, ok := s.backend.(fullNodeBackend)
|
fullBackend, ok := s.backend.(fullNodeBackend)
|
||||||
if ok {
|
if ok {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
block = fullBackend.CurrentBlock()
|
head := fullBackend.CurrentBlock()
|
||||||
|
block, _ = fullBackend.BlockByNumber(context.Background(), rpc.BlockNumber(head.Number.Uint64()))
|
||||||
}
|
}
|
||||||
header = block.Header()
|
header = block.Header()
|
||||||
td = fullBackend.GetTd(context.Background(), header.Hash())
|
td = fullBackend.GetTd(context.Background(), header.Hash())
|
||||||
|
|
@ -779,10 +786,11 @@ func (s *Service) reportStats(conn *connWrapper) error {
|
||||||
gasprice int
|
gasprice int
|
||||||
)
|
)
|
||||||
// check if backend is a full node
|
// check if backend is a full node
|
||||||
fullBackend, ok := s.backend.(fullNodeBackend)
|
if fullBackend, ok := s.backend.(fullNodeBackend); ok {
|
||||||
if ok {
|
if miningBackend, ok := s.backend.(miningNodeBackend); ok {
|
||||||
mining = fullBackend.Miner().Mining()
|
mining = miningBackend.Miner().Mining()
|
||||||
hashrate = int(fullBackend.Miner().Hashrate())
|
hashrate = int(miningBackend.Miner().Hashrate())
|
||||||
|
}
|
||||||
|
|
||||||
sync := fullBackend.SyncProgress()
|
sync := fullBackend.SyncProgress()
|
||||||
syncing = fullBackend.CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
syncing = fullBackend.CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ func ResubscribeErr(backoffMax time.Duration, fn ResubscribeErrFunc) Subscriptio
|
||||||
backoffMax: backoffMax,
|
backoffMax: backoffMax,
|
||||||
fn: fn,
|
fn: fn,
|
||||||
err: make(chan error),
|
err: make(chan error),
|
||||||
unsub: make(chan struct{}),
|
unsub: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
go s.loop()
|
go s.loop()
|
||||||
return s
|
return s
|
||||||
|
|
|
||||||
|
|
@ -154,3 +154,27 @@ func TestResubscribeWithErrorHandler(t *testing.T) {
|
||||||
t.Fatalf("unexpected subscription errors %v, want %v", subErrs, expectedSubErrs)
|
t.Fatalf("unexpected subscription errors %v, want %v", subErrs, expectedSubErrs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResubscribeWithCompletedSubscription(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
quitProducerAck := make(chan struct{})
|
||||||
|
quitProducer := make(chan struct{})
|
||||||
|
|
||||||
|
sub := ResubscribeErr(100*time.Millisecond, func(ctx context.Context, lastErr error) (Subscription, error) {
|
||||||
|
return NewSubscription(func(unsubscribed <-chan struct{}) error {
|
||||||
|
select {
|
||||||
|
case <-quitProducer:
|
||||||
|
quitProducerAck <- struct{}{}
|
||||||
|
return nil
|
||||||
|
case <-unsubscribed:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure producer has started and exited before Unsubscribe
|
||||||
|
close(quitProducer)
|
||||||
|
<-quitProducerAck
|
||||||
|
sub.Unsubscribe()
|
||||||
|
}
|
||||||
|
|
|
||||||
4
go.mod
4
go.mod
|
|
@ -16,12 +16,12 @@ require (
|
||||||
github.com/cockroachdb/errors v1.8.1
|
github.com/cockroachdb/errors v1.8.1
|
||||||
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
|
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
|
||||||
github.com/consensys/gnark-crypto v0.12.1
|
github.com/consensys/gnark-crypto v0.12.1
|
||||||
github.com/crate-crypto/go-kzg-4844 v0.3.0
|
github.com/crate-crypto/go-kzg-4844 v0.7.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0
|
github.com/deckarep/golang-set/v2 v2.1.0
|
||||||
github.com/docker/docker v24.0.5+incompatible
|
github.com/docker/docker v24.0.5+incompatible
|
||||||
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127
|
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127
|
||||||
github.com/ethereum/c-kzg-4844 v0.3.1
|
github.com/ethereum/c-kzg-4844 v0.4.0
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e
|
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e
|
||||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
|
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
|
||||||
|
|
|
||||||
8
go.sum
8
go.sum
|
|
@ -147,8 +147,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 h1:DuBDHVjgGMPki7bAyh91+3cF1Vh34sAEdH8JQgbc2R0=
|
github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 h1:DuBDHVjgGMPki7bAyh91+3cF1Vh34sAEdH8JQgbc2R0=
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI=
|
github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI=
|
||||||
github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A=
|
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
|
||||||
github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4=
|
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
|
@ -183,8 +183,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
||||||
github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg=
|
github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY=
|
||||||
github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
||||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash")
|
errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash")
|
||||||
|
errInvalidBlockRange = errors.New("invalid from and to block combination: from > to")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Long int64
|
type Long int64
|
||||||
|
|
@ -1333,7 +1334,7 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
|
||||||
to = rpc.BlockNumber(r.backend.CurrentBlock().Number.Int64())
|
to = rpc.BlockNumber(r.backend.CurrentBlock().Number.Int64())
|
||||||
}
|
}
|
||||||
if to < from {
|
if to < from {
|
||||||
return []*Block{}, nil
|
return nil, errInvalidBlockRange
|
||||||
}
|
}
|
||||||
var ret []*Block
|
var ret []*Block
|
||||||
for i := from; i <= to; i++ {
|
for i := from; i <= to; i++ {
|
||||||
|
|
|
||||||
|
|
@ -675,10 +675,6 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
||||||
keys = make([]common.Hash, len(storageKeys))
|
keys = make([]common.Hash, len(storageKeys))
|
||||||
keyLengths = make([]int, len(storageKeys))
|
keyLengths = make([]int, len(storageKeys))
|
||||||
storageProof = make([]StorageResult, len(storageKeys))
|
storageProof = make([]StorageResult, len(storageKeys))
|
||||||
|
|
||||||
storageTrie state.Trie
|
|
||||||
storageHash = types.EmptyRootHash
|
|
||||||
codeHash = types.EmptyCodeHash
|
|
||||||
)
|
)
|
||||||
// Deserialize all keys. This prevents state access on invalid input.
|
// Deserialize all keys. This prevents state access on invalid input.
|
||||||
for i, hexKey := range storageKeys {
|
for i, hexKey := range storageKeys {
|
||||||
|
|
@ -688,51 +684,49 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
statedb, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if statedb == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if storageRoot := state.GetStorageRoot(address); storageRoot != types.EmptyRootHash && storageRoot != (common.Hash{}) {
|
codeHash := statedb.GetCodeHash(address)
|
||||||
id := trie.StorageTrieID(header.Root, crypto.Keccak256Hash(address.Bytes()), storageRoot)
|
storageRoot := statedb.GetStorageRoot(address)
|
||||||
tr, err := trie.NewStateTrie(id, state.Database().TrieDB())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
storageTrie = tr
|
|
||||||
}
|
|
||||||
// If we have a storageTrie, the account exists and we must update
|
|
||||||
// the storage root hash and the code hash.
|
|
||||||
if storageTrie != nil {
|
|
||||||
storageHash = storageTrie.Hash()
|
|
||||||
codeHash = state.GetCodeHash(address)
|
|
||||||
}
|
|
||||||
// Create the proofs for the storageKeys.
|
|
||||||
for i, key := range keys {
|
|
||||||
// Output key encoding is a bit special: if the input was a 32-byte hash, it is
|
|
||||||
// returned as such. Otherwise, we apply the QUANTITY encoding mandated by the
|
|
||||||
// JSON-RPC spec for getProof. This behavior exists to preserve backwards
|
|
||||||
// compatibility with older client versions.
|
|
||||||
var outputKey string
|
|
||||||
if keyLengths[i] != 32 {
|
|
||||||
outputKey = hexutil.EncodeBig(key.Big())
|
|
||||||
} else {
|
|
||||||
outputKey = hexutil.Encode(key[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
if storageTrie == nil {
|
if len(keys) > 0 {
|
||||||
storageProof[i] = StorageResult{outputKey, &hexutil.Big{}, []string{}}
|
var storageTrie state.Trie
|
||||||
continue
|
if storageRoot != types.EmptyRootHash && storageRoot != (common.Hash{}) {
|
||||||
|
id := trie.StorageTrieID(header.Root, crypto.Keccak256Hash(address.Bytes()), storageRoot)
|
||||||
|
st, err := trie.NewStateTrie(id, statedb.Database().TrieDB())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
storageTrie = st
|
||||||
}
|
}
|
||||||
var proof proofList
|
// Create the proofs for the storageKeys.
|
||||||
if err := storageTrie.Prove(crypto.Keccak256(key.Bytes()), &proof); err != nil {
|
for i, key := range keys {
|
||||||
return nil, err
|
// Output key encoding is a bit special: if the input was a 32-byte hash, it is
|
||||||
|
// returned as such. Otherwise, we apply the QUANTITY encoding mandated by the
|
||||||
|
// JSON-RPC spec for getProof. This behavior exists to preserve backwards
|
||||||
|
// compatibility with older client versions.
|
||||||
|
var outputKey string
|
||||||
|
if keyLengths[i] != 32 {
|
||||||
|
outputKey = hexutil.EncodeBig(key.Big())
|
||||||
|
} else {
|
||||||
|
outputKey = hexutil.Encode(key[:])
|
||||||
|
}
|
||||||
|
if storageTrie == nil {
|
||||||
|
storageProof[i] = StorageResult{outputKey, &hexutil.Big{}, []string{}}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var proof proofList
|
||||||
|
if err := storageTrie.Prove(crypto.Keccak256(key.Bytes()), &proof); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
value := (*hexutil.Big)(statedb.GetState(address, key).Big())
|
||||||
|
storageProof[i] = StorageResult{outputKey, value, proof}
|
||||||
}
|
}
|
||||||
value := (*hexutil.Big)(state.GetState(address, key).Big())
|
|
||||||
storageProof[i] = StorageResult{outputKey, value, proof}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the accountProof.
|
// Create the accountProof.
|
||||||
tr, err := trie.NewStateTrie(trie.StateTrieID(header.Root), state.Database().TrieDB())
|
tr, err := trie.NewStateTrie(trie.StateTrieID(header.Root), statedb.Database().TrieDB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -743,12 +737,12 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
||||||
return &AccountResult{
|
return &AccountResult{
|
||||||
Address: address,
|
Address: address,
|
||||||
AccountProof: accountProof,
|
AccountProof: accountProof,
|
||||||
Balance: (*hexutil.Big)(state.GetBalance(address)),
|
Balance: (*hexutil.Big)(statedb.GetBalance(address)),
|
||||||
CodeHash: codeHash,
|
CodeHash: codeHash,
|
||||||
Nonce: hexutil.Uint64(state.GetNonce(address)),
|
Nonce: hexutil.Uint64(statedb.GetNonce(address)),
|
||||||
StorageHash: storageHash,
|
StorageHash: storageRoot,
|
||||||
StorageProof: storageProof,
|
StorageProof: storageProof,
|
||||||
}, state.Error()
|
}, statedb.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeHash parses a hex-encoded 32-byte hash. The input may optionally
|
// decodeHash parses a hex-encoded 32-byte hash. The input may optionally
|
||||||
|
|
@ -1277,7 +1271,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if failed {
|
if failed {
|
||||||
if result != nil && result.Err != vm.ErrOutOfGas {
|
if result != nil && !errors.Is(result.Err, vm.ErrOutOfGas) {
|
||||||
if len(result.Revert()) > 0 {
|
if len(result.Revert()) > 0 {
|
||||||
return 0, newRevertError(result)
|
return 0, newRevertError(result)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import (
|
||||||
|
|
||||||
// BuildPayloadArgs contains the provided parameters for building payload.
|
// BuildPayloadArgs contains the provided parameters for building payload.
|
||||||
// Check engine-api specification for more details.
|
// Check engine-api specification for more details.
|
||||||
// https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1
|
// https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#payloadattributesv3
|
||||||
type BuildPayloadArgs struct {
|
type BuildPayloadArgs struct {
|
||||||
Parent common.Hash // The parent block to build payload on top
|
Parent common.Hash // The parent block to build payload on top
|
||||||
Timestamp uint64 // The provided timestamp of generated payload
|
Timestamp uint64 // The provided timestamp of generated payload
|
||||||
|
|
|
||||||
56
oss-fuzz.sh
56
oss-fuzz.sh
|
|
@ -1,5 +1,5 @@
|
||||||
#/bin/bash -eu
|
#!/bin/bash -eu
|
||||||
# Copyright 2020 Google Inc.
|
# Copyright 2022 Google LLC
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
@ -15,17 +15,6 @@
|
||||||
#
|
#
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
# This file is for integration with Google OSS-Fuzz.
|
|
||||||
# The following ENV variables are available when executing on OSS-fuzz:
|
|
||||||
#
|
|
||||||
# /out/ $OUT Directory to store build artifacts (fuzz targets, dictionaries, options files, seed corpus archives).
|
|
||||||
# /src/ $SRC Directory to checkout source files.
|
|
||||||
# /work/ $WORK Directory to store intermediate files.
|
|
||||||
#
|
|
||||||
# $CC, $CXX, $CCC The C and C++ compiler binaries.
|
|
||||||
# $CFLAGS, $CXXFLAGS C and C++ compiler flags.
|
|
||||||
# $LIB_FUZZING_ENGINE C++ compiler argument to link fuzz target against the prebuilt engine library (e.g. libFuzzer).
|
|
||||||
|
|
||||||
# This sets the -coverpgk for the coverage report when the corpus is executed through go test
|
# This sets the -coverpgk for the coverage report when the corpus is executed through go test
|
||||||
coverpkg="github.com/ethereum/go-ethereum/..."
|
coverpkg="github.com/ethereum/go-ethereum/..."
|
||||||
|
|
||||||
|
|
@ -59,25 +48,38 @@ DOG
|
||||||
cd -
|
cd -
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile_fuzzer {
|
function build_native_go_fuzzer() {
|
||||||
# Inputs:
|
fuzzer=$1
|
||||||
# $1: The package to fuzz, within go-ethereum
|
function=$2
|
||||||
# $2: The name of the fuzzing function
|
path=$3
|
||||||
# $3: The name to give to the final fuzzing-binary
|
tags="-tags gofuzz"
|
||||||
|
|
||||||
|
if [[ $SANITIZER == *coverage* ]]; then
|
||||||
|
coverbuild $path $function $fuzzer $coverpkg
|
||||||
|
else
|
||||||
|
go-118-fuzz-build $tags -o $fuzzer.a -func $function $path
|
||||||
|
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function compile_fuzzer() {
|
||||||
path=$GOPATH/src/github.com/ethereum/go-ethereum/$1
|
path=$GOPATH/src/github.com/ethereum/go-ethereum/$1
|
||||||
func=$2
|
function=$2
|
||||||
fuzzer=$3
|
fuzzer=$3
|
||||||
|
|
||||||
echo "Building $fuzzer"
|
echo "Building $fuzzer"
|
||||||
|
cd $path
|
||||||
|
|
||||||
# Do a coverage-build or a regular build
|
# Install build dependencies
|
||||||
if [[ $SANITIZER = *coverage* ]]; then
|
go install github.com/AdamKorcz/go-118-fuzz-build@latest
|
||||||
coverbuild $path $func $fuzzer $coverpkg
|
go get github.com/AdamKorcz/go-118-fuzz-build/testing
|
||||||
|
|
||||||
|
# Test if file contains a line with "func $function(" and "testing.F".
|
||||||
|
if [ $(grep -r "func $function(" $path | grep "testing.F" | wc -l) -eq 1 ]
|
||||||
|
then
|
||||||
|
build_native_go_fuzzer $fuzzer $function $path
|
||||||
else
|
else
|
||||||
(cd $path && \
|
echo "Could not find the function: func ${function}(f *testing.F)"
|
||||||
go-fuzz -func $func -o $WORK/$fuzzer.a . && \
|
|
||||||
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE $WORK/$fuzzer.a -o $OUT/$fuzzer)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## Check if there exists a seed corpus file
|
## Check if there exists a seed corpus file
|
||||||
|
|
@ -87,9 +89,11 @@ function compile_fuzzer {
|
||||||
cp $corpusfile $OUT/
|
cp $corpusfile $OUT/
|
||||||
echo "Found seed corpus: $corpusfile"
|
echo "Found seed corpus: $corpusfile"
|
||||||
fi
|
fi
|
||||||
|
cd -
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_fuzzer tests/fuzzers/bitutil Fuzz fuzzBitutilCompress
|
compile_fuzzer tests/fuzzers/bitutil FuzzEncoder fuzzBitutilEncoder
|
||||||
|
compile_fuzzer tests/fuzzers/bitutil FuzzDecoder fuzzBitutilDecoder
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add
|
compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul
|
compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair
|
compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 1 // Major version component of the current release
|
VersionMajor = 1 // Major version component of the current release
|
||||||
VersionMinor = 13 // Minor version component of the current release
|
VersionMinor = 13 // Minor version component of the current release
|
||||||
VersionPatch = 4 // Patch version component of the current release
|
VersionPatch = 5 // Patch version component of the current release
|
||||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ func TestClientWebsocketPing(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
sendPing = make(chan struct{})
|
sendPing = make(chan struct{})
|
||||||
server = wsPingTestServer(t, sendPing)
|
server = wsPingTestServer(t, sendPing)
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
|
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
defer server.Shutdown(ctx)
|
defer server.Shutdown(ctx)
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,6 @@ func TestExecutionSpec(t *testing.T) {
|
||||||
}
|
}
|
||||||
bt := new(testMatcher)
|
bt := new(testMatcher)
|
||||||
|
|
||||||
// cancun tests are not complete yet
|
|
||||||
bt.skipLoad(`^cancun/`)
|
|
||||||
bt.skipLoad(`-fork=Cancun`)
|
|
||||||
|
|
||||||
bt.walk(t, executionSpecDir, func(t *testing.T, name string, test *BlockTest) {
|
bt.walk(t, executionSpecDir, func(t *testing.T, name string, test *BlockTest) {
|
||||||
execBlockTest(t, bt, test)
|
execBlockTest(t, bt, test)
|
||||||
})
|
})
|
||||||
|
|
@ -75,14 +71,18 @@ func TestExecutionSpec(t *testing.T) {
|
||||||
func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
|
func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
|
||||||
if err := bt.checkFailure(t, test.Run(false, rawdb.HashScheme, nil)); err != nil {
|
if err := bt.checkFailure(t, test.Run(false, rawdb.HashScheme, nil)); err != nil {
|
||||||
t.Errorf("test in hash mode without snapshotter failed: %v", err)
|
t.Errorf("test in hash mode without snapshotter failed: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if err := bt.checkFailure(t, test.Run(true, rawdb.HashScheme, nil)); err != nil {
|
if err := bt.checkFailure(t, test.Run(true, rawdb.HashScheme, nil)); err != nil {
|
||||||
t.Errorf("test in hash mode with snapshotter failed: %v", err)
|
t.Errorf("test in hash mode with snapshotter failed: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if err := bt.checkFailure(t, test.Run(false, rawdb.PathScheme, nil)); err != nil {
|
if err := bt.checkFailure(t, test.Run(false, rawdb.PathScheme, nil)); err != nil {
|
||||||
t.Errorf("test in path mode without snapshotter failed: %v", err)
|
t.Errorf("test in path mode without snapshotter failed: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if err := bt.checkFailure(t, test.Run(true, rawdb.PathScheme, nil)); err != nil {
|
if err := bt.checkFailure(t, test.Run(true, rawdb.PathScheme, nil)); err != nil {
|
||||||
t.Errorf("test in path mode with snapshotter failed: %v", err)
|
t.Errorf("test in path mode with snapshotter failed: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
// Copyright 2020 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 abi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
fuzz "github.com/google/gofuzz"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
|
|
||||||
stateMut = []string{"", "pure", "view", "payable"}
|
|
||||||
stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
|
|
||||||
pays = []string{"", "true", "false"}
|
|
||||||
payables = []*string{&pays[0], &pays[1]}
|
|
||||||
vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
|
|
||||||
varNames = append(vNames, names...)
|
|
||||||
varTypes = []string{"bool", "address", "bytes", "string",
|
|
||||||
"uint8", "int8", "uint8", "int8", "uint16", "int16",
|
|
||||||
"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
|
|
||||||
"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
|
|
||||||
"uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136",
|
|
||||||
"uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176",
|
|
||||||
"uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216",
|
|
||||||
"uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256",
|
|
||||||
"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11",
|
|
||||||
"bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21",
|
|
||||||
"bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31",
|
|
||||||
"bytes32", "bytes"}
|
|
||||||
)
|
|
||||||
|
|
||||||
func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool) {
|
|
||||||
if out, err := abi.Unpack(method, input); err == nil {
|
|
||||||
_, err := abi.Pack(method, out...)
|
|
||||||
if err != nil {
|
|
||||||
// We have some false positives as we can unpack these type successfully, but not pack them
|
|
||||||
if err.Error() == "abi: cannot use []uint8 as type [0]int8 as argument" ||
|
|
||||||
err.Error() == "abi: cannot use uint8 as type int8 as argument" {
|
|
||||||
return out, false
|
|
||||||
}
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return out, true
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
|
|
||||||
if packed, err := abi.Pack(method, input); err == nil {
|
|
||||||
outptr := reflect.New(reflect.TypeOf(input))
|
|
||||||
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
out := outptr.Elem().Interface()
|
|
||||||
if !reflect.DeepEqual(input, out) {
|
|
||||||
panic(fmt.Sprintf("unpackPack is not equal, \ninput : %x\noutput: %x", input, out))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
name string
|
|
||||||
typ string
|
|
||||||
}
|
|
||||||
|
|
||||||
func createABI(name string, stateMutability, payable *string, inputs []args) (abi.ABI, error) {
|
|
||||||
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
|
|
||||||
if stateMutability != nil {
|
|
||||||
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
|
||||||
}
|
|
||||||
if payable != nil {
|
|
||||||
sig += fmt.Sprintf(`, "payable": %v `, *payable)
|
|
||||||
}
|
|
||||||
if len(inputs) > 0 {
|
|
||||||
sig += `, "inputs" : [ {`
|
|
||||||
for i, inp := range inputs {
|
|
||||||
sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
|
|
||||||
if i+1 < len(inputs) {
|
|
||||||
sig += ","
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sig += "} ]"
|
|
||||||
sig += `, "outputs" : [ {`
|
|
||||||
for i, inp := range inputs {
|
|
||||||
sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
|
|
||||||
if i+1 < len(inputs) {
|
|
||||||
sig += ","
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sig += "} ]"
|
|
||||||
}
|
|
||||||
sig += `}]`
|
|
||||||
|
|
||||||
return abi.JSON(strings.NewReader(sig))
|
|
||||||
}
|
|
||||||
|
|
||||||
func runFuzzer(input []byte) int {
|
|
||||||
good := false
|
|
||||||
fuzzer := fuzz.NewFromGoFuzz(input)
|
|
||||||
|
|
||||||
name := names[getUInt(fuzzer)%len(names)]
|
|
||||||
stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
|
|
||||||
payable := payables[getUInt(fuzzer)%len(payables)]
|
|
||||||
maxLen := 5
|
|
||||||
for k := 1; k < maxLen; k++ {
|
|
||||||
var arg []args
|
|
||||||
for i := k; i > 0; i-- {
|
|
||||||
argName := varNames[i]
|
|
||||||
argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
|
|
||||||
if getUInt(fuzzer)%10 == 0 {
|
|
||||||
argTyp += "[]"
|
|
||||||
} else if getUInt(fuzzer)%10 == 0 {
|
|
||||||
arrayArgs := getUInt(fuzzer)%30 + 1
|
|
||||||
argTyp += fmt.Sprintf("[%d]", arrayArgs)
|
|
||||||
}
|
|
||||||
arg = append(arg, args{
|
|
||||||
name: argName,
|
|
||||||
typ: argTyp,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
abi, err := createABI(name, stateM, payable, arg)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
structs, b := unpackPack(abi, name, input)
|
|
||||||
c := packUnpack(abi, name, &structs)
|
|
||||||
good = good || b || c
|
|
||||||
}
|
|
||||||
if good {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func Fuzz(input []byte) int {
|
|
||||||
return runFuzzer(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUInt(fuzzer *fuzz.Fuzzer) int {
|
|
||||||
var i int
|
|
||||||
fuzzer.Fuzz(&i)
|
|
||||||
if i < 0 {
|
|
||||||
i = -i
|
|
||||||
if i < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
@ -17,7 +17,13 @@
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
fuzz "github.com/google/gofuzz"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestReplicate can be used to replicate crashers from the fuzzing tests.
|
// TestReplicate can be used to replicate crashers from the fuzzing tests.
|
||||||
|
|
@ -25,19 +31,151 @@ import (
|
||||||
func TestReplicate(t *testing.T) {
|
func TestReplicate(t *testing.T) {
|
||||||
testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"
|
testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"
|
||||||
data := []byte(testString)
|
data := []byte(testString)
|
||||||
runFuzzer(data)
|
fuzzAbi(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGenerateCorpus can be used to add corpus for the fuzzer.
|
func Fuzz(f *testing.F) {
|
||||||
// Just replace corpusHex with the hexEncoded output you want to add to the fuzzer.
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
func TestGenerateCorpus(t *testing.T) {
|
fuzzAbi(data)
|
||||||
/*
|
})
|
||||||
corpusHex := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
}
|
||||||
data := common.FromHex(corpusHex)
|
|
||||||
checksum := sha1.Sum(data)
|
var (
|
||||||
outf := fmt.Sprintf("corpus/%x", checksum)
|
names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
|
||||||
if err := os.WriteFile(outf, data, 0777); err != nil {
|
stateMut = []string{"", "pure", "view", "payable"}
|
||||||
|
stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
|
||||||
|
pays = []string{"", "true", "false"}
|
||||||
|
payables = []*string{&pays[0], &pays[1]}
|
||||||
|
vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||||
|
varNames = append(vNames, names...)
|
||||||
|
varTypes = []string{"bool", "address", "bytes", "string",
|
||||||
|
"uint8", "int8", "uint8", "int8", "uint16", "int16",
|
||||||
|
"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
|
||||||
|
"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
|
||||||
|
"uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136",
|
||||||
|
"uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176",
|
||||||
|
"uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216",
|
||||||
|
"uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256",
|
||||||
|
"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11",
|
||||||
|
"bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21",
|
||||||
|
"bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31",
|
||||||
|
"bytes32", "bytes"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool) {
|
||||||
|
if out, err := abi.Unpack(method, input); err == nil {
|
||||||
|
_, err := abi.Pack(method, out...)
|
||||||
|
if err != nil {
|
||||||
|
// We have some false positives as we can unpack these type successfully, but not pack them
|
||||||
|
if err.Error() == "abi: cannot use []uint8 as type [0]int8 as argument" ||
|
||||||
|
err.Error() == "abi: cannot use uint8 as type int8 as argument" {
|
||||||
|
return out, false
|
||||||
|
}
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
*/
|
return out, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
|
||||||
|
if packed, err := abi.Pack(method, input); err == nil {
|
||||||
|
outptr := reflect.New(reflect.TypeOf(input))
|
||||||
|
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
out := outptr.Elem().Interface()
|
||||||
|
if !reflect.DeepEqual(input, out) {
|
||||||
|
panic(fmt.Sprintf("unpackPack is not equal, \ninput : %x\noutput: %x", input, out))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
name string
|
||||||
|
typ string
|
||||||
|
}
|
||||||
|
|
||||||
|
func createABI(name string, stateMutability, payable *string, inputs []args) (abi.ABI, error) {
|
||||||
|
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
|
||||||
|
if stateMutability != nil {
|
||||||
|
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
||||||
|
}
|
||||||
|
if payable != nil {
|
||||||
|
sig += fmt.Sprintf(`, "payable": %v `, *payable)
|
||||||
|
}
|
||||||
|
if len(inputs) > 0 {
|
||||||
|
sig += `, "inputs" : [ {`
|
||||||
|
for i, inp := range inputs {
|
||||||
|
sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
|
||||||
|
if i+1 < len(inputs) {
|
||||||
|
sig += ","
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sig += "} ]"
|
||||||
|
sig += `, "outputs" : [ {`
|
||||||
|
for i, inp := range inputs {
|
||||||
|
sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
|
||||||
|
if i+1 < len(inputs) {
|
||||||
|
sig += ","
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sig += "} ]"
|
||||||
|
}
|
||||||
|
sig += `}]`
|
||||||
|
|
||||||
|
return abi.JSON(strings.NewReader(sig))
|
||||||
|
}
|
||||||
|
|
||||||
|
func fuzzAbi(input []byte) int {
|
||||||
|
good := false
|
||||||
|
fuzzer := fuzz.NewFromGoFuzz(input)
|
||||||
|
|
||||||
|
name := names[getUInt(fuzzer)%len(names)]
|
||||||
|
stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
|
||||||
|
payable := payables[getUInt(fuzzer)%len(payables)]
|
||||||
|
maxLen := 5
|
||||||
|
for k := 1; k < maxLen; k++ {
|
||||||
|
var arg []args
|
||||||
|
for i := k; i > 0; i-- {
|
||||||
|
argName := varNames[i]
|
||||||
|
argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
|
||||||
|
if getUInt(fuzzer)%10 == 0 {
|
||||||
|
argTyp += "[]"
|
||||||
|
} else if getUInt(fuzzer)%10 == 0 {
|
||||||
|
arrayArgs := getUInt(fuzzer)%30 + 1
|
||||||
|
argTyp += fmt.Sprintf("[%d]", arrayArgs)
|
||||||
|
}
|
||||||
|
arg = append(arg, args{
|
||||||
|
name: argName,
|
||||||
|
typ: argTyp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
abi, err := createABI(name, stateM, payable, arg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
structs, b := unpackPack(abi, name, input)
|
||||||
|
c := packUnpack(abi, name, &structs)
|
||||||
|
good = good || b || c
|
||||||
|
}
|
||||||
|
if good {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUInt(fuzzer *fuzz.Fuzzer) int {
|
||||||
|
var i int
|
||||||
|
fuzzer.Fuzz(&i)
|
||||||
|
if i < 0 {
|
||||||
|
i = -i
|
||||||
|
if i < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
// Copyright 2023 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -18,38 +18,37 @@ package bitutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/bitutil"
|
"github.com/ethereum/go-ethereum/common/bitutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fuzz implements a go-fuzz fuzzer method to test various encoding method
|
func FuzzEncoder(f *testing.F) {
|
||||||
// invocations.
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
func Fuzz(data []byte) int {
|
fuzzEncode(data)
|
||||||
if len(data) == 0 {
|
})
|
||||||
return 0
|
}
|
||||||
}
|
func FuzzDecoder(f *testing.F) {
|
||||||
if data[0]%2 == 0 {
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
return fuzzEncode(data[1:])
|
fuzzDecode(data)
|
||||||
}
|
})
|
||||||
return fuzzDecode(data[1:])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
|
// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
|
||||||
// decoding algorithm.
|
// decoding algorithm.
|
||||||
func fuzzEncode(data []byte) int {
|
func fuzzEncode(data []byte) {
|
||||||
proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data))
|
proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data))
|
||||||
if !bytes.Equal(data, proc) {
|
if !bytes.Equal(data, proc) {
|
||||||
panic("content mismatch")
|
panic("content mismatch")
|
||||||
}
|
}
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
|
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
|
||||||
// reencoding algorithm.
|
// reencoding algorithm.
|
||||||
func fuzzDecode(data []byte) int {
|
func fuzzDecode(data []byte) {
|
||||||
blob, err := bitutil.DecompressBytes(data, 1024)
|
blob, err := bitutil.DecompressBytes(data, 1024)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return
|
||||||
}
|
}
|
||||||
// re-compress it (it's OK if the re-compressed differs from the
|
// re-compress it (it's OK if the re-compressed differs from the
|
||||||
// original - the first input may not have been compressed at all)
|
// original - the first input may not have been compressed at all)
|
||||||
|
|
@ -66,5 +65,4 @@ func fuzzDecode(data []byte) int {
|
||||||
if !bytes.Equal(decomp, blob) {
|
if !bytes.Equal(decomp, blob) {
|
||||||
panic("content mismatch")
|
panic("content mismatch")
|
||||||
}
|
}
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
@ -14,9 +14,6 @@
|
||||||
// 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/>.
|
||||||
|
|
||||||
//go:build gofuzz
|
|
||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package bls
|
package bls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -35,7 +32,7 @@ import (
|
||||||
blst "github.com/supranational/blst/bindings/go"
|
blst "github.com/supranational/blst/bindings/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FuzzCrossPairing(data []byte) int {
|
func fuzzCrossPairing(data []byte) int {
|
||||||
input := bytes.NewReader(data)
|
input := bytes.NewReader(data)
|
||||||
|
|
||||||
// get random G1 points
|
// get random G1 points
|
||||||
|
|
@ -101,7 +98,7 @@ func massageBLST(in []byte) []byte {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func FuzzCrossG1Add(data []byte) int {
|
func fuzzCrossG1Add(data []byte) int {
|
||||||
input := bytes.NewReader(data)
|
input := bytes.NewReader(data)
|
||||||
|
|
||||||
// get random G1 points
|
// get random G1 points
|
||||||
|
|
@ -139,7 +136,7 @@ func FuzzCrossG1Add(data []byte) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func FuzzCrossG2Add(data []byte) int {
|
func fuzzCrossG2Add(data []byte) int {
|
||||||
input := bytes.NewReader(data)
|
input := bytes.NewReader(data)
|
||||||
|
|
||||||
// get random G2 points
|
// get random G2 points
|
||||||
|
|
@ -177,7 +174,7 @@ func FuzzCrossG2Add(data []byte) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func FuzzCrossG1MultiExp(data []byte) int {
|
func fuzzCrossG1MultiExp(data []byte) int {
|
||||||
var (
|
var (
|
||||||
input = bytes.NewReader(data)
|
input = bytes.NewReader(data)
|
||||||
gethScalars []*big.Int
|
gethScalars []*big.Int
|
||||||
|
|
|
||||||
97
tests/fuzzers/bls12381/bls12381_test.go
Normal file
97
tests/fuzzers/bls12381/bls12381_test.go
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2023 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 bls
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func FuzzCrossPairing(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzzCrossPairing(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzCrossG1Add(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzzCrossG1Add(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzCrossG2Add(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzzCrossG2Add(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzCrossG1MultiExp(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzzCrossG1MultiExp(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzG1Add(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsG1Add, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzG1Mul(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsG1Mul, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzG1MultiExp(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsG1MultiExp, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzG2Add(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsG2Add, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzG2Mul(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsG2Mul, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzG2MultiExp(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsG2MultiExp, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzPairing(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsPairing, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzMapG1(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsMapG1, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzMapG2(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(blsMapG2, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -36,16 +36,6 @@ const (
|
||||||
blsMapG2 = byte(18)
|
blsMapG2 = byte(18)
|
||||||
)
|
)
|
||||||
|
|
||||||
func FuzzG1Add(data []byte) int { return fuzz(blsG1Add, data) }
|
|
||||||
func FuzzG1Mul(data []byte) int { return fuzz(blsG1Mul, data) }
|
|
||||||
func FuzzG1MultiExp(data []byte) int { return fuzz(blsG1MultiExp, data) }
|
|
||||||
func FuzzG2Add(data []byte) int { return fuzz(blsG2Add, data) }
|
|
||||||
func FuzzG2Mul(data []byte) int { return fuzz(blsG2Mul, data) }
|
|
||||||
func FuzzG2MultiExp(data []byte) int { return fuzz(blsG2MultiExp, data) }
|
|
||||||
func FuzzPairing(data []byte) int { return fuzz(blsPairing, data) }
|
|
||||||
func FuzzMapG1(data []byte) int { return fuzz(blsMapG1, data) }
|
|
||||||
func FuzzMapG2(data []byte) int { return fuzz(blsMapG2, data) }
|
|
||||||
|
|
||||||
func checkInput(id byte, inputLen int) bool {
|
func checkInput(id byte, inputLen int) bool {
|
||||||
switch id {
|
switch id {
|
||||||
case blsG1Add:
|
case blsG1Add:
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@
|
||||||
// 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/>.
|
||||||
|
|
||||||
//go:build gofuzz
|
|
||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package bn256
|
package bn256
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -64,8 +61,8 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine)
|
||||||
return xc, xg, xs
|
return xc, xg, xs
|
||||||
}
|
}
|
||||||
|
|
||||||
// FuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries.
|
// fuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries.
|
||||||
func FuzzAdd(data []byte) int {
|
func fuzzAdd(data []byte) int {
|
||||||
input := bytes.NewReader(data)
|
input := bytes.NewReader(data)
|
||||||
xc, xg, xs := getG1Points(input)
|
xc, xg, xs := getG1Points(input)
|
||||||
if xc == nil {
|
if xc == nil {
|
||||||
|
|
@ -97,9 +94,9 @@ func FuzzAdd(data []byte) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// FuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare
|
// fuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare
|
||||||
// libraries.
|
// libraries.
|
||||||
func FuzzMul(data []byte) int {
|
func fuzzMul(data []byte) int {
|
||||||
input := bytes.NewReader(data)
|
input := bytes.NewReader(data)
|
||||||
pc, pg, ps := getG1Points(input)
|
pc, pg, ps := getG1Points(input)
|
||||||
if pc == nil {
|
if pc == nil {
|
||||||
|
|
@ -139,7 +136,7 @@ func FuzzMul(data []byte) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func FuzzPair(data []byte) int {
|
func fuzzPair(data []byte) int {
|
||||||
input := bytes.NewReader(data)
|
input := bytes.NewReader(data)
|
||||||
pc, pg, ps := getG1Points(input)
|
pc, pg, ps := getG1Points(input)
|
||||||
if pc == nil {
|
if pc == nil {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The go-ethereum Authors
|
// Copyright 2023 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -14,25 +14,24 @@
|
||||||
// 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 main
|
package bn256
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/tests/fuzzers/snap"
|
func FuzzAdd(f *testing.F) {
|
||||||
)
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzzAdd(data)
|
||||||
func main() {
|
})
|
||||||
if len(os.Args) != 2 {
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Usage: debug <file>\n")
|
|
||||||
os.Exit(1)
|
func FuzzMul(f *testing.F) {
|
||||||
}
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
crasher := os.Args[1]
|
fuzzMul(data)
|
||||||
data, err := os.ReadFile(crasher)
|
})
|
||||||
if err != nil {
|
}
|
||||||
fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err)
|
|
||||||
os.Exit(1)
|
func FuzzPair(f *testing.F) {
|
||||||
}
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
snap.FuzzTrieNodes(data)
|
fuzzPair(data)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ func (f *fuzzer) readBool() bool {
|
||||||
// - 0 otherwise
|
// - 0 otherwise
|
||||||
//
|
//
|
||||||
// other values are reserved for future use.
|
// other values are reserved for future use.
|
||||||
func Fuzz(data []byte) int {
|
func fuzz(data []byte) int {
|
||||||
f := fuzzer{
|
f := fuzzer{
|
||||||
input: bytes.NewReader(data),
|
input: bytes.NewReader(data),
|
||||||
exhausted: false,
|
exhausted: false,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The go-ethereum Authors
|
// Copyright 2023 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -14,25 +14,12 @@
|
||||||
// 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 main
|
package difficulty
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/tests/fuzzers/stacktrie"
|
func Fuzz(f *testing.F) {
|
||||||
)
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
func main() {
|
})
|
||||||
if len(os.Args) != 2 {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage: debug <file>")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
crasher := os.Args[1]
|
|
||||||
data, err := os.ReadFile(crasher)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
stacktrie.Debug(data)
|
|
||||||
}
|
}
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
ns©›,²Ô
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Fuzz(input []byte) int {
|
func fuzz(input []byte) int {
|
||||||
ks := keystore.NewKeyStore("/tmp/ks", keystore.LightScryptN, keystore.LightScryptP)
|
ks := keystore.NewKeyStore("/tmp/ks", keystore.LightScryptN, keystore.LightScryptP)
|
||||||
|
|
||||||
a, err := ks.NewAccount(string(input))
|
a, err := ks.NewAccount(string(input))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The go-ethereum Authors
|
// Copyright 2023 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -14,25 +14,12 @@
|
||||||
// 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 main
|
package keystore
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/tests/fuzzers/difficulty"
|
func Fuzz(f *testing.F) {
|
||||||
)
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
func main() {
|
})
|
||||||
if len(os.Args) != 2 {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage: debug <file>")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
crasher := os.Args[1]
|
|
||||||
data, err := os.ReadFile(crasher)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
difficulty.Fuzz(data)
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
// Copyright 2020 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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/tests/fuzzers/les"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) != 2 {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage: debug <file>\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "Example\n")
|
|
||||||
fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
crasher := os.Args[1]
|
|
||||||
data, err := os.ReadFile(crasher)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
les.Fuzz(data)
|
|
||||||
}
|
|
||||||
|
|
@ -70,7 +70,7 @@ func makechain() (bc *core.BlockChain, addresses []common.Address, txHashes []co
|
||||||
)
|
)
|
||||||
nonce := uint64(i)
|
nonce := uint64(i)
|
||||||
if i%4 == 0 {
|
if i%4 == 0 {
|
||||||
tx, _ = types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, bankKey)
|
tx, _ = types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(params.GWei), testContractCode), signer, bankKey)
|
||||||
addr = crypto.CreateAddress(bankAddr, nonce)
|
addr = crypto.CreateAddress(bankAddr, nonce)
|
||||||
} else {
|
} else {
|
||||||
addr = common.BigToAddress(big.NewInt(int64(i)))
|
addr = common.BigToAddress(big.NewInt(int64(i)))
|
||||||
|
|
@ -279,7 +279,7 @@ func (f *fuzzer) doFuzz(msgCode uint64, packet interface{}) {
|
||||||
fn(f, peer, func() bool { return true })
|
fn(f, peer, func() bool { return true })
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fuzz(input []byte) int {
|
func fuzz(input []byte) int {
|
||||||
// We expect some large inputs
|
// We expect some large inputs
|
||||||
if len(input) < 100 {
|
if len(input) < 100 {
|
||||||
return -1
|
return -1
|
||||||
|
|
|
||||||
25
tests/fuzzers/les/les_test.go
Normal file
25
tests/fuzzers/les/les_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 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 les
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
// Copyright 2020 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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/tests/fuzzers/rangeproof"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) != 2 {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage: debug <file>\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "Example\n")
|
|
||||||
fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
crasher := os.Args[1]
|
|
||||||
data, err := os.ReadFile(crasher)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
rangeproof.Fuzz(data)
|
|
||||||
}
|
|
||||||
|
|
@ -185,7 +185,7 @@ func (f *fuzzer) fuzz() int {
|
||||||
// - 0 otherwise
|
// - 0 otherwise
|
||||||
//
|
//
|
||||||
// other values are reserved for future use.
|
// other values are reserved for future use.
|
||||||
func Fuzz(input []byte) int {
|
func fuzz(input []byte) int {
|
||||||
if len(input) < 100 {
|
if len(input) < 100 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
tests/fuzzers/rangeproof/rangeproof_test.go
Normal file
25
tests/fuzzers/rangeproof/rangeproof_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 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 rangeproof
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ func decodeEncode(input []byte, val interface{}, i int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fuzz(input []byte) int {
|
func fuzz(input []byte) int {
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
tests/fuzzers/rlp/rlp_test.go
Normal file
25
tests/fuzzers/rlp/rlp_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 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 rlp
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
// Copyright 2023 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -17,20 +17,15 @@
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fuzz is the basic entry point for the go-fuzz tool
|
func Fuzz(f *testing.F) {
|
||||||
//
|
f.Fuzz(func(t *testing.T, code, input []byte) {
|
||||||
// This returns 1 for valid parse:able/runnable code, 0
|
runtime.Execute(code, input, &runtime.Config{
|
||||||
// for invalid opcode.
|
GasLimit: 12000000,
|
||||||
func Fuzz(input []byte) int {
|
})
|
||||||
_, _, err := runtime.Execute(input, input, &runtime.Config{
|
|
||||||
GasLimit: 12000000,
|
|
||||||
})
|
})
|
||||||
// invalid opcode
|
|
||||||
if err != nil && len(err.Error()) > 6 && err.Error()[:7] == "invalid" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,50 +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/>.
|
|
||||||
|
|
||||||
// build +gofuzz
|
|
||||||
|
|
||||||
package secp256k1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
|
||||||
fuzz "github.com/google/gofuzz"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Fuzz(input []byte) int {
|
|
||||||
var (
|
|
||||||
fuzzer = fuzz.NewFromGoFuzz(input)
|
|
||||||
curveA = secp256k1.S256()
|
|
||||||
curveB = btcec.S256()
|
|
||||||
dataP1 []byte
|
|
||||||
dataP2 []byte
|
|
||||||
)
|
|
||||||
// first point
|
|
||||||
fuzzer.Fuzz(&dataP1)
|
|
||||||
x1, y1 := curveB.ScalarBaseMult(dataP1)
|
|
||||||
// second point
|
|
||||||
fuzzer.Fuzz(&dataP2)
|
|
||||||
x2, y2 := curveB.ScalarBaseMult(dataP2)
|
|
||||||
resAX, resAY := curveA.Add(x1, y1, x2, y2)
|
|
||||||
resBX, resBY := curveB.Add(x1, y1, x2, y2)
|
|
||||||
if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 {
|
|
||||||
fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2)
|
|
||||||
panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY))
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
@ -16,9 +16,39 @@
|
||||||
|
|
||||||
package secp256k1
|
package secp256k1
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
|
)
|
||||||
|
|
||||||
func TestFuzzer(t *testing.T) {
|
func TestFuzzer(t *testing.T) {
|
||||||
test := "00000000N0000000/R00000000000000000U0000S0000000mkhP000000000000000U"
|
a, b := "00000000N0000000/R0000000000000000", "0U0000S0000000mkhP000000000000000U"
|
||||||
Fuzz([]byte(test))
|
fuzz([]byte(a), []byte(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, a, b []byte) {
|
||||||
|
fuzz(a, b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func fuzz(dataP1, dataP2 []byte) int {
|
||||||
|
var (
|
||||||
|
curveA = secp256k1.S256()
|
||||||
|
curveB = btcec.S256()
|
||||||
|
)
|
||||||
|
// first point
|
||||||
|
x1, y1 := curveB.ScalarBaseMult(dataP1)
|
||||||
|
// second points
|
||||||
|
x2, y2 := curveB.ScalarBaseMult(dataP2)
|
||||||
|
resAX, resAY := curveA.Add(x1, y1, x2, y2)
|
||||||
|
resBX, resBY := curveB.Add(x1, y1, x2, y2)
|
||||||
|
if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 {
|
||||||
|
fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2)
|
||||||
|
panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,20 +141,3 @@ func doFuzz(input []byte, obj interface{}, code int) int {
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// To run a fuzzer, do
|
|
||||||
// $ CGO_ENABLED=0 go-fuzz-build -func FuzzTrieNodes
|
|
||||||
// $ go-fuzz
|
|
||||||
|
|
||||||
func FuzzARange(input []byte) int {
|
|
||||||
return doFuzz(input, &snap.GetAccountRangePacket{}, snap.GetAccountRangeMsg)
|
|
||||||
}
|
|
||||||
func FuzzSRange(input []byte) int {
|
|
||||||
return doFuzz(input, &snap.GetStorageRangesPacket{}, snap.GetStorageRangesMsg)
|
|
||||||
}
|
|
||||||
func FuzzByteCodes(input []byte) int {
|
|
||||||
return doFuzz(input, &snap.GetByteCodesPacket{}, snap.GetByteCodesMsg)
|
|
||||||
}
|
|
||||||
func FuzzTrieNodes(input []byte) int {
|
|
||||||
return doFuzz(input, &snap.GetTrieNodesPacket{}, snap.GetTrieNodesMsg)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The go-ethereum Authors
|
// Copyright 2023 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -14,30 +14,34 @@
|
||||||
// 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 main
|
package snap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"testing"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||||
"github.com/ethereum/go-ethereum/tests/fuzzers/vflux"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func FuzzARange(f *testing.F) {
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
doFuzz(data, &snap.GetAccountRangePacket{}, snap.GetAccountRangeMsg)
|
||||||
if len(os.Args) != 2 {
|
})
|
||||||
fmt.Fprintf(os.Stderr, "Usage: debug <file>\n")
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Example\n")
|
|
||||||
fmt.Fprintf(os.Stderr, " $ debug ../crashers/4bbef6857c733a87ecf6fd8b9e7238f65eb9862a\n")
|
func FuzzSRange(f *testing.F) {
|
||||||
os.Exit(1)
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
}
|
doFuzz(data, &snap.GetStorageRangesPacket{}, snap.GetStorageRangesMsg)
|
||||||
crasher := os.Args[1]
|
})
|
||||||
data, err := os.ReadFile(crasher)
|
}
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err)
|
func FuzzByteCodes(f *testing.F) {
|
||||||
os.Exit(1)
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
}
|
doFuzz(data, &snap.GetByteCodesPacket{}, snap.GetByteCodesMsg)
|
||||||
vflux.FuzzClientPool(data)
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzTrieNodes(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
doFuzz(data, &snap.GetTrieNodesPacket{}, snap.GetTrieNodesMsg)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +115,7 @@ type kv struct {
|
||||||
// - 0 otherwise
|
// - 0 otherwise
|
||||||
//
|
//
|
||||||
// other values are reserved for future use.
|
// other values are reserved for future use.
|
||||||
func Fuzz(data []byte) int {
|
func fuzz(data []byte) int {
|
||||||
f := fuzzer{
|
f := fuzzer{
|
||||||
input: bytes.NewReader(data),
|
input: bytes.NewReader(data),
|
||||||
exhausted: false,
|
exhausted: false,
|
||||||
|
|
@ -140,9 +140,11 @@ func (f *fuzzer) fuzz() int {
|
||||||
trieA = trie.NewEmpty(dbA)
|
trieA = trie.NewEmpty(dbA)
|
||||||
spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
|
spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
|
||||||
dbB = trie.NewDatabase(rawdb.NewDatabase(spongeB), nil)
|
dbB = trie.NewDatabase(rawdb.NewDatabase(spongeB), nil)
|
||||||
trieB = trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
|
||||||
|
options = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme())
|
rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme())
|
||||||
})
|
})
|
||||||
|
trieB = trie.NewStackTrie(options)
|
||||||
vals []kv
|
vals []kv
|
||||||
useful bool
|
useful bool
|
||||||
maxElements = 10000
|
maxElements = 10000
|
||||||
|
|
@ -204,19 +206,20 @@ func (f *fuzzer) fuzz() int {
|
||||||
|
|
||||||
// Ensure all the nodes are persisted correctly
|
// Ensure all the nodes are persisted correctly
|
||||||
var (
|
var (
|
||||||
nodeset = make(map[string][]byte) // path -> blob
|
nodeset = make(map[string][]byte) // path -> blob
|
||||||
trieC = trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
optionsC = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
if crypto.Keccak256Hash(blob) != hash {
|
if crypto.Keccak256Hash(blob) != hash {
|
||||||
panic("invalid node blob")
|
panic("invalid node blob")
|
||||||
}
|
}
|
||||||
nodeset[string(path)] = common.CopyBytes(blob)
|
nodeset[string(path)] = common.CopyBytes(blob)
|
||||||
})
|
})
|
||||||
|
trieC = trie.NewStackTrie(optionsC)
|
||||||
checked int
|
checked int
|
||||||
)
|
)
|
||||||
for _, kv := range vals {
|
for _, kv := range vals {
|
||||||
trieC.MustUpdate(kv.k, kv.v)
|
trieC.MustUpdate(kv.k, kv.v)
|
||||||
}
|
}
|
||||||
rootC, _ := trieC.Commit()
|
rootC := trieC.Commit()
|
||||||
if rootA != rootC {
|
if rootA != rootC {
|
||||||
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
|
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
tests/fuzzers/stacktrie/trie_test.go
Normal file
25
tests/fuzzers/stacktrie/trie_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 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 stacktrie
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
asdlfkjasf23oiejfasdfadkfqlkjfasdlkfjalwk4jfalsdkfjawlefkjsadlfkjasldkfjwalefkjasdlfkjM
|
|
||||||
|
|
@ -130,7 +130,7 @@ func Generate(input []byte) randTest {
|
||||||
// - 0 otherwise
|
// - 0 otherwise
|
||||||
//
|
//
|
||||||
// other values are reserved for future use.
|
// other values are reserved for future use.
|
||||||
func Fuzz(input []byte) int {
|
func fuzz(input []byte) int {
|
||||||
program := Generate(input)
|
program := Generate(input)
|
||||||
if len(program) == 0 {
|
if len(program) == 0 {
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
25
tests/fuzzers/trie/trie_test.go
Normal file
25
tests/fuzzers/trie/trie_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 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 trie
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fuzz(input []byte) int {
|
func fuzz(input []byte) int {
|
||||||
// Don't generate insanely large test cases, not much value in them
|
// Don't generate insanely large test cases, not much value in them
|
||||||
if len(input) > 16*1024 {
|
if len(input) > 16*1024 {
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
25
tests/fuzzers/txfetcher/txfetcher_test.go
Normal file
25
tests/fuzzers/txfetcher/txfetcher_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 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 txfetcher
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Fuzz(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzz(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -214,7 +214,7 @@ func (f *fuzzer) atomicBalanceOp(balance vfs.AtomicBalanceOperator, id enode.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FuzzClientPool(input []byte) int {
|
func fuzzClientPool(input []byte) int {
|
||||||
if len(input) > 10000 {
|
if len(input) > 10000 {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
tests/fuzzers/vflux/clientpool_test.go
Normal file
25
tests/fuzzers/vflux/clientpool_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 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 vflux
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func FuzzClientPool(f *testing.F) {
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
fuzzClientPool(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -200,6 +200,9 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo
|
||||||
if triedb != nil {
|
if triedb != nil {
|
||||||
triedb.Close()
|
triedb.Close()
|
||||||
}
|
}
|
||||||
|
if snaps != nil {
|
||||||
|
snaps.Release()
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
checkedErr := t.checkError(subtest, err)
|
checkedErr := t.checkError(subtest, err)
|
||||||
if checkedErr != nil {
|
if checkedErr != nil {
|
||||||
|
|
|
||||||
|
|
@ -17,37 +17,74 @@
|
||||||
package trie
|
package trie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"bytes"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrCommitDisabled = errors.New("no database for committing")
|
stPool = sync.Pool{New: func() any { return new(stNode) }}
|
||||||
stPool = sync.Pool{New: func() any { return new(stNode) }}
|
_ = types.TrieHasher((*StackTrie)(nil))
|
||||||
_ = types.TrieHasher((*StackTrie)(nil))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeWriteFunc is used to provide all information of a dirty node for committing
|
// StackTrieOptions contains the configured options for manipulating the stackTrie.
|
||||||
// so that callers can flush nodes into database with desired scheme.
|
type StackTrieOptions struct {
|
||||||
type NodeWriteFunc = func(path []byte, hash common.Hash, blob []byte)
|
Writer func(path []byte, hash common.Hash, blob []byte) // The function to commit the dirty nodes
|
||||||
|
Cleaner func(path []byte) // The function to clean up dangling nodes
|
||||||
|
|
||||||
|
SkipLeftBoundary bool // Flag whether the nodes on the left boundary are skipped for committing
|
||||||
|
SkipRightBoundary bool // Flag whether the nodes on the right boundary are skipped for committing
|
||||||
|
boundaryGauge metrics.Gauge // Gauge to track how many boundary nodes are met
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStackTrieOptions initializes an empty options for stackTrie.
|
||||||
|
func NewStackTrieOptions() *StackTrieOptions { return &StackTrieOptions{} }
|
||||||
|
|
||||||
|
// WithWriter configures trie node writer within the options.
|
||||||
|
func (o *StackTrieOptions) WithWriter(writer func(path []byte, hash common.Hash, blob []byte)) *StackTrieOptions {
|
||||||
|
o.Writer = writer
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCleaner configures the cleaner in the option for removing dangling nodes.
|
||||||
|
func (o *StackTrieOptions) WithCleaner(cleaner func(path []byte)) *StackTrieOptions {
|
||||||
|
o.Cleaner = cleaner
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSkipBoundary configures whether the left and right boundary nodes are
|
||||||
|
// filtered for committing, along with a gauge metrics to track how many
|
||||||
|
// boundary nodes are met.
|
||||||
|
func (o *StackTrieOptions) WithSkipBoundary(skipLeft, skipRight bool, gauge metrics.Gauge) *StackTrieOptions {
|
||||||
|
o.SkipLeftBoundary = skipLeft
|
||||||
|
o.SkipRightBoundary = skipRight
|
||||||
|
o.boundaryGauge = gauge
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
// StackTrie is a trie implementation that expects keys to be inserted
|
// StackTrie is a trie implementation that expects keys to be inserted
|
||||||
// in order. Once it determines that a subtree will no longer be inserted
|
// in order. Once it determines that a subtree will no longer be inserted
|
||||||
// into, it will hash it and free up the memory it uses.
|
// into, it will hash it and free up the memory it uses.
|
||||||
type StackTrie struct {
|
type StackTrie struct {
|
||||||
writeFn NodeWriteFunc // function for committing nodes, can be nil
|
options *StackTrieOptions
|
||||||
root *stNode
|
root *stNode
|
||||||
h *hasher
|
h *hasher
|
||||||
|
|
||||||
|
first []byte // The (hex-encoded without terminator) key of first inserted entry, tracked as left boundary.
|
||||||
|
last []byte // The (hex-encoded without terminator) key of last inserted entry, tracked as right boundary.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStackTrie allocates and initializes an empty trie.
|
// NewStackTrie allocates and initializes an empty trie.
|
||||||
func NewStackTrie(writeFn NodeWriteFunc) *StackTrie {
|
func NewStackTrie(options *StackTrieOptions) *StackTrie {
|
||||||
|
if options == nil {
|
||||||
|
options = NewStackTrieOptions()
|
||||||
|
}
|
||||||
return &StackTrie{
|
return &StackTrie{
|
||||||
writeFn: writeFn,
|
options: options,
|
||||||
root: stPool.Get().(*stNode),
|
root: stPool.Get().(*stNode),
|
||||||
h: newHasher(false),
|
h: newHasher(false),
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +96,18 @@ func (t *StackTrie) Update(key, value []byte) error {
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
panic("deletion not supported")
|
panic("deletion not supported")
|
||||||
}
|
}
|
||||||
t.insert(t.root, k[:len(k)-1], value, nil)
|
k = k[:len(k)-1] // chop the termination flag
|
||||||
|
|
||||||
|
// track the first and last inserted entries.
|
||||||
|
if t.first == nil {
|
||||||
|
t.first = append([]byte{}, k...)
|
||||||
|
}
|
||||||
|
if t.last == nil {
|
||||||
|
t.last = append([]byte{}, k...) // allocate key slice
|
||||||
|
} else {
|
||||||
|
t.last = append(t.last[:0], k...) // reuse key slice
|
||||||
|
}
|
||||||
|
t.insert(t.root, k, value, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,9 +119,12 @@ func (t *StackTrie) MustUpdate(key, value []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset resets the stack trie object to empty state.
|
||||||
func (t *StackTrie) Reset() {
|
func (t *StackTrie) Reset() {
|
||||||
t.writeFn = nil
|
t.options = NewStackTrieOptions()
|
||||||
t.root = stPool.Get().(*stNode)
|
t.root = stPool.Get().(*stNode)
|
||||||
|
t.first = nil
|
||||||
|
t.last = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// stNode represents a node within a StackTrie
|
// stNode represents a node within a StackTrie
|
||||||
|
|
@ -138,7 +189,7 @@ func (n *stNode) getDiffIndex(key []byte) int {
|
||||||
|
|
||||||
// Helper function to that inserts a (key, value) pair into
|
// Helper function to that inserts a (key, value) pair into
|
||||||
// the trie.
|
// the trie.
|
||||||
func (t *StackTrie) insert(st *stNode, key, value []byte, prefix []byte) {
|
func (t *StackTrie) insert(st *stNode, key, value []byte, path []byte) {
|
||||||
switch st.typ {
|
switch st.typ {
|
||||||
case branchNode: /* Branch */
|
case branchNode: /* Branch */
|
||||||
idx := int(key[0])
|
idx := int(key[0])
|
||||||
|
|
@ -147,7 +198,7 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, prefix []byte) {
|
||||||
for i := idx - 1; i >= 0; i-- {
|
for i := idx - 1; i >= 0; i-- {
|
||||||
if st.children[i] != nil {
|
if st.children[i] != nil {
|
||||||
if st.children[i].typ != hashedNode {
|
if st.children[i].typ != hashedNode {
|
||||||
t.hash(st.children[i], append(prefix, byte(i)))
|
t.hash(st.children[i], append(path, byte(i)))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +208,7 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, prefix []byte) {
|
||||||
if st.children[idx] == nil {
|
if st.children[idx] == nil {
|
||||||
st.children[idx] = newLeaf(key[1:], value)
|
st.children[idx] = newLeaf(key[1:], value)
|
||||||
} else {
|
} else {
|
||||||
t.insert(st.children[idx], key[1:], value, append(prefix, key[0]))
|
t.insert(st.children[idx], key[1:], value, append(path, key[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
case extNode: /* Ext */
|
case extNode: /* Ext */
|
||||||
|
|
@ -172,7 +223,7 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, prefix []byte) {
|
||||||
if diffidx == len(st.key) {
|
if diffidx == len(st.key) {
|
||||||
// Ext key and key segment are identical, recurse into
|
// Ext key and key segment are identical, recurse into
|
||||||
// the child node.
|
// the child node.
|
||||||
t.insert(st.children[0], key[diffidx:], value, append(prefix, key[:diffidx]...))
|
t.insert(st.children[0], key[diffidx:], value, append(path, key[:diffidx]...))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Save the original part. Depending if the break is
|
// Save the original part. Depending if the break is
|
||||||
|
|
@ -185,14 +236,14 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, prefix []byte) {
|
||||||
// extension. The path prefix of the newly-inserted
|
// extension. The path prefix of the newly-inserted
|
||||||
// extension should also contain the different byte.
|
// extension should also contain the different byte.
|
||||||
n = newExt(st.key[diffidx+1:], st.children[0])
|
n = newExt(st.key[diffidx+1:], st.children[0])
|
||||||
t.hash(n, append(prefix, st.key[:diffidx+1]...))
|
t.hash(n, append(path, st.key[:diffidx+1]...))
|
||||||
} else {
|
} else {
|
||||||
// Break on the last byte, no need to insert
|
// Break on the last byte, no need to insert
|
||||||
// an extension node: reuse the current node.
|
// an extension node: reuse the current node.
|
||||||
// The path prefix of the original part should
|
// The path prefix of the original part should
|
||||||
// still be same.
|
// still be same.
|
||||||
n = st.children[0]
|
n = st.children[0]
|
||||||
t.hash(n, append(prefix, st.key...))
|
t.hash(n, append(path, st.key...))
|
||||||
}
|
}
|
||||||
var p *stNode
|
var p *stNode
|
||||||
if diffidx == 0 {
|
if diffidx == 0 {
|
||||||
|
|
@ -257,7 +308,7 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, prefix []byte) {
|
||||||
// is hashed directly in order to free up some memory.
|
// is hashed directly in order to free up some memory.
|
||||||
origIdx := st.key[diffidx]
|
origIdx := st.key[diffidx]
|
||||||
p.children[origIdx] = newLeaf(st.key[diffidx+1:], st.val)
|
p.children[origIdx] = newLeaf(st.key[diffidx+1:], st.val)
|
||||||
t.hash(p.children[origIdx], append(prefix, st.key[:diffidx+1]...))
|
t.hash(p.children[origIdx], append(path, st.key[:diffidx+1]...))
|
||||||
|
|
||||||
newIdx := key[diffidx]
|
newIdx := key[diffidx]
|
||||||
p.children[newIdx] = newLeaf(key[diffidx+1:], value)
|
p.children[newIdx] = newLeaf(key[diffidx+1:], value)
|
||||||
|
|
@ -292,9 +343,10 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, prefix []byte) {
|
||||||
//
|
//
|
||||||
// This method also sets 'st.type' to hashedNode, and clears 'st.key'.
|
// This method also sets 'st.type' to hashedNode, and clears 'st.key'.
|
||||||
func (t *StackTrie) hash(st *stNode, path []byte) {
|
func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
// The switch below sets this to the RLP-encoding of this node.
|
var (
|
||||||
var encodedNode []byte
|
blob []byte // RLP-encoded node blob
|
||||||
|
internal [][]byte // List of node paths covered by the extension node
|
||||||
|
)
|
||||||
switch st.typ {
|
switch st.typ {
|
||||||
case hashedNode:
|
case hashedNode:
|
||||||
return
|
return
|
||||||
|
|
@ -323,11 +375,22 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
stPool.Put(child.reset()) // Release child back to pool.
|
stPool.Put(child.reset()) // Release child back to pool.
|
||||||
}
|
}
|
||||||
nodes.encode(t.h.encbuf)
|
nodes.encode(t.h.encbuf)
|
||||||
encodedNode = t.h.encodedBytes()
|
blob = t.h.encodedBytes()
|
||||||
|
|
||||||
case extNode:
|
case extNode:
|
||||||
|
// recursively hash and commit child as the first step
|
||||||
t.hash(st.children[0], append(path, st.key...))
|
t.hash(st.children[0], append(path, st.key...))
|
||||||
|
|
||||||
|
// Collect the path of internal nodes between shortNode and its **in disk**
|
||||||
|
// child. This is essential in the case of path mode scheme to avoid leaving
|
||||||
|
// danging nodes within the range of this internal path on disk, which would
|
||||||
|
// break the guarantee for state healing.
|
||||||
|
if len(st.children[0].val) >= 32 && t.options.Cleaner != nil {
|
||||||
|
for i := 1; i < len(st.key); i++ {
|
||||||
|
internal = append(internal, append(path, st.key[:i]...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// encode the extension node
|
||||||
n := shortNode{Key: hexToCompactInPlace(st.key)}
|
n := shortNode{Key: hexToCompactInPlace(st.key)}
|
||||||
if len(st.children[0].val) < 32 {
|
if len(st.children[0].val) < 32 {
|
||||||
n.Val = rawNode(st.children[0].val)
|
n.Val = rawNode(st.children[0].val)
|
||||||
|
|
@ -335,7 +398,7 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
n.Val = hashNode(st.children[0].val)
|
n.Val = hashNode(st.children[0].val)
|
||||||
}
|
}
|
||||||
n.encode(t.h.encbuf)
|
n.encode(t.h.encbuf)
|
||||||
encodedNode = t.h.encodedBytes()
|
blob = t.h.encodedBytes()
|
||||||
|
|
||||||
stPool.Put(st.children[0].reset()) // Release child back to pool.
|
stPool.Put(st.children[0].reset()) // Release child back to pool.
|
||||||
st.children[0] = nil
|
st.children[0] = nil
|
||||||
|
|
@ -345,7 +408,7 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
n := shortNode{Key: hexToCompactInPlace(st.key), Val: valueNode(st.val)}
|
n := shortNode{Key: hexToCompactInPlace(st.key), Val: valueNode(st.val)}
|
||||||
|
|
||||||
n.encode(t.h.encbuf)
|
n.encode(t.h.encbuf)
|
||||||
encodedNode = t.h.encodedBytes()
|
blob = t.h.encodedBytes()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("invalid node type")
|
panic("invalid node type")
|
||||||
|
|
@ -353,60 +416,61 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
|
|
||||||
st.typ = hashedNode
|
st.typ = hashedNode
|
||||||
st.key = st.key[:0]
|
st.key = st.key[:0]
|
||||||
if len(encodedNode) < 32 {
|
|
||||||
st.val = common.CopyBytes(encodedNode)
|
// Skip committing the non-root node if the size is smaller than 32 bytes.
|
||||||
|
if len(blob) < 32 && len(path) > 0 {
|
||||||
|
st.val = common.CopyBytes(blob)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
||||||
// input values
|
// input values.
|
||||||
st.val = t.h.hashData(encodedNode)
|
st.val = t.h.hashData(blob)
|
||||||
if t.writeFn != nil {
|
|
||||||
t.writeFn(path, common.BytesToHash(st.val), encodedNode)
|
// Short circuit if the stack trie is not configured for writing.
|
||||||
|
if t.options.Writer == nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
// Skip committing if the node is on the left boundary and stackTrie is
|
||||||
|
// configured to filter the boundary.
|
||||||
|
if t.options.SkipLeftBoundary && bytes.HasPrefix(t.first, path) {
|
||||||
|
if t.options.boundaryGauge != nil {
|
||||||
|
t.options.boundaryGauge.Inc(1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Skip committing if the node is on the right boundary and stackTrie is
|
||||||
|
// configured to filter the boundary.
|
||||||
|
if t.options.SkipRightBoundary && bytes.HasPrefix(t.last, path) {
|
||||||
|
if t.options.boundaryGauge != nil {
|
||||||
|
t.options.boundaryGauge.Inc(1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Clean up the internal dangling nodes covered by the extension node.
|
||||||
|
// This should be done before writing the node to adhere to the committing
|
||||||
|
// order from bottom to top.
|
||||||
|
for _, path := range internal {
|
||||||
|
t.options.Cleaner(path)
|
||||||
|
}
|
||||||
|
t.options.Writer(path, common.BytesToHash(st.val), blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash of the current node.
|
// Hash will firstly hash the entire trie if it's still not hashed and then commit
|
||||||
func (t *StackTrie) Hash() (h common.Hash) {
|
// all nodes to the associated database. Actually most of the trie nodes have been
|
||||||
st := t.root
|
// committed already. The main purpose here is to commit the nodes on right boundary.
|
||||||
t.hash(st, nil)
|
|
||||||
if len(st.val) == 32 {
|
|
||||||
copy(h[:], st.val)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
// If the node's RLP isn't 32 bytes long, the node will not
|
|
||||||
// be hashed, and instead contain the rlp-encoding of the
|
|
||||||
// node. For the top level node, we need to force the hashing.
|
|
||||||
t.h.sha.Reset()
|
|
||||||
t.h.sha.Write(st.val)
|
|
||||||
t.h.sha.Read(h[:])
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit will firstly hash the entire trie if it's still not hashed
|
|
||||||
// and then commit all nodes to the associated database. Actually most
|
|
||||||
// of the trie nodes MAY have been committed already. The main purpose
|
|
||||||
// here is to commit the root node.
|
|
||||||
//
|
//
|
||||||
// The associated database is expected, otherwise the whole commit
|
// For stack trie, Hash and Commit are functionally identical.
|
||||||
// functionality should be disabled.
|
func (t *StackTrie) Hash() common.Hash {
|
||||||
func (t *StackTrie) Commit() (h common.Hash, err error) {
|
n := t.root
|
||||||
if t.writeFn == nil {
|
t.hash(n, nil)
|
||||||
return common.Hash{}, ErrCommitDisabled
|
return common.BytesToHash(n.val)
|
||||||
}
|
}
|
||||||
st := t.root
|
|
||||||
t.hash(st, nil)
|
// Commit will firstly hash the entire trie if it's still not hashed and then commit
|
||||||
if len(st.val) == 32 {
|
// all nodes to the associated database. Actually most of the trie nodes have been
|
||||||
copy(h[:], st.val)
|
// committed already. The main purpose here is to commit the nodes on right boundary.
|
||||||
return h, nil
|
//
|
||||||
}
|
// For stack trie, Hash and Commit are functionally identical.
|
||||||
// If the node's RLP isn't 32 bytes long, the node will not
|
func (t *StackTrie) Commit() common.Hash {
|
||||||
// be hashed (and committed), and instead contain the rlp-encoding of the
|
return t.Hash()
|
||||||
// node. For the top level node, we need to force the hashing+commit.
|
|
||||||
t.h.sha.Reset()
|
|
||||||
t.h.sha.Write(st.val)
|
|
||||||
t.h.sha.Read(h[:])
|
|
||||||
|
|
||||||
t.writeFn(nil, h, st.val)
|
|
||||||
return h, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,14 @@ package trie
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/testutil"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStackTrieInsertAndHash(t *testing.T) {
|
func TestStackTrieInsertAndHash(t *testing.T) {
|
||||||
|
|
@ -376,3 +379,87 @@ func TestStacktrieNotModifyValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildPartialTree(entries []*kv, t *testing.T) map[string]common.Hash {
|
||||||
|
var (
|
||||||
|
options = NewStackTrieOptions()
|
||||||
|
nodes = make(map[string]common.Hash)
|
||||||
|
)
|
||||||
|
var (
|
||||||
|
first int
|
||||||
|
last = len(entries) - 1
|
||||||
|
|
||||||
|
noLeft bool
|
||||||
|
noRight bool
|
||||||
|
)
|
||||||
|
// Enter split mode if there are at least two elements
|
||||||
|
if rand.Intn(5) != 0 {
|
||||||
|
for {
|
||||||
|
first = rand.Intn(len(entries))
|
||||||
|
last = rand.Intn(len(entries))
|
||||||
|
if first <= last {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if first != 0 {
|
||||||
|
noLeft = true
|
||||||
|
}
|
||||||
|
if last != len(entries)-1 {
|
||||||
|
noRight = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options = options.WithSkipBoundary(noLeft, noRight, nil)
|
||||||
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
nodes[string(path)] = hash
|
||||||
|
})
|
||||||
|
tr := NewStackTrie(options)
|
||||||
|
|
||||||
|
for i := first; i <= last; i++ {
|
||||||
|
tr.MustUpdate(entries[i].k, entries[i].v)
|
||||||
|
}
|
||||||
|
tr.Commit()
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartialStackTrie(t *testing.T) {
|
||||||
|
for round := 0; round < 100; round++ {
|
||||||
|
var (
|
||||||
|
n = rand.Intn(100) + 1
|
||||||
|
entries []*kv
|
||||||
|
)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
var val []byte
|
||||||
|
if rand.Intn(3) == 0 {
|
||||||
|
val = testutil.RandBytes(3)
|
||||||
|
} else {
|
||||||
|
val = testutil.RandBytes(32)
|
||||||
|
}
|
||||||
|
entries = append(entries, &kv{
|
||||||
|
k: testutil.RandBytes(32),
|
||||||
|
v: val,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
slices.SortFunc(entries, (*kv).cmp)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nodes = make(map[string]common.Hash)
|
||||||
|
options = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
|
nodes[string(path)] = hash
|
||||||
|
})
|
||||||
|
)
|
||||||
|
tr := NewStackTrie(options)
|
||||||
|
|
||||||
|
for i := 0; i < len(entries); i++ {
|
||||||
|
tr.MustUpdate(entries[i].k, entries[i].v)
|
||||||
|
}
|
||||||
|
tr.Commit()
|
||||||
|
|
||||||
|
for j := 0; j < 100; j++ {
|
||||||
|
for path, hash := range buildPartialTree(entries, t) {
|
||||||
|
if nodes[path] != hash {
|
||||||
|
t.Errorf("%v, want %x, got %x", []byte(path), nodes[path], hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
26
trie/sync.go
26
trie/sync.go
|
|
@ -51,6 +51,18 @@ var (
|
||||||
// lookupGauge is the metric to track how many trie node lookups are
|
// lookupGauge is the metric to track how many trie node lookups are
|
||||||
// performed to determine if node needs to be deleted.
|
// performed to determine if node needs to be deleted.
|
||||||
lookupGauge = metrics.NewRegisteredGauge("trie/sync/lookup", nil)
|
lookupGauge = metrics.NewRegisteredGauge("trie/sync/lookup", nil)
|
||||||
|
|
||||||
|
// accountNodeSyncedGauge is the metric to track how many account trie
|
||||||
|
// node are written during the sync.
|
||||||
|
accountNodeSyncedGauge = metrics.NewRegisteredGauge("trie/sync/nodes/account", nil)
|
||||||
|
|
||||||
|
// storageNodeSyncedGauge is the metric to track how many account trie
|
||||||
|
// node are written during the sync.
|
||||||
|
storageNodeSyncedGauge = metrics.NewRegisteredGauge("trie/sync/nodes/storage", nil)
|
||||||
|
|
||||||
|
// codeSyncedGauge is the metric to track how many contract codes are
|
||||||
|
// written during the sync.
|
||||||
|
codeSyncedGauge = metrics.NewRegisteredGauge("trie/sync/codes", nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
// SyncPath is a path tuple identifying a particular trie node either in a single
|
// SyncPath is a path tuple identifying a particular trie node either in a single
|
||||||
|
|
@ -362,10 +374,22 @@ func (s *Sync) ProcessNode(result NodeSyncResult) error {
|
||||||
// storage, returning any occurred error.
|
// storage, returning any occurred error.
|
||||||
func (s *Sync) Commit(dbw ethdb.Batch) error {
|
func (s *Sync) Commit(dbw ethdb.Batch) error {
|
||||||
// Flush the pending node writes into database batch.
|
// Flush the pending node writes into database batch.
|
||||||
|
var (
|
||||||
|
account int
|
||||||
|
storage int
|
||||||
|
)
|
||||||
for path, value := range s.membatch.nodes {
|
for path, value := range s.membatch.nodes {
|
||||||
owner, inner := ResolvePath([]byte(path))
|
owner, inner := ResolvePath([]byte(path))
|
||||||
|
if owner == (common.Hash{}) {
|
||||||
|
account += 1
|
||||||
|
} else {
|
||||||
|
storage += 1
|
||||||
|
}
|
||||||
rawdb.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value, s.scheme)
|
rawdb.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value, s.scheme)
|
||||||
}
|
}
|
||||||
|
accountNodeSyncedGauge.Inc(int64(account))
|
||||||
|
storageNodeSyncedGauge.Inc(int64(storage))
|
||||||
|
|
||||||
// Flush the pending node deletes into the database batch.
|
// Flush the pending node deletes into the database batch.
|
||||||
// Please note that each written and deleted node has a
|
// Please note that each written and deleted node has a
|
||||||
// unique path, ensuring no duplication occurs.
|
// unique path, ensuring no duplication occurs.
|
||||||
|
|
@ -377,6 +401,8 @@ func (s *Sync) Commit(dbw ethdb.Batch) error {
|
||||||
for hash, value := range s.membatch.codes {
|
for hash, value := range s.membatch.codes {
|
||||||
rawdb.WriteCode(dbw, hash, value)
|
rawdb.WriteCode(dbw, hash, value)
|
||||||
}
|
}
|
||||||
|
codeSyncedGauge.Inc(int64(len(s.membatch.codes)))
|
||||||
|
|
||||||
s.membatch = newSyncMemBatch() // reset the batch
|
s.membatch = newSyncMemBatch() // reset the batch
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -912,9 +912,12 @@ func TestCommitSequenceStackTrie(t *testing.T) {
|
||||||
trie := NewEmpty(db)
|
trie := NewEmpty(db)
|
||||||
// Another sponge is used for the stacktrie commits
|
// Another sponge is used for the stacktrie commits
|
||||||
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
||||||
stTrie := NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
|
||||||
|
options := NewStackTrieOptions()
|
||||||
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme())
|
rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme())
|
||||||
})
|
})
|
||||||
|
stTrie := NewStackTrie(options)
|
||||||
// Fill the trie with elements
|
// Fill the trie with elements
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
// For the stack trie, we need to do inserts in proper order
|
// For the stack trie, we need to do inserts in proper order
|
||||||
|
|
@ -937,10 +940,7 @@ func TestCommitSequenceStackTrie(t *testing.T) {
|
||||||
db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
||||||
db.Commit(root, false)
|
db.Commit(root, false)
|
||||||
// And flush stacktrie -> disk
|
// And flush stacktrie -> disk
|
||||||
stRoot, err := stTrie.Commit()
|
stRoot := stTrie.Commit()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to commit stack trie %v", err)
|
|
||||||
}
|
|
||||||
if stRoot != root {
|
if stRoot != root {
|
||||||
t.Fatalf("root wrong, got %x exp %x", stRoot, root)
|
t.Fatalf("root wrong, got %x exp %x", stRoot, root)
|
||||||
}
|
}
|
||||||
|
|
@ -971,9 +971,12 @@ func TestCommitSequenceSmallRoot(t *testing.T) {
|
||||||
trie := NewEmpty(db)
|
trie := NewEmpty(db)
|
||||||
// Another sponge is used for the stacktrie commits
|
// Another sponge is used for the stacktrie commits
|
||||||
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
||||||
stTrie := NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
|
||||||
|
options := NewStackTrieOptions()
|
||||||
|
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme())
|
rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme())
|
||||||
})
|
})
|
||||||
|
stTrie := NewStackTrie(options)
|
||||||
// Add a single small-element to the trie(s)
|
// Add a single small-element to the trie(s)
|
||||||
key := make([]byte, 5)
|
key := make([]byte, 5)
|
||||||
key[0] = 1
|
key[0] = 1
|
||||||
|
|
@ -985,10 +988,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) {
|
||||||
db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
||||||
db.Commit(root, false)
|
db.Commit(root, false)
|
||||||
// And flush stacktrie -> disk
|
// And flush stacktrie -> disk
|
||||||
stRoot, err := stTrie.Commit()
|
stRoot := stTrie.Commit()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to commit stack trie %v", err)
|
|
||||||
}
|
|
||||||
if stRoot != root {
|
if stRoot != root {
|
||||||
t.Fatalf("root wrong, got %x exp %x", stRoot, root)
|
t.Fatalf("root wrong, got %x exp %x", stRoot, root)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -581,7 +581,16 @@ func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if ohead <= nhead {
|
otail, err := freezer.Tail()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Ensure that the truncation target falls within the specified range.
|
||||||
|
if ohead < nhead || nhead < otail {
|
||||||
|
return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, nhead)
|
||||||
|
}
|
||||||
|
// Short circuit if nothing to truncate.
|
||||||
|
if ohead == nhead {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
// Load the meta objects in range [nhead+1, ohead]
|
// Load the meta objects in range [nhead+1, ohead]
|
||||||
|
|
@ -610,11 +619,20 @@ func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead
|
||||||
// truncateFromTail removes the extra state histories from the tail with the given
|
// truncateFromTail removes the extra state histories from the tail with the given
|
||||||
// parameters. It returns the number of items removed from the tail.
|
// parameters. It returns the number of items removed from the tail.
|
||||||
func truncateFromTail(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, ntail uint64) (int, error) {
|
func truncateFromTail(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, ntail uint64) (int, error) {
|
||||||
|
ohead, err := freezer.Ancients()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
otail, err := freezer.Tail()
|
otail, err := freezer.Tail()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if otail >= ntail {
|
// Ensure that the truncation target falls within the specified range.
|
||||||
|
if otail > ntail || ntail > ohead {
|
||||||
|
return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, ntail)
|
||||||
|
}
|
||||||
|
// Short circuit if nothing to truncate.
|
||||||
|
if otail == ntail {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
// Load the meta objects in range [otail+1, ntail]
|
// Load the meta objects in range [otail+1, ntail]
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,50 @@ func TestTruncateTailHistories(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTruncateOutOfRange(t *testing.T) {
|
||||||
|
var (
|
||||||
|
hs = makeHistories(10)
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
freezer, _ = openFreezer(t.TempDir(), false)
|
||||||
|
)
|
||||||
|
defer freezer.Close()
|
||||||
|
|
||||||
|
for i := 0; i < len(hs); i++ {
|
||||||
|
accountData, storageData, accountIndex, storageIndex := hs[i].encode()
|
||||||
|
rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
|
||||||
|
rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
|
||||||
|
}
|
||||||
|
truncateFromTail(db, freezer, uint64(len(hs)/2))
|
||||||
|
|
||||||
|
// Ensure of-out-range truncations are rejected correctly.
|
||||||
|
head, _ := freezer.Ancients()
|
||||||
|
tail, _ := freezer.Tail()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
mode int
|
||||||
|
target uint64
|
||||||
|
expErr error
|
||||||
|
}{
|
||||||
|
{0, head, nil}, // nothing to delete
|
||||||
|
{0, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)},
|
||||||
|
{0, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)},
|
||||||
|
{1, tail, nil}, // nothing to delete
|
||||||
|
{1, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)},
|
||||||
|
{1, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
var gotErr error
|
||||||
|
if c.mode == 0 {
|
||||||
|
_, gotErr = truncateFromHead(db, freezer, c.target)
|
||||||
|
} else {
|
||||||
|
_, gotErr = truncateFromTail(db, freezer, c.target)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotErr, c.expErr) {
|
||||||
|
t.Errorf("Unexpected error, want: %v, got: %v", c.expErr, gotErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// openFreezer initializes the freezer instance for storing state histories.
|
// openFreezer initializes the freezer instance for storing state histories.
|
||||||
func openFreezer(datadir string, readOnly bool) (*rawdb.ResettableFreezer, error) {
|
func openFreezer(datadir string, readOnly bool) (*rawdb.ResettableFreezer, error) {
|
||||||
return rawdb.NewStateFreezer(datadir, readOnly)
|
return rawdb.NewStateFreezer(datadir, readOnly)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue