mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
merge geth v1.12.2 into develop
This commit is contained in:
commit
b343cffdb4
596 changed files with 37249 additions and 33711 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -6,6 +6,3 @@
|
||||||
path = tests/evm-benchmarks
|
path = tests/evm-benchmarks
|
||||||
url = https://github.com/ipsilon/evm-benchmarks
|
url = https://github.com/ipsilon/evm-benchmarks
|
||||||
shallow = true
|
shallow = true
|
||||||
[submodule "tests/testdata"]
|
|
||||||
path = tests/testdata
|
|
||||||
url = https://github.com/ethereum/tests.git
|
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,17 @@ func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
||||||
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorByID looks up an error by the 4-byte id,
|
||||||
|
// returns nil if none found.
|
||||||
|
func (abi *ABI) ErrorByID(sigdata [4]byte) (*Error, error) {
|
||||||
|
for _, errABI := range abi.Errors {
|
||||||
|
if bytes.Equal(errABI.ID[:4], sigdata[:]) {
|
||||||
|
return &errABI, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no error with id: %#x", sigdata[:])
|
||||||
|
}
|
||||||
|
|
||||||
// HasFallback returns an indicator whether a fallback function is included.
|
// HasFallback returns an indicator whether a fallback function is included.
|
||||||
func (abi *ABI) HasFallback() bool {
|
func (abi *ABI) HasFallback() bool {
|
||||||
return abi.Fallback.Type == Fallback
|
return abi.Fallback.Type == Fallback
|
||||||
|
|
|
||||||
|
|
@ -1148,6 +1148,34 @@ func TestABI_EventById(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestABI_ErrorByID(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(`[
|
||||||
|
{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"MyError1","type":"error"},
|
||||||
|
{"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"string","name":"b","type":"string"},{"internalType":"address","name":"c","type":"address"}],"internalType":"struct MyError.MyStruct","name":"x","type":"tuple"},{"internalType":"address","name":"y","type":"address"},{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"string","name":"b","type":"string"},{"internalType":"address","name":"c","type":"address"}],"internalType":"struct MyError.MyStruct","name":"z","type":"tuple"}],"name":"MyError2","type":"error"},
|
||||||
|
{"inputs":[{"internalType":"uint256[]","name":"x","type":"uint256[]"}],"name":"MyError3","type":"error"}
|
||||||
|
]`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for name, m := range abi.Errors {
|
||||||
|
a := fmt.Sprintf("%v", &m)
|
||||||
|
var id [4]byte
|
||||||
|
copy(id[:], m.ID[:4])
|
||||||
|
m2, err := abi.ErrorByID(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to look up ABI error: %v", err)
|
||||||
|
}
|
||||||
|
b := fmt.Sprintf("%v", m2)
|
||||||
|
if a != b {
|
||||||
|
t.Errorf("Error %v (id %x) not 'findable' by id in ABI", name, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// test unsuccessful lookups
|
||||||
|
if _, err = abi.ErrorByID([4]byte{}); err == nil {
|
||||||
|
t.Error("Expected error: no error with this id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
|
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
|
||||||
// conflict and that the second transfer method will be renamed transfer1.
|
// conflict and that the second transfer method will be renamed transfer1.
|
||||||
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import (
|
||||||
var (
|
var (
|
||||||
// ErrNoCode is returned by call and transact operations for which the requested
|
// ErrNoCode is returned by call and transact operations for which the requested
|
||||||
// recipient contract to operate on does not exist in the state db or does not
|
// recipient contract to operate on does not exist in the state db or does not
|
||||||
// have any code associated with it (i.e. suicided).
|
// have any code associated with it (i.e. self-destructed).
|
||||||
ErrNoCode = errors.New("no contract code at given address")
|
ErrNoCode = errors.New("no contract code at given address")
|
||||||
|
|
||||||
// ErrNoPendingState is raised when attempting to perform a pending state action
|
// ErrNoPendingState is raised when attempting to perform a pending state action
|
||||||
|
|
|
||||||
|
|
@ -718,11 +718,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||||
// Get the last block
|
// Get the last block
|
||||||
block, err := b.blockByHash(ctx, b.pendingBlock.ParentHash())
|
block, err := b.blockByHash(ctx, b.pendingBlock.ParentHash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not fetch parent")
|
return errors.New("could not fetch parent")
|
||||||
}
|
}
|
||||||
// Check transaction validity
|
// Check transaction validity
|
||||||
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
|
signer := types.MakeSigner(b.blockchain.Config(), block.Number(), block.Time())
|
||||||
|
|
||||||
sender, err := types.Sender(signer, tx)
|
sender, err := types.Sender(signer, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid transaction: %v", err)
|
return fmt.Errorf("invalid transaction: %v", err)
|
||||||
|
|
@ -862,7 +861,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
||||||
// Get the last block
|
// Get the last block
|
||||||
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
|
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return fmt.Errorf("could not find parent")
|
return errors.New("could not find parent")
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
|
|
@ -934,8 +933,11 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
header := rawdb.ReadHeader(fb.db, hash, *number)
|
||||||
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
|
if header == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return rawdb.ReadReceipts(fb.db, hash, *number, header.Time, fb.bc.Config()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ func TestAdjustTime(t *testing.T) {
|
||||||
func TestNewAdjustTimeFail(t *testing.T) {
|
func TestNewAdjustTimeFail(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
|
defer sim.blockchain.Stop()
|
||||||
|
|
||||||
// Create tx and send
|
// Create tx and send
|
||||||
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||||
|
|
|
||||||
|
|
@ -135,13 +135,19 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
||||||
normalized := original
|
normalized := original
|
||||||
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
||||||
|
|
||||||
// Ensure there is no duplicated identifier
|
// Ensure there is no duplicated identifier
|
||||||
var identifiers = callIdentifiers
|
var identifiers = callIdentifiers
|
||||||
if !original.IsConstant() {
|
if !original.IsConstant() {
|
||||||
identifiers = transactIdentifiers
|
identifiers = transactIdentifiers
|
||||||
}
|
}
|
||||||
|
// Name shouldn't start with a digit. It will make the generated code invalid.
|
||||||
|
if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
|
||||||
|
normalizedName = fmt.Sprintf("M%s", normalizedName)
|
||||||
|
normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool {
|
||||||
|
_, ok := identifiers[name]
|
||||||
|
return ok
|
||||||
|
})
|
||||||
|
}
|
||||||
if identifiers[normalizedName] {
|
if identifiers[normalizedName] {
|
||||||
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||||
}
|
}
|
||||||
|
|
@ -192,6 +198,14 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
|
|
||||||
// Ensure there is no duplicated identifier
|
// Ensure there is no duplicated identifier
|
||||||
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
||||||
|
// Name shouldn't start with a digit. It will make the generated code invalid.
|
||||||
|
if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
|
||||||
|
normalizedName = fmt.Sprintf("E%s", normalizedName)
|
||||||
|
normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool {
|
||||||
|
_, ok := eventIdentifiers[name]
|
||||||
|
return ok
|
||||||
|
})
|
||||||
|
}
|
||||||
if eventIdentifiers[normalizedName] {
|
if eventIdentifiers[normalizedName] {
|
||||||
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2038,6 +2038,29 @@ var bindTests = []struct {
|
||||||
t.Errorf("error deploying the contract: %v", err)
|
t.Errorf("error deploying the contract: %v", err)
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
}, {
|
||||||
|
name: "NumericMethodName",
|
||||||
|
contract: `
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.4.22 <0.9.0;
|
||||||
|
|
||||||
|
contract NumericMethodName {
|
||||||
|
event _1TestEvent(address _param);
|
||||||
|
function _1test() public pure {}
|
||||||
|
function __1test() public pure {}
|
||||||
|
function __2test() public pure {}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
bytecode: []string{"0x6080604052348015600f57600080fd5b5060958061001e6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80639d993132146041578063d02767c7146049578063ffa02795146051575b600080fd5b60476059565b005b604f605b565b005b6057605d565b005b565b565b56fea26469706673582212200382ca602dff96a7e2ba54657985e2b4ac423a56abe4a1f0667bc635c4d4371f64736f6c63430008110033"},
|
||||||
|
abi: []string{`[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_param","type":"address"}],"name":"_1TestEvent","type":"event"},{"inputs":[],"name":"_1test","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"__1test","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"__2test","outputs":[],"stateMutability":"pure","type":"function"}]`},
|
||||||
|
imports: `
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
`,
|
||||||
|
tester: `
|
||||||
|
if b, err := NewNumericMethodName(common.Address{}, nil); b == nil || err != nil {
|
||||||
|
t.Fatalf("combined binding (%v) nil or error (%v) not nil", b, nil)
|
||||||
|
}
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ type Error struct {
|
||||||
str string
|
str string
|
||||||
|
|
||||||
// Sig contains the string signature according to the ABI spec.
|
// Sig contains the string signature according to the ABI spec.
|
||||||
// e.g. error foo(uint32 a, int b) = "foo(uint32,int256)"
|
// e.g. error foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||||
// Please note that "int" is substitute for its canonical representation "int256"
|
// Please note that "int" is substitute for its canonical representation "int256"
|
||||||
Sig string
|
Sig string
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ func NewError(name string, inputs Arguments) Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Error) String() string {
|
func (e Error) String() string {
|
||||||
return e.str
|
return e.str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||||
structFieldName := ToCamelCase(argName)
|
structFieldName := ToCamelCase(argName)
|
||||||
|
|
||||||
if structFieldName == "" {
|
if structFieldName == "" {
|
||||||
return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
|
return nil, errors.New("abi: purely underscored output cannot unpack to struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
// this abi has already been paired, skip it... unless there exists another, yet unassigned
|
// this abi has already been paired, skip it... unless there exists another, yet unassigned
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ func isIdentifierSymbol(c byte) bool {
|
||||||
|
|
||||||
func parseToken(unescapedSelector string, isIdent bool) (string, string, error) {
|
func parseToken(unescapedSelector string, isIdent bool) (string, string, error) {
|
||||||
if len(unescapedSelector) == 0 {
|
if len(unescapedSelector) == 0 {
|
||||||
return "", "", fmt.Errorf("empty token")
|
return "", "", errors.New("empty token")
|
||||||
}
|
}
|
||||||
|
|
||||||
firstChar := unescapedSelector[0]
|
firstChar := unescapedSelector[0]
|
||||||
|
|
@ -126,7 +127,7 @@ func parseCompositeType(unescapedSelector string) ([]interface{}, string, error)
|
||||||
|
|
||||||
func parseType(unescapedSelector string) (interface{}, string, error) {
|
func parseType(unescapedSelector string) (interface{}, string, error) {
|
||||||
if len(unescapedSelector) == 0 {
|
if len(unescapedSelector) == 0 {
|
||||||
return nil, "", fmt.Errorf("empty type")
|
return nil, "", errors.New("empty type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if unescapedSelector[0] == '(' {
|
if unescapedSelector[0] == '(' {
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ var (
|
||||||
func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
|
func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
// check that array brackets are equal if they exist
|
// check that array brackets are equal if they exist
|
||||||
if strings.Count(t, "[") != strings.Count(t, "]") {
|
if strings.Count(t, "[") != strings.Count(t, "]") {
|
||||||
return Type{}, fmt.Errorf("invalid arg type in abi")
|
return Type{}, errors.New("invalid arg type in abi")
|
||||||
}
|
}
|
||||||
|
|
||||||
typ.stringKind = t
|
typ.stringKind = t
|
||||||
|
|
@ -113,7 +113,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
|
|
||||||
typ.stringKind = embeddedType.stringKind + sliced
|
typ.stringKind = embeddedType.stringKind + sliced
|
||||||
} else {
|
} else {
|
||||||
return Type{}, fmt.Errorf("invalid formatting of array type")
|
return Type{}, errors.New("invalid formatting of array type")
|
||||||
}
|
}
|
||||||
|
|
||||||
return typ, err
|
return typ, err
|
||||||
|
|
@ -380,7 +380,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requireLengthPrefix returns whether the type requires any sort of length
|
// requiresLengthPrefix returns whether the type requires any sort of length
|
||||||
// prefixing.
|
// prefixing.
|
||||||
func (t Type) requiresLengthPrefix() bool {
|
func (t Type) requiresLengthPrefix() bool {
|
||||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
|
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -137,7 +138,7 @@ func readBool(word []byte) (bool, error) {
|
||||||
// readFunctionType enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
|
// readFunctionType enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
|
||||||
func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
||||||
if t.T != FunctionTy {
|
if t.T != FunctionTy {
|
||||||
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
|
return [24]byte{}, errors.New("abi: invalid type in call to make function type byte array")
|
||||||
}
|
}
|
||||||
|
|
||||||
if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
|
if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
|
||||||
|
|
@ -152,7 +153,7 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
||||||
// ReadFixedBytes uses reflection to create a fixed array to be read from.
|
// ReadFixedBytes uses reflection to create a fixed array to be read from.
|
||||||
func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
|
func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
|
||||||
if t.T != FixedBytesTy {
|
if t.T != FixedBytesTy {
|
||||||
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
|
return nil, errors.New("abi: invalid type in call to make fixed byte array")
|
||||||
}
|
}
|
||||||
// convert
|
// convert
|
||||||
array := reflect.New(t.GetType()).Elem()
|
array := reflect.New(t.GetType()).Elem()
|
||||||
|
|
@ -182,7 +183,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
// declare our array
|
// declare our array
|
||||||
refSlice = reflect.New(t.GetType()).Elem()
|
refSlice = reflect.New(t.GetType()).Elem()
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
|
return nil, errors.New("abi: invalid type in array/slice unpacking stage")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrays have packed elements, resulting in longer unpack steps.
|
// Arrays have packed elements, resulting in longer unpack steps.
|
||||||
|
|
|
||||||
13
accounts/external/backend.go
vendored
13
accounts/external/backend.go
vendored
|
|
@ -17,6 +17,7 @@
|
||||||
package external
|
package external
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -102,11 +103,11 @@ func (api *ExternalSigner) Status() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) Open(passphrase string) error {
|
func (api *ExternalSigner) Open(passphrase string) error {
|
||||||
return fmt.Errorf("operation not supported on external signers")
|
return errors.New("operation not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) Close() error {
|
func (api *ExternalSigner) Close() error {
|
||||||
return fmt.Errorf("operation not supported on external signers")
|
return errors.New("operation not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) Accounts() []accounts.Account {
|
func (api *ExternalSigner) Accounts() []accounts.Account {
|
||||||
|
|
@ -156,7 +157,7 @@ func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
|
func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
|
||||||
return accounts.Account{}, fmt.Errorf("operation not supported on external signers")
|
return accounts.Account{}, errors.New("operation not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
|
func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
|
||||||
|
|
@ -265,14 +266,14 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
||||||
return []byte{}, fmt.Errorf("password-operations not supported on external signers")
|
return []byte{}, errors.New("password-operations not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
return nil, fmt.Errorf("password-operations not supported on external signers")
|
return nil, errors.New("password-operations not supported on external signers")
|
||||||
}
|
}
|
||||||
func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("password-operations not supported on external signers")
|
return nil, errors.New("password-operations not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
|
func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Minimum amount of time between cache reloads. This limit applies if the platform does
|
// Minimum amount of time between cache reloads. This limit applies if the platform does
|
||||||
|
|
@ -39,11 +40,10 @@ import (
|
||||||
// exist yet, the code will attempt to create a watcher at most this often.
|
// exist yet, the code will attempt to create a watcher at most this often.
|
||||||
const minReloadInterval = 2 * time.Second
|
const minReloadInterval = 2 * time.Second
|
||||||
|
|
||||||
type accountsByURL []accounts.Account
|
// byURL defines the sorting order for accounts.
|
||||||
|
func byURL(a, b accounts.Account) int {
|
||||||
func (s accountsByURL) Len() int { return len(s) }
|
return a.URL.Cmp(b.URL)
|
||||||
func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 }
|
}
|
||||||
func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
// AmbiguousAddrError is returned when attempting to unlock
|
// AmbiguousAddrError is returned when attempting to unlock
|
||||||
// an address for which more than one file exists.
|
// an address for which more than one file exists.
|
||||||
|
|
@ -69,7 +69,7 @@ type accountCache struct {
|
||||||
keydir string
|
keydir string
|
||||||
watcher *watcher
|
watcher *watcher
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
all accountsByURL
|
all []accounts.Account
|
||||||
byAddr map[common.Address][]accounts.Account
|
byAddr map[common.Address][]accounts.Account
|
||||||
throttle *time.Timer
|
throttle *time.Timer
|
||||||
notify chan struct{}
|
notify chan struct{}
|
||||||
|
|
@ -206,8 +206,7 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
|
||||||
default:
|
default:
|
||||||
err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
|
err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
|
||||||
copy(err.Matches, matches)
|
copy(err.Matches, matches)
|
||||||
sort.Sort(accountsByURL(err.Matches))
|
slices.SortFunc(err.Matches, byURL)
|
||||||
|
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -75,7 +76,7 @@ func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
|
||||||
select {
|
select {
|
||||||
case <-ks.changes:
|
case <-ks.changes:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("wasn't notified of new accounts")
|
return errors.New("wasn't notified of new accounts")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -212,8 +213,7 @@ func TestCacheAddDeleteOrder(t *testing.T) {
|
||||||
// Check that the account list is sorted by filename.
|
// Check that the account list is sorted by filename.
|
||||||
wantAccounts := make([]accounts.Account, len(accs))
|
wantAccounts := make([]accounts.Account, len(accs))
|
||||||
copy(wantAccounts, accs)
|
copy(wantAccounts, accs)
|
||||||
sort.Sort(accountsByURL(wantAccounts))
|
slices.SortFunc(wantAccounts, byURL)
|
||||||
|
|
||||||
list := cache.accounts()
|
list := cache.accounts()
|
||||||
if !reflect.DeepEqual(list, wantAccounts) {
|
if !reflect.DeepEqual(list, wantAccounts) {
|
||||||
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
|
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -31,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testSigData = make([]byte, 32)
|
var testSigData = make([]byte, 32)
|
||||||
|
|
@ -439,9 +439,7 @@ func TestImportRace(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ks2 := tmpKeyStore(t, true)
|
_, ks2 := tmpKeyStore(t, true)
|
||||||
|
var atom atomic.Uint32
|
||||||
var atom uint32
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
@ -451,13 +449,12 @@ func TestImportRace(t *testing.T) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
||||||
atomic.AddUint32(&atom, 1)
|
atom.Add(1)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
if atom.Load() != 1 {
|
||||||
if atom != 1 {
|
|
||||||
t.Errorf("Import is racy")
|
t.Errorf("Import is racy")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -473,9 +470,7 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle
|
||||||
for _, account := range live {
|
for _, account := range live {
|
||||||
liveList = append(liveList, account)
|
liveList = append(liveList, account)
|
||||||
}
|
}
|
||||||
|
slices.SortFunc(liveList, byURL)
|
||||||
sort.Sort(accountsByURL(liveList))
|
|
||||||
|
|
||||||
for j, wallet := range wallets {
|
for j, wallet := range wallets {
|
||||||
if accs := wallet.Accounts(); len(accs) != 1 {
|
if accs := wallet.Accounts(); len(accs) != 1 {
|
||||||
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
||||||
|
|
|
||||||
|
|
@ -241,12 +241,13 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
key, err := crypto.ToECDSA(keyBytes)
|
||||||
key := crypto.ToECDSAUnsafe(keyBytes)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid key: %w", err)
|
||||||
|
}
|
||||||
id, err := uuid.FromBytes(keyId)
|
id, err := uuid.FromBytes(keyId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("invalid UUID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Key{
|
return &Key{
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -129,7 +130,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
||||||
// Unpair disestablishes an existing pairing.
|
// Unpair disestablishes an existing pairing.
|
||||||
func (s *SecureChannelSession) Unpair() error {
|
func (s *SecureChannelSession) Unpair() error {
|
||||||
if s.PairingKey == nil {
|
if s.PairingKey == nil {
|
||||||
return fmt.Errorf("cannot unpair: not paired")
|
return errors.New("cannot unpair: not paired")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
||||||
|
|
@ -147,7 +148,7 @@ func (s *SecureChannelSession) Unpair() error {
|
||||||
// Open initializes the secure channel.
|
// Open initializes the secure channel.
|
||||||
func (s *SecureChannelSession) Open() error {
|
func (s *SecureChannelSession) Open() error {
|
||||||
if s.iv != nil {
|
if s.iv != nil {
|
||||||
return fmt.Errorf("session already opened")
|
return errors.New("session already opened")
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.open()
|
response, err := s.open()
|
||||||
|
|
@ -222,7 +223,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error
|
||||||
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
|
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
|
||||||
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
|
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
|
||||||
if s.iv == nil {
|
if s.iv == nil {
|
||||||
return nil, fmt.Errorf("channel not open")
|
return nil, errors.New("channel not open")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := s.encryptAPDU(data)
|
data, err := s.encryptAPDU(data)
|
||||||
|
|
@ -264,7 +265,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(s.iv, rmac) {
|
if !bytes.Equal(s.iv, rmac) {
|
||||||
return nil, fmt.Errorf("invalid MAC in response")
|
return nil, errors.New("invalid MAC in response")
|
||||||
}
|
}
|
||||||
|
|
||||||
rapdu := &responseAPDU{}
|
rapdu := &responseAPDU{}
|
||||||
|
|
@ -334,8 +335,7 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
|
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("expected end of padding, got 0")
|
||||||
return nil, fmt.Errorf("expected end of padding, got 0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateIV is an internal method that updates the initialization vector after
|
// updateIV is an internal method that updates the initialization vector after
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ func (w *Wallet) release() error {
|
||||||
// with the wallet.
|
// with the wallet.
|
||||||
func (w *Wallet) pair(puk []byte) error {
|
func (w *Wallet) pair(puk []byte) error {
|
||||||
if w.session.paired() {
|
if w.session.paired() {
|
||||||
return fmt.Errorf("wallet already paired")
|
return errors.New("wallet already paired")
|
||||||
}
|
}
|
||||||
|
|
||||||
pairing, err := w.session.pair(puk)
|
pairing, err := w.session.pair(puk)
|
||||||
|
|
@ -850,7 +850,7 @@ func (s *Session) pair(secret []byte) (smartcardPairing, error) {
|
||||||
// unpair deletes an existing pairing.
|
// unpair deletes an existing pairing.
|
||||||
func (s *Session) unpair() error {
|
func (s *Session) unpair() error {
|
||||||
if !s.verified {
|
if !s.verified {
|
||||||
return fmt.Errorf("unpair requires that the PIN be verified")
|
return errors.New("unpair requires that the PIN be verified")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.Channel.Unpair()
|
return s.Channel.Unpair()
|
||||||
|
|
@ -955,7 +955,7 @@ func (s *Session) initialize(seed []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if status == "Online" {
|
if status == "Online" {
|
||||||
return fmt.Errorf("card is already initialized, cowardly refusing to proceed")
|
return errors.New("card is already initialized, cowardly refusing to proceed")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Wallet.lock.Lock()
|
s.Wallet.lock.Lock()
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,9 @@ type Hub struct {
|
||||||
stateLock sync.RWMutex // Protects the internals of the hub from racey access
|
stateLock sync.RWMutex // Protects the internals of the hub from racey access
|
||||||
|
|
||||||
// TODO(karalabe): remove if hotplug lands on Windows
|
// TODO(karalabe): remove if hotplug lands on Windows
|
||||||
commsPend int // Number of operations blocking enumeration
|
commsPend int // Number of operations blocking enumeration
|
||||||
commsLock sync.Mutex // Lock protecting the pending counter and enumeration
|
commsLock sync.Mutex // Lock protecting the pending counter and enumeration
|
||||||
enumFails uint32 // Number of times enumeration has failed
|
enumFails atomic.Uint32 // Number of times enumeration has failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
||||||
|
|
@ -154,7 +154,7 @@ func (hub *Hub) refreshWallets() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// If USB enumeration is continually failing, don't keep trying indefinitely
|
// If USB enumeration is continually failing, don't keep trying indefinitely
|
||||||
if atomic.LoadUint32(&hub.enumFails) > 2 {
|
if hub.enumFails.Load() > 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Retrieve the current list of USB wallet devices
|
// Retrieve the current list of USB wallet devices
|
||||||
|
|
@ -176,8 +176,7 @@ func (hub *Hub) refreshWallets() {
|
||||||
|
|
||||||
infos, err := usb.Enumerate(hub.vendorID, 0)
|
infos, err := usb.Enumerate(hub.vendorID, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failcount := atomic.AddUint32(&hub.enumFails, 1)
|
failcount := hub.enumFails.Add(1)
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
// See rationale before the enumeration why this is needed and only on Linux.
|
// See rationale before the enumeration why this is needed and only on Linux.
|
||||||
hub.commsLock.Unlock()
|
hub.commsLock.Unlock()
|
||||||
|
|
@ -188,8 +187,7 @@ func (hub *Hub) refreshWallets() {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
hub.enumFails.Store(0)
|
||||||
atomic.StoreUint32(&hub.enumFails, 0)
|
|
||||||
|
|
||||||
for _, info := range infos {
|
for _, info := range infos {
|
||||||
for _, id := range hub.productIDs {
|
for _, id := range hub.productIDs {
|
||||||
|
|
|
||||||
|
|
@ -653,7 +653,7 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
|
||||||
return signed, nil
|
return signed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary
|
// SignTextWithPassphrase implements accounts.Wallet, however signing arbitrary
|
||||||
// data is not supported for Ledger wallets, so this method will always return
|
// data is not supported for Ledger wallets, so this method will always return
|
||||||
// an error.
|
// an error.
|
||||||
func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc ExecutableData
|
var enc ExecutableData
|
||||||
|
|
@ -57,7 +59,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
enc.Withdrawals = e.Withdrawals
|
enc.Withdrawals = e.Withdrawals
|
||||||
|
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
||||||
|
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,6 +82,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var dec ExecutableData
|
var dec ExecutableData
|
||||||
|
|
@ -176,6 +181,11 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
if dec.Withdrawals != nil {
|
if dec.Withdrawals != nil {
|
||||||
e.Withdrawals = dec.Withdrawals
|
e.Withdrawals = dec.Withdrawals
|
||||||
}
|
}
|
||||||
|
if dec.BlobGasUsed != nil {
|
||||||
|
e.BlobGasUsed = (*uint64)(dec.BlobGasUsed)
|
||||||
|
}
|
||||||
|
if dec.ExcessBlobGas != nil {
|
||||||
|
e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,13 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
|
||||||
type ExecutionPayloadEnvelope struct {
|
type ExecutionPayloadEnvelope struct {
|
||||||
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
|
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc ExecutionPayloadEnvelope
|
var enc ExecutionPayloadEnvelope
|
||||||
enc.ExecutionPayload = e.ExecutionPayload
|
enc.ExecutionPayload = e.ExecutionPayload
|
||||||
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
||||||
|
enc.BlobsBundle = e.BlobsBundle
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||||
type ExecutionPayloadEnvelope struct {
|
type ExecutionPayloadEnvelope struct {
|
||||||
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
|
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var dec ExecutionPayloadEnvelope
|
var dec ExecutionPayloadEnvelope
|
||||||
|
|
@ -49,6 +51,8 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
e.BlockValue = (*big.Int)(dec.BlockValue)
|
e.BlockValue = (*big.Int)(dec.BlockValue)
|
||||||
|
if dec.BlobsBundle != nil {
|
||||||
|
e.BlobsBundle = dec.BlobsBundle
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -61,6 +62,8 @@ type ExecutableData struct {
|
||||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||||
|
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for executableData.
|
// JSON type overrides for executableData.
|
||||||
|
|
@ -73,6 +76,8 @@ type executableDataMarshaling struct {
|
||||||
ExtraData hexutil.Bytes
|
ExtraData hexutil.Bytes
|
||||||
LogsBloom hexutil.Bytes
|
LogsBloom hexutil.Bytes
|
||||||
Transactions []hexutil.Bytes
|
Transactions []hexutil.Bytes
|
||||||
|
BlobGasUsed *hexutil.Uint64
|
||||||
|
ExcessBlobGas *hexutil.Uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
||||||
|
|
@ -80,6 +85,13 @@ type executableDataMarshaling struct {
|
||||||
type ExecutionPayloadEnvelope struct {
|
type ExecutionPayloadEnvelope struct {
|
||||||
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
||||||
|
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlobsBundleV1 struct {
|
||||||
|
Commitments []hexutil.Bytes `json:"commitments"`
|
||||||
|
Proofs []hexutil.Bytes `json:"proofs"`
|
||||||
|
Blobs []hexutil.Bytes `json:"blobs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for ExecutionPayloadEnvelope.
|
// JSON type overrides for ExecutionPayloadEnvelope.
|
||||||
|
|
@ -157,14 +169,15 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||||
// ExecutableDataToBlock constructs a block from executable data.
|
// ExecutableDataToBlock constructs a block from executable data.
|
||||||
// It verifies that the following fields:
|
// It verifies that the following fields:
|
||||||
//
|
//
|
||||||
// len(extraData) <= 32
|
// len(extraData) <= 32
|
||||||
// uncleHash = emptyUncleHash
|
// uncleHash = emptyUncleHash
|
||||||
// difficulty = 0
|
// difficulty = 0
|
||||||
|
// if versionedHashes != nil, versionedHashes match to blob transactions
|
||||||
//
|
//
|
||||||
// and that the blockhash of the constructed block matches the parameters. Nil
|
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||||
// Withdrawals value will propagate through the returned block. Empty
|
// Withdrawals value will propagate through the returned block. Empty
|
||||||
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
||||||
func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash) (*types.Block, error) {
|
||||||
txs, err := decodeTransactions(params.Transactions)
|
txs, err := decodeTransactions(params.Transactions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -181,6 +194,18 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
||||||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
||||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
||||||
}
|
}
|
||||||
|
var blobHashes []common.Hash
|
||||||
|
for _, tx := range txs {
|
||||||
|
blobHashes = append(blobHashes, tx.BlobHashes()...)
|
||||||
|
}
|
||||||
|
if len(blobHashes) != len(versionedHashes) {
|
||||||
|
return nil, fmt.Errorf("invalid number of versionedHashes: %v blobHashes: %v", versionedHashes, blobHashes)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(blobHashes); i++ {
|
||||||
|
if blobHashes[i] != versionedHashes[i] {
|
||||||
|
return nil, fmt.Errorf("invalid versionedHash at %v: %v blobHashes: %v", i, versionedHashes, blobHashes)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Only set withdrawalsRoot if it is non-nil. This allows CLs to use
|
// Only set withdrawalsRoot if it is non-nil. This allows CLs to use
|
||||||
// ExecutableData before withdrawals are enabled by marshaling
|
// ExecutableData before withdrawals are enabled by marshaling
|
||||||
// Withdrawals as the json null value.
|
// Withdrawals as the json null value.
|
||||||
|
|
@ -208,6 +233,8 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
||||||
Extra: params.ExtraData,
|
Extra: params.ExtraData,
|
||||||
MixDigest: params.Random,
|
MixDigest: params.Random,
|
||||||
WithdrawalsHash: withdrawalsRoot,
|
WithdrawalsHash: withdrawalsRoot,
|
||||||
|
ExcessBlobGas: params.ExcessBlobGas,
|
||||||
|
BlobGasUsed: params.BlobGasUsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||||
|
|
@ -220,7 +247,7 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
||||||
|
|
||||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||||
// fields from the given block. It assumes the given block is post-merge block.
|
// fields from the given block. It assumes the given block is post-merge block.
|
||||||
func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadEnvelope {
|
func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Blob, commitments []kzg4844.Commitment, proofs []kzg4844.Proof) *ExecutionPayloadEnvelope {
|
||||||
data := &ExecutableData{
|
data := &ExecutableData{
|
||||||
BlockHash: block.Hash(),
|
BlockHash: block.Hash(),
|
||||||
ParentHash: block.ParentHash(),
|
ParentHash: block.ParentHash(),
|
||||||
|
|
@ -237,9 +264,20 @@ func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadE
|
||||||
Random: block.MixDigest(),
|
Random: block.MixDigest(),
|
||||||
ExtraData: block.Extra(),
|
ExtraData: block.Extra(),
|
||||||
Withdrawals: block.Withdrawals(),
|
Withdrawals: block.Withdrawals(),
|
||||||
|
BlobGasUsed: block.BlobGasUsed(),
|
||||||
|
ExcessBlobGas: block.ExcessBlobGas(),
|
||||||
}
|
}
|
||||||
|
blobsBundle := BlobsBundleV1{
|
||||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees}
|
Commitments: make([]hexutil.Bytes, 0),
|
||||||
|
Blobs: make([]hexutil.Bytes, 0),
|
||||||
|
Proofs: make([]hexutil.Bytes, 0),
|
||||||
|
}
|
||||||
|
for i := range blobs {
|
||||||
|
blobsBundle.Blobs = append(blobsBundle.Blobs, hexutil.Bytes(blobs[i][:]))
|
||||||
|
blobsBundle.Commitments = append(blobsBundle.Commitments, hexutil.Bytes(commitments[i][:]))
|
||||||
|
blobsBundle.Proofs = append(blobsBundle.Proofs, hexutil.Bytes(proofs[i][:]))
|
||||||
|
}
|
||||||
|
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &blobsBundle}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
||||||
|
|
|
||||||
67
beacon/merkle/merkle.go
Normal file
67
beacon/merkle/merkle.go
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2022 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 merkle implements proof verifications in binary merkle trees.
|
||||||
|
package merkle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Value represents either a 32 byte leaf value or hash node in a binary merkle tree/partial proof.
|
||||||
|
type Value [32]byte
|
||||||
|
|
||||||
|
// Values represent a series of merkle tree leaves/nodes.
|
||||||
|
type Values []Value
|
||||||
|
|
||||||
|
var valueT = reflect.TypeOf(Value{})
|
||||||
|
|
||||||
|
// UnmarshalJSON parses a merkle value in hex syntax.
|
||||||
|
func (m *Value) UnmarshalJSON(input []byte) error {
|
||||||
|
return hexutil.UnmarshalFixedJSON(valueT, input, m[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyProof verifies a Merkle proof branch for a single value in a
|
||||||
|
// binary Merkle tree (index is a generalized tree index).
|
||||||
|
func VerifyProof(root common.Hash, index uint64, branch Values, value Value) error {
|
||||||
|
hasher := sha256.New()
|
||||||
|
for _, sibling := range branch {
|
||||||
|
hasher.Reset()
|
||||||
|
if index&1 == 0 {
|
||||||
|
hasher.Write(value[:])
|
||||||
|
hasher.Write(sibling[:])
|
||||||
|
} else {
|
||||||
|
hasher.Write(sibling[:])
|
||||||
|
hasher.Write(value[:])
|
||||||
|
}
|
||||||
|
hasher.Sum(value[:0])
|
||||||
|
if index >>= 1; index == 0 {
|
||||||
|
return errors.New("branch has extra items")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index != 1 {
|
||||||
|
return errors.New("branch is missing items")
|
||||||
|
}
|
||||||
|
if common.Hash(value) != root {
|
||||||
|
return errors.New("root mismatch")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
// Copyright 2022 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,23 +14,31 @@
|
||||||
// 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 snapshot
|
package params
|
||||||
|
|
||||||
import (
|
const (
|
||||||
"bytes"
|
EpochLength = 32
|
||||||
|
SyncPeriodLength = 8192
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
BLSSignatureSize = 96
|
||||||
|
BLSPubkeySize = 48
|
||||||
|
|
||||||
|
SyncCommitteeSize = 512
|
||||||
|
SyncCommitteeBitmaskSize = SyncCommitteeSize / 8
|
||||||
|
SyncCommitteeSupermajority = (SyncCommitteeSize*2 + 2) / 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// hashes is a helper to implement sort.Interface.
|
const (
|
||||||
type hashes []common.Hash
|
StateIndexGenesisTime = 32
|
||||||
|
StateIndexGenesisValidators = 33
|
||||||
// Len is the number of elements in the collection.
|
StateIndexForkVersion = 141
|
||||||
func (hs hashes) Len() int { return len(hs) }
|
StateIndexLatestHeader = 36
|
||||||
|
StateIndexBlockRoots = 37
|
||||||
// Less reports whether the element with index i should sort before the element
|
StateIndexStateRoots = 38
|
||||||
// with index j.
|
StateIndexHistoricRoots = 39
|
||||||
func (hs hashes) Less(i, j int) bool { return bytes.Compare(hs[i][:], hs[j][:]) < 0 }
|
StateIndexFinalBlock = 105
|
||||||
|
StateIndexSyncCommittee = 54
|
||||||
// Swap swaps the elements with indexes i and j.
|
StateIndexNextSyncCommittee = 55
|
||||||
func (hs hashes) Swap(i, j int) { hs[i], hs[j] = hs[j], hs[i] }
|
StateIndexExecPayload = 56
|
||||||
|
StateIndexExecHead = 908
|
||||||
|
)
|
||||||
191
beacon/types/committee.go
Normal file
191
beacon/types/committee.go
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
// 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 types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/params"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
bls "github.com/protolambda/bls12-381-util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SerializedSyncCommitteeSize is the size of the sync committee plus the
|
||||||
|
// aggregate public key.
|
||||||
|
const SerializedSyncCommitteeSize = (params.SyncCommitteeSize + 1) * params.BLSPubkeySize
|
||||||
|
|
||||||
|
// SerializedSyncCommittee is the serialized version of a sync committee
|
||||||
|
// plus the aggregate public key.
|
||||||
|
type SerializedSyncCommittee [SerializedSyncCommitteeSize]byte
|
||||||
|
|
||||||
|
// jsonSyncCommittee is the JSON representation of a sync committee.
|
||||||
|
//
|
||||||
|
// See data structure definition here:
|
||||||
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#syncaggregate
|
||||||
|
type jsonSyncCommittee struct {
|
||||||
|
Pubkeys []hexutil.Bytes `json:"pubkeys"`
|
||||||
|
Aggregate hexutil.Bytes `json:"aggregate_pubkey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s *SerializedSyncCommittee) MarshalJSON() ([]byte, error) {
|
||||||
|
sc := jsonSyncCommittee{Pubkeys: make([]hexutil.Bytes, params.SyncCommitteeSize)}
|
||||||
|
for i := range sc.Pubkeys {
|
||||||
|
sc.Pubkeys[i] = make(hexutil.Bytes, params.BLSPubkeySize)
|
||||||
|
copy(sc.Pubkeys[i][:], s[i*params.BLSPubkeySize:(i+1)*params.BLSPubkeySize])
|
||||||
|
}
|
||||||
|
sc.Aggregate = make(hexutil.Bytes, params.BLSPubkeySize)
|
||||||
|
copy(sc.Aggregate[:], s[params.SyncCommitteeSize*params.BLSPubkeySize:])
|
||||||
|
return json.Marshal(&sc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Marshaler.
|
||||||
|
func (s *SerializedSyncCommittee) UnmarshalJSON(input []byte) error {
|
||||||
|
var sc jsonSyncCommittee
|
||||||
|
if err := json.Unmarshal(input, &sc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(sc.Pubkeys) != params.SyncCommitteeSize {
|
||||||
|
return fmt.Errorf("invalid number of pubkeys %d", len(sc.Pubkeys))
|
||||||
|
}
|
||||||
|
for i, key := range sc.Pubkeys {
|
||||||
|
if len(key) != params.BLSPubkeySize {
|
||||||
|
return fmt.Errorf("pubkey %d has invalid size %d", i, len(key))
|
||||||
|
}
|
||||||
|
copy(s[i*params.BLSPubkeySize:], key[:])
|
||||||
|
}
|
||||||
|
if len(sc.Aggregate) != params.BLSPubkeySize {
|
||||||
|
return fmt.Errorf("invalid aggregate pubkey size %d", len(sc.Aggregate))
|
||||||
|
}
|
||||||
|
copy(s[params.SyncCommitteeSize*params.BLSPubkeySize:], sc.Aggregate[:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root calculates the root hash of the binary tree representation of a sync
|
||||||
|
// committee provided in serialized format.
|
||||||
|
//
|
||||||
|
// TODO(zsfelfoldi): Get rid of this when SSZ encoding lands.
|
||||||
|
func (s *SerializedSyncCommittee) Root() common.Hash {
|
||||||
|
var (
|
||||||
|
hasher = sha256.New()
|
||||||
|
padding [64 - params.BLSPubkeySize]byte
|
||||||
|
data [params.SyncCommitteeSize]common.Hash
|
||||||
|
l = params.SyncCommitteeSize
|
||||||
|
)
|
||||||
|
for i := range data {
|
||||||
|
hasher.Reset()
|
||||||
|
hasher.Write(s[i*params.BLSPubkeySize : (i+1)*params.BLSPubkeySize])
|
||||||
|
hasher.Write(padding[:])
|
||||||
|
hasher.Sum(data[i][:0])
|
||||||
|
}
|
||||||
|
for l > 1 {
|
||||||
|
for i := 0; i < l/2; i++ {
|
||||||
|
hasher.Reset()
|
||||||
|
hasher.Write(data[i*2][:])
|
||||||
|
hasher.Write(data[i*2+1][:])
|
||||||
|
hasher.Sum(data[i][:0])
|
||||||
|
}
|
||||||
|
l /= 2
|
||||||
|
}
|
||||||
|
hasher.Reset()
|
||||||
|
hasher.Write(s[SerializedSyncCommitteeSize-params.BLSPubkeySize : SerializedSyncCommitteeSize])
|
||||||
|
hasher.Write(padding[:])
|
||||||
|
hasher.Sum(data[1][:0])
|
||||||
|
hasher.Reset()
|
||||||
|
hasher.Write(data[0][:])
|
||||||
|
hasher.Write(data[1][:])
|
||||||
|
hasher.Sum(data[0][:0])
|
||||||
|
return data[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize splits open the pubkeys into proper BLS key types.
|
||||||
|
func (s *SerializedSyncCommittee) Deserialize() (*SyncCommittee, error) {
|
||||||
|
sc := new(SyncCommittee)
|
||||||
|
for i := 0; i <= params.SyncCommitteeSize; i++ {
|
||||||
|
key := new(bls.Pubkey)
|
||||||
|
|
||||||
|
var bytes [params.BLSPubkeySize]byte
|
||||||
|
copy(bytes[:], s[i*params.BLSPubkeySize:(i+1)*params.BLSPubkeySize])
|
||||||
|
|
||||||
|
if err := key.Deserialize(&bytes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if i < params.SyncCommitteeSize {
|
||||||
|
sc.keys[i] = key
|
||||||
|
} else {
|
||||||
|
sc.aggregate = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncCommittee is a set of sync committee signer pubkeys and the aggregate key.
|
||||||
|
//
|
||||||
|
// See data structure definition here:
|
||||||
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#syncaggregate
|
||||||
|
type SyncCommittee struct {
|
||||||
|
keys [params.SyncCommitteeSize]*bls.Pubkey
|
||||||
|
aggregate *bls.Pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifySignature returns true if the given sync aggregate is a valid signature
|
||||||
|
// or the given hash.
|
||||||
|
func (sc *SyncCommittee) VerifySignature(signingRoot common.Hash, signature *SyncAggregate) bool {
|
||||||
|
var (
|
||||||
|
sig bls.Signature
|
||||||
|
keys = make([]*bls.Pubkey, 0, params.SyncCommitteeSize)
|
||||||
|
)
|
||||||
|
if err := sig.Deserialize(&signature.Signature); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, key := range sc.keys {
|
||||||
|
if signature.Signers[i/8]&(byte(1)<<(i%8)) != 0 {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bls.FastAggregateVerify(keys, signingRoot[:], &sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type SyncAggregate -field-override syncAggregateMarshaling -out gen_syncaggregate_json.go
|
||||||
|
|
||||||
|
// SyncAggregate represents an aggregated BLS signature with Signers referring
|
||||||
|
// to a subset of the corresponding sync committee.
|
||||||
|
//
|
||||||
|
// See data structure definition here:
|
||||||
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#syncaggregate
|
||||||
|
type SyncAggregate struct {
|
||||||
|
Signers [params.SyncCommitteeBitmaskSize]byte `gencodec:"required" json:"sync_committee_bits"`
|
||||||
|
Signature [params.BLSSignatureSize]byte `gencodec:"required" json:"sync_committee_signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type syncAggregateMarshaling struct {
|
||||||
|
Signers hexutil.Bytes
|
||||||
|
Signature hexutil.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignerCount returns the number of signers in the aggregate signature.
|
||||||
|
func (s *SyncAggregate) SignerCount() int {
|
||||||
|
var count int
|
||||||
|
for _, v := range s.Signers {
|
||||||
|
count += bits.OnesCount8(v)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
176
beacon/types/config.go
Normal file
176
beacon/types/config.go
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/merkle"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// syncCommitteeDomain specifies the signatures specific use to avoid clashes
|
||||||
|
// across signing different data structures.
|
||||||
|
const syncCommitteeDomain = 7
|
||||||
|
|
||||||
|
// Fork describes a single beacon chain fork and also stores the calculated
|
||||||
|
// signature domain used after this fork.
|
||||||
|
type Fork struct {
|
||||||
|
// Name of the fork in the chain config (config.yaml) file{
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Epoch when given fork version is activated
|
||||||
|
Epoch uint64
|
||||||
|
|
||||||
|
// Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
|
||||||
|
Version []byte
|
||||||
|
|
||||||
|
// calculated by computeDomain, based on fork version and genesis validators root
|
||||||
|
domain merkle.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeDomain returns the signature domain based on the given fork version
|
||||||
|
// and genesis validator set root.
|
||||||
|
func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) {
|
||||||
|
var (
|
||||||
|
hasher = sha256.New()
|
||||||
|
forkVersion32 merkle.Value
|
||||||
|
forkDataRoot merkle.Value
|
||||||
|
)
|
||||||
|
copy(forkVersion32[:], f.Version)
|
||||||
|
hasher.Write(forkVersion32[:])
|
||||||
|
hasher.Write(genesisValidatorsRoot[:])
|
||||||
|
hasher.Sum(forkDataRoot[:0])
|
||||||
|
|
||||||
|
f.domain[0] = syncCommitteeDomain
|
||||||
|
copy(f.domain[4:], forkDataRoot[:28])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forks is the list of all beacon chain forks in the chain configuration.
|
||||||
|
type Forks []*Fork
|
||||||
|
|
||||||
|
// domain returns the signature domain for the given epoch (assumes that domains
|
||||||
|
// have already been calculated).
|
||||||
|
func (f Forks) domain(epoch uint64) (merkle.Value, error) {
|
||||||
|
for i := len(f) - 1; i >= 0; i-- {
|
||||||
|
if epoch >= f[i].Epoch {
|
||||||
|
return f[i].domain, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigningRoot calculates the signing root of the given header.
|
||||||
|
func (f Forks) SigningRoot(header Header) (common.Hash, error) {
|
||||||
|
domain, err := f.domain(header.Epoch())
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
signingRoot common.Hash
|
||||||
|
headerHash = header.Hash()
|
||||||
|
hasher = sha256.New()
|
||||||
|
)
|
||||||
|
hasher.Write(headerHash[:])
|
||||||
|
hasher.Write(domain[:])
|
||||||
|
hasher.Sum(signingRoot[:0])
|
||||||
|
|
||||||
|
return signingRoot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Forks) Len() int { return len(f) }
|
||||||
|
func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||||
|
func (f Forks) Less(i, j int) bool { return f[i].Epoch < f[j].Epoch }
|
||||||
|
|
||||||
|
// ChainConfig contains the beacon chain configuration.
|
||||||
|
type ChainConfig struct {
|
||||||
|
GenesisTime uint64 // Unix timestamp of slot 0
|
||||||
|
GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation
|
||||||
|
Forks Forks
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFork adds a new item to the list of forks.
|
||||||
|
func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
|
||||||
|
fork := &Fork{
|
||||||
|
Name: name,
|
||||||
|
Epoch: epoch,
|
||||||
|
Version: version,
|
||||||
|
}
|
||||||
|
fork.computeDomain(c.GenesisValidatorsRoot)
|
||||||
|
|
||||||
|
c.Forks = append(c.Forks, fork)
|
||||||
|
sort.Sort(c.Forks)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadForks parses the beacon chain configuration file (config.yaml) and extracts
|
||||||
|
// the list of forks.
|
||||||
|
func (c *ChainConfig) LoadForks(path string) error {
|
||||||
|
file, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read beacon chain config file: %v", err)
|
||||||
|
}
|
||||||
|
config := make(map[string]string)
|
||||||
|
if err := yaml.Unmarshal(file, &config); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse beacon chain config file: %v", err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
versions = make(map[string][]byte)
|
||||||
|
epochs = make(map[string]uint64)
|
||||||
|
)
|
||||||
|
epochs["GENESIS"] = 0
|
||||||
|
|
||||||
|
for key, value := range config {
|
||||||
|
if strings.HasSuffix(key, "_FORK_VERSION") {
|
||||||
|
name := key[:len(key)-len("_FORK_VERSION")]
|
||||||
|
if v, err := hexutil.Decode(value); err == nil {
|
||||||
|
versions[name] = v
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", value, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(key, "_FORK_EPOCH") {
|
||||||
|
name := key[:len(key)-len("_FORK_EPOCH")]
|
||||||
|
if v, err := strconv.ParseUint(value, 10, 64); err == nil {
|
||||||
|
epochs[name] = v
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", value, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for name, epoch := range epochs {
|
||||||
|
if version, ok := versions[name]; ok {
|
||||||
|
delete(versions, name)
|
||||||
|
c.AddFork(name, epoch, version)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("fork id missing for %q in beacon chain config file", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for name := range versions {
|
||||||
|
return fmt.Errorf("epoch number missing for fork %q in beacon chain config file", name)
|
||||||
|
}
|
||||||
|
sort.Sort(c.Forks)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
66
beacon/types/gen_header_json.go
Normal file
66
beacon/types/gen_header_json.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*headerMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
|
type Header struct {
|
||||||
|
Slot common.Decimal `gencodec:"required" json:"slot"`
|
||||||
|
ProposerIndex common.Decimal `gencodec:"required" json:"proposer_index"`
|
||||||
|
ParentRoot common.Hash `gencodec:"required" json:"parent_root"`
|
||||||
|
StateRoot common.Hash `gencodec:"required" json:"state_root"`
|
||||||
|
BodyRoot common.Hash `gencodec:"required" json:"body_root"`
|
||||||
|
}
|
||||||
|
var enc Header
|
||||||
|
enc.Slot = common.Decimal(h.Slot)
|
||||||
|
enc.ProposerIndex = common.Decimal(h.ProposerIndex)
|
||||||
|
enc.ParentRoot = h.ParentRoot
|
||||||
|
enc.StateRoot = h.StateRoot
|
||||||
|
enc.BodyRoot = h.BodyRoot
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
|
type Header struct {
|
||||||
|
Slot *common.Decimal `gencodec:"required" json:"slot"`
|
||||||
|
ProposerIndex *common.Decimal `gencodec:"required" json:"proposer_index"`
|
||||||
|
ParentRoot *common.Hash `gencodec:"required" json:"parent_root"`
|
||||||
|
StateRoot *common.Hash `gencodec:"required" json:"state_root"`
|
||||||
|
BodyRoot *common.Hash `gencodec:"required" json:"body_root"`
|
||||||
|
}
|
||||||
|
var dec Header
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.Slot == nil {
|
||||||
|
return errors.New("missing required field 'slot' for Header")
|
||||||
|
}
|
||||||
|
h.Slot = uint64(*dec.Slot)
|
||||||
|
if dec.ProposerIndex == nil {
|
||||||
|
return errors.New("missing required field 'proposer_index' for Header")
|
||||||
|
}
|
||||||
|
h.ProposerIndex = uint64(*dec.ProposerIndex)
|
||||||
|
if dec.ParentRoot == nil {
|
||||||
|
return errors.New("missing required field 'parent_root' for Header")
|
||||||
|
}
|
||||||
|
h.ParentRoot = *dec.ParentRoot
|
||||||
|
if dec.StateRoot == nil {
|
||||||
|
return errors.New("missing required field 'state_root' for Header")
|
||||||
|
}
|
||||||
|
h.StateRoot = *dec.StateRoot
|
||||||
|
if dec.BodyRoot == nil {
|
||||||
|
return errors.New("missing required field 'body_root' for Header")
|
||||||
|
}
|
||||||
|
h.BodyRoot = *dec.BodyRoot
|
||||||
|
return nil
|
||||||
|
}
|
||||||
51
beacon/types/gen_syncaggregate_json.go
Normal file
51
beacon/types/gen_syncaggregate_json.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*syncAggregateMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (s SyncAggregate) MarshalJSON() ([]byte, error) {
|
||||||
|
type SyncAggregate struct {
|
||||||
|
Signers hexutil.Bytes `gencodec:"required" json:"sync_committee_bits"`
|
||||||
|
Signature hexutil.Bytes `gencodec:"required" json:"sync_committee_signature"`
|
||||||
|
}
|
||||||
|
var enc SyncAggregate
|
||||||
|
enc.Signers = s.Signers[:]
|
||||||
|
enc.Signature = s.Signature[:]
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (s *SyncAggregate) UnmarshalJSON(input []byte) error {
|
||||||
|
type SyncAggregate struct {
|
||||||
|
Signers *hexutil.Bytes `gencodec:"required" json:"sync_committee_bits"`
|
||||||
|
Signature *hexutil.Bytes `gencodec:"required" json:"sync_committee_signature"`
|
||||||
|
}
|
||||||
|
var dec SyncAggregate
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.Signers == nil {
|
||||||
|
return errors.New("missing required field 'sync_committee_bits' for SyncAggregate")
|
||||||
|
}
|
||||||
|
if len(*dec.Signers) != len(s.Signers) {
|
||||||
|
return errors.New("field 'sync_committee_bits' has wrong length, need 64 items")
|
||||||
|
}
|
||||||
|
copy(s.Signers[:], *dec.Signers)
|
||||||
|
if dec.Signature == nil {
|
||||||
|
return errors.New("missing required field 'sync_committee_signature' for SyncAggregate")
|
||||||
|
}
|
||||||
|
if len(*dec.Signature) != len(s.Signature) {
|
||||||
|
return errors.New("field 'sync_committee_signature' has wrong length, need 96 items")
|
||||||
|
}
|
||||||
|
copy(s.Signature[:], *dec.Signature)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
121
beacon/types/header.go
Normal file
121
beacon/types/header.go
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package types implements a few types of the beacon chain for light client usage.
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/merkle"
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/params"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
|
||||||
|
|
||||||
|
const (
|
||||||
|
headerIndexSlot = 8
|
||||||
|
headerIndexProposerIndex = 9
|
||||||
|
headerIndexParentRoot = 10
|
||||||
|
headerIndexStateRoot = 11
|
||||||
|
headerIndexBodyRoot = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// Header defines a beacon header.
|
||||||
|
//
|
||||||
|
// See data structure definition here:
|
||||||
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
|
||||||
|
type Header struct {
|
||||||
|
// Monotonically increasing slot number for the beacon block (may be gapped)
|
||||||
|
Slot uint64 `gencodec:"required" json:"slot"`
|
||||||
|
|
||||||
|
// Index into the validator table who created the beacon block
|
||||||
|
ProposerIndex uint64 `gencodec:"required" json:"proposer_index"`
|
||||||
|
|
||||||
|
// SSZ hash of the parent beacon header
|
||||||
|
ParentRoot common.Hash `gencodec:"required" json:"parent_root"`
|
||||||
|
|
||||||
|
// SSZ hash of the beacon state (https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#beacon-state)
|
||||||
|
StateRoot common.Hash `gencodec:"required" json:"state_root"`
|
||||||
|
|
||||||
|
// SSZ hash of the beacon block body (https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#beaconblockbody)
|
||||||
|
BodyRoot common.Hash `gencodec:"required" json:"body_root"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// headerMarshaling is a field type overrides for gencodec.
|
||||||
|
type headerMarshaling struct {
|
||||||
|
Slot common.Decimal
|
||||||
|
ProposerIndex common.Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash calculates the block root of the header.
|
||||||
|
//
|
||||||
|
// TODO(zsfelfoldi): Remove this when an SSZ encoder lands.
|
||||||
|
func (h *Header) Hash() common.Hash {
|
||||||
|
var values [16]merkle.Value // values corresponding to indices 8 to 15 of the beacon header tree
|
||||||
|
binary.LittleEndian.PutUint64(values[headerIndexSlot][:8], h.Slot)
|
||||||
|
binary.LittleEndian.PutUint64(values[headerIndexProposerIndex][:8], h.ProposerIndex)
|
||||||
|
values[headerIndexParentRoot] = merkle.Value(h.ParentRoot)
|
||||||
|
values[headerIndexStateRoot] = merkle.Value(h.StateRoot)
|
||||||
|
values[headerIndexBodyRoot] = merkle.Value(h.BodyRoot)
|
||||||
|
hasher := sha256.New()
|
||||||
|
for i := 7; i > 0; i-- {
|
||||||
|
hasher.Reset()
|
||||||
|
hasher.Write(values[i*2][:])
|
||||||
|
hasher.Write(values[i*2+1][:])
|
||||||
|
hasher.Sum(values[i][:0])
|
||||||
|
}
|
||||||
|
return common.Hash(values[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Epoch returns the epoch the header belongs to.
|
||||||
|
func (h *Header) Epoch() uint64 {
|
||||||
|
return h.Slot / params.EpochLength
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncPeriod returns the sync period the header belongs to.
|
||||||
|
func (h *Header) SyncPeriod() uint64 {
|
||||||
|
return SyncPeriod(h.Slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncPeriodStart returns the first slot of the given period.
|
||||||
|
func SyncPeriodStart(period uint64) uint64 {
|
||||||
|
return period * params.SyncPeriodLength
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncPeriod returns the sync period that the given slot belongs to.
|
||||||
|
func SyncPeriod(slot uint64) uint64 {
|
||||||
|
return slot / params.SyncPeriodLength
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedHeader represents a beacon header signed by a sync committee.
|
||||||
|
//
|
||||||
|
// This structure is created from either an optimistic update or an instant update:
|
||||||
|
// - https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate
|
||||||
|
// - https://github.com/zsfelfoldi/beacon-APIs/blob/instant_update/apis/beacon/light_client/instant_update.yaml
|
||||||
|
type SignedHeader struct {
|
||||||
|
// Beacon header being signed
|
||||||
|
Header Header
|
||||||
|
|
||||||
|
// Sync committee BLS signature aggregate
|
||||||
|
Signature SyncAggregate
|
||||||
|
|
||||||
|
// Slot in which the signature has been created (newer than Header.Slot,
|
||||||
|
// determines the signing sync committee)
|
||||||
|
SignatureSlot uint64
|
||||||
|
}
|
||||||
118
beacon/types/update.go
Normal file
118
beacon/types/update.go
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/merkle"
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/params"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LightClientUpdate is a proof of the next sync committee root based on a header
|
||||||
|
// signed by the sync committee of the given period. Optionally, the update can
|
||||||
|
// prove quasi-finality by the signed header referring to a previous, finalized
|
||||||
|
// header from the same period, and the finalized header referring to the next
|
||||||
|
// sync committee root.
|
||||||
|
//
|
||||||
|
// See data structure definition here:
|
||||||
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientupdate
|
||||||
|
type LightClientUpdate struct {
|
||||||
|
AttestedHeader SignedHeader // Arbitrary header out of the period signed by the sync committee
|
||||||
|
NextSyncCommitteeRoot common.Hash // Sync committee of the next period advertised in the current one
|
||||||
|
NextSyncCommitteeBranch merkle.Values // Proof for the next period's sync committee
|
||||||
|
|
||||||
|
FinalizedHeader *Header `rlp:"nil"` // Optional header to announce a point of finality
|
||||||
|
FinalityBranch merkle.Values // Proof for the announced finality
|
||||||
|
|
||||||
|
score *UpdateScore // Weight of the update to compare between competing ones
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate verifies the validity of the update.
|
||||||
|
func (update *LightClientUpdate) Validate() error {
|
||||||
|
period := update.AttestedHeader.Header.SyncPeriod()
|
||||||
|
if SyncPeriod(update.AttestedHeader.SignatureSlot) != period {
|
||||||
|
return errors.New("signature slot and signed header are from different periods")
|
||||||
|
}
|
||||||
|
if update.FinalizedHeader != nil {
|
||||||
|
if update.FinalizedHeader.SyncPeriod() != period {
|
||||||
|
return errors.New("finalized header is from different period")
|
||||||
|
}
|
||||||
|
if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexFinalBlock, update.FinalityBranch, merkle.Value(update.FinalizedHeader.Hash())); err != nil {
|
||||||
|
return fmt.Errorf("invalid finalized header proof: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexNextSyncCommittee, update.NextSyncCommitteeBranch, merkle.Value(update.NextSyncCommitteeRoot)); err != nil {
|
||||||
|
return fmt.Errorf("invalid next sync committee proof: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Score returns the UpdateScore describing the proof strength of the update
|
||||||
|
// Note: thread safety can be ensured by always calling Score on a newly received
|
||||||
|
// or decoded update before making it potentially available for other threads
|
||||||
|
func (update *LightClientUpdate) Score() UpdateScore {
|
||||||
|
if update.score == nil {
|
||||||
|
update.score = &UpdateScore{
|
||||||
|
SignerCount: uint32(update.AttestedHeader.Signature.SignerCount()),
|
||||||
|
SubPeriodIndex: uint32(update.AttestedHeader.Header.Slot & 0x1fff),
|
||||||
|
FinalizedHeader: update.FinalizedHeader != nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *update.score
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateScore allows the comparison between updates at the same period in order
|
||||||
|
// to find the best update chain that provides the strongest proof of being canonical.
|
||||||
|
//
|
||||||
|
// UpdateScores have a tightly packed binary encoding format for efficient p2p
|
||||||
|
// protocol transmission. Each UpdateScore is encoded in 3 bytes.
|
||||||
|
// When interpreted as a 24 bit little indian unsigned integer:
|
||||||
|
// - the lowest 10 bits contain the number of signers in the header signature aggregate
|
||||||
|
// - the next 13 bits contain the "sub-period index" which is he signed header's
|
||||||
|
// slot modulo params.SyncPeriodLength (which is correlated with the risk of the chain being
|
||||||
|
// re-orged before the previous period boundary in case of non-finalized updates)
|
||||||
|
// - the highest bit is set when the update is finalized (meaning that the finality
|
||||||
|
// header referenced by the signed header is in the same period as the signed
|
||||||
|
// header, making reorgs before the period boundary impossible
|
||||||
|
type UpdateScore struct {
|
||||||
|
SignerCount uint32 // number of signers in the header signature aggregate
|
||||||
|
SubPeriodIndex uint32 // signed header's slot modulo params.SyncPeriodLength
|
||||||
|
FinalizedHeader bool // update is considered finalized if has finalized header from the same period and 2/3 signatures
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalized returns true if the update has a header signed by at least 2/3 of
|
||||||
|
// the committee, referring to a finalized header that refers to the next sync
|
||||||
|
// committee. This condition is a close approximation of the actual finality
|
||||||
|
// condition that can only be verified by full beacon nodes.
|
||||||
|
func (u *UpdateScore) finalized() bool {
|
||||||
|
return u.FinalizedHeader && u.SignerCount >= params.SyncCommitteeSupermajority
|
||||||
|
}
|
||||||
|
|
||||||
|
// BetterThan returns true if update u is considered better than w.
|
||||||
|
func (u UpdateScore) BetterThan(w UpdateScore) bool {
|
||||||
|
var (
|
||||||
|
uFinalized = u.finalized()
|
||||||
|
wFinalized = w.finalized()
|
||||||
|
)
|
||||||
|
if uFinalized != wFinalized {
|
||||||
|
return uFinalized
|
||||||
|
}
|
||||||
|
return u.SignerCount > w.SignerCount
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
# This file contains sha256 checksums of optional build dependencies.
|
# This file contains sha256 checksums of optional build dependencies.
|
||||||
|
|
||||||
38f423db4cc834883f2b52344282fa7a39fbb93650dc62a11fdf0be6409bdad6 go1.18.src.tar.gz
|
2c5ee9c9ec1e733b0dbbc2bdfed3f62306e51d8172bf38f4f4e542b27520f597 go1.20.7.src.tar.gz
|
||||||
70bb4a066997535e346c8bfa3e0dfe250d61100b17ccc5676274642447834969 go1.18.darwin-amd64.tar.gz
|
785170eab380a8985d53896808b0a71336d0ea60e0a26099b4ccec77798b1cf4 go1.20.7.darwin-amd64.tar.gz
|
||||||
9cab6123af9ffade905525d79fc9ee76651e716c85f1f215872b5f2976782480 go1.18.darwin-arm64.tar.gz
|
eea1e7e4c2f75c72629050e6a6c7c46c446d64056732a7787fb3ba16ace1982e go1.20.7.darwin-arm64.tar.gz
|
||||||
e63492d4f38487331518eb4b50e670d853bb8d67e88596269af84bb9aca0b381 go1.18.freebsd-386.tar.gz
|
d8cff0357ac24eb06f3f280535397eeaacf95611d29f9b2abc3060f3d6dce3b4 go1.20.7.freebsd-386.tar.gz
|
||||||
01cd67bbc12e659ff236ecebde1806f76452f7ca145c172d5ecdbf4f4803daae go1.18.freebsd-amd64.tar.gz
|
26918dcebf474a9e81ccf9f648cdf36968dfb76b481518cf615d78455dda4416 go1.20.7.freebsd-amd64.tar.gz
|
||||||
1c04cf4440b323a66328e0df95d409f955b9b475e58eae235fdd3d1f1cf02f4f go1.18.linux-386.tar.gz
|
ddb48145f05bda2f4617a22c979d4e94b22802cdb1a1fde1b1974e733b26f091 go1.20.7.linux-386.tar.gz
|
||||||
e85278e98f57cdb150fe8409e6e5df5343ecb13cebf03a5d5ff12bd55a80264f go1.18.linux-amd64.tar.gz
|
f0a87f1bcae91c4b69f8dc2bc6d7e6bfcd7524fceec130af525058c0c17b1b44 go1.20.7.linux-amd64.tar.gz
|
||||||
7ac7b396a691e588c5fb57687759e6c4db84a2a3bbebb0765f4b38e5b1c5b00e go1.18.linux-arm64.tar.gz
|
44781ae3b153c3b07651d93b6bc554e835a36e2d72a696281c1e4dad9efffe43 go1.20.7.linux-arm64.tar.gz
|
||||||
a80fa43d1f4575fb030adbfbaa94acd860c6847820764eecb06c63b7c103612b go1.18.linux-armv6l.tar.gz
|
7cc231b415b94f2f7065870a73f67dd2b0ec12b5a98052b7ee0121c42bc04f8d go1.20.7.linux-armv6l.tar.gz
|
||||||
070351edac192483c074b38d08ec19251a83f8210765a532a84c3dcf8aec04d8 go1.18.linux-ppc64le.tar.gz
|
6318a1db307c12b8afe68808bd6fae4fba1e558a85b958216096869ed506dcb3 go1.20.7.linux-ppc64le.tar.gz
|
||||||
ea265f5e62fcaf941d53f0cdb81222d9668e1672a0d39d992f16ff0e87c0ee6b go1.18.linux-s390x.tar.gz
|
26aea2ede8722ceecbd9db883328a8d963136fd96c11dacc356c44c4c19c6515 go1.20.7.linux-s390x.tar.gz
|
||||||
e23fd2a0509690fe7e63b2b1bcd4c39ed57b46ccde76f35dc0d16ca7fdbc5aaa go1.18.windows-386.zip
|
5b0ef6f58d3e04d6cc003aa98e9172f41ba9e091b1c98e7339b41c4c87fb78a1 go1.20.7.windows-386.zip
|
||||||
65c5c0c709a7ca1b357091b10b795b439d8b50e579d3893edab4c7e9b384f435 go1.18.windows-amd64.zip
|
736dc6c7fcab1c96b682c8c93e38d7e371e62a17d34cb2c37d451a1147f66af9 go1.20.7.windows-amd64.zip
|
||||||
1c454eb60c64d481965a165c623ff1ed6cf32d68c6b31f36069c8768d908f093 go1.18.windows-arm64.zip
|
fc6f79c1e1ed9e506c65f2112ac4e387479916f1accb0d046a6a19ff6938baa5 go1.20.7.windows-arm64.zip
|
||||||
|
|
||||||
03c181fc1bb29ea3e73cbb23399c43b081063833a7cf7554b94e5a98308df53e golangci-lint-1.45.2-linux-riscv64.deb
|
03c181fc1bb29ea3e73cbb23399c43b081063833a7cf7554b94e5a98308df53e golangci-lint-1.45.2-linux-riscv64.deb
|
||||||
08a50bbbf451ede6d5354179eb3e14a5634e156dfa92cb9a2606f855a637e35b golangci-lint-1.45.2-linux-ppc64le.rpm
|
08a50bbbf451ede6d5354179eb3e14a5634e156dfa92cb9a2606f855a637e35b golangci-lint-1.45.2-linux-ppc64le.rpm
|
||||||
|
|
|
||||||
24
build/ci.go
24
build/ci.go
|
|
@ -139,7 +139,15 @@ var (
|
||||||
// This is the version of Go that will be downloaded by
|
// This is the version of Go that will be downloaded by
|
||||||
//
|
//
|
||||||
// go run ci.go install -dlgo
|
// go run ci.go install -dlgo
|
||||||
dlgoVersion = "1.20"
|
dlgoVersion = "1.20.7"
|
||||||
|
|
||||||
|
// This is the version of Go that will be used to bootstrap the PPA builder.
|
||||||
|
//
|
||||||
|
// This version is fine to be old and full of security holes, we just use it
|
||||||
|
// to build the latest Go. Don't change it. If it ever becomes insufficient,
|
||||||
|
// we need to switch over to a recursive builder to jumpt across supported
|
||||||
|
// versions.
|
||||||
|
gobootVersion = "1.19.6"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
||||||
|
|
@ -192,6 +200,7 @@ func doInstall(cmdline []string) {
|
||||||
staticlink = flag.Bool("static", false, "Create statically-linked executable")
|
staticlink = flag.Bool("static", false, "Create statically-linked executable")
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
env := build.Env()
|
||||||
|
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||||
|
|
@ -203,8 +212,12 @@ func doInstall(cmdline []string) {
|
||||||
// Disable CLI markdown doc generation in release builds.
|
// Disable CLI markdown doc generation in release builds.
|
||||||
buildTags := []string{"urfave_cli_no_docs"}
|
buildTags := []string{"urfave_cli_no_docs"}
|
||||||
|
|
||||||
|
// Enable linking the CKZG library since we can make it work with additional flags.
|
||||||
|
if env.UbuntuVersion != "trusty" {
|
||||||
|
buildTags = append(buildTags, "ckzg")
|
||||||
|
}
|
||||||
|
|
||||||
// Configure the build.
|
// Configure the build.
|
||||||
env := build.Env()
|
|
||||||
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
|
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
|
||||||
|
|
||||||
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
||||||
|
|
@ -213,7 +226,6 @@ func doInstall(cmdline []string) {
|
||||||
if env.CI && runtime.GOARCH == "arm64" {
|
if env.CI && runtime.GOARCH == "arm64" {
|
||||||
gobuild.Args = append(gobuild.Args, "-p", "1")
|
gobuild.Args = append(gobuild.Args, "-p", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use -trimpath to avoid leaking local paths into the built executables.
|
// We use -trimpath to avoid leaking local paths into the built executables.
|
||||||
gobuild.Args = append(gobuild.Args, "-trimpath")
|
gobuild.Args = append(gobuild.Args, "-trimpath")
|
||||||
|
|
||||||
|
|
@ -293,6 +305,12 @@ func doTest(cmdline []string) {
|
||||||
}
|
}
|
||||||
gotest := tc.Go("test")
|
gotest := tc.Go("test")
|
||||||
|
|
||||||
|
// CI needs a bit more time for the statetests (default 10m).
|
||||||
|
gotest.Args = append(gotest.Args, "-timeout=20m")
|
||||||
|
|
||||||
|
// Enable CKZG backend in CI.
|
||||||
|
gotest.Args = append(gotest.Args, "-tags=ckzg")
|
||||||
|
|
||||||
// Test a single package at a time. CI builders are slow
|
// Test a single package at a time. CI builders are slow
|
||||||
// and some tests run into timeouts under load.
|
// and some tests run into timeouts under load.
|
||||||
gotest.Args = append(gotest.Args, "-p", "1")
|
gotest.Args = append(gotest.Args, "-p", "1")
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ override_dh_auto_build:
|
||||||
mv .mod $(GOPATH)/pkg/mod
|
mv .mod $(GOPATH)/pkg/mod
|
||||||
|
|
||||||
# A fresh Go was built, all dependency downloads faked, hope build works now
|
# A fresh Go was built, all dependency downloads faked, hope build works now
|
||||||
../.go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
|
../.go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} -ubuntu {{.Distro}}
|
||||||
|
|
||||||
override_dh_auto_test:
|
override_dh_auto_test:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,13 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -152,13 +153,6 @@ func (i info) gpl() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// authors implements the sort.Interface for strings in case-insensitive mode.
|
|
||||||
type authors []string
|
|
||||||
|
|
||||||
func (as authors) Len() int { return len(as) }
|
|
||||||
func (as authors) Less(i, j int) bool { return strings.ToLower(as[i]) < strings.ToLower(as[j]) }
|
|
||||||
func (as authors) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
files = getFiles()
|
files = getFiles()
|
||||||
|
|
@ -299,7 +293,9 @@ func writeAuthors(files []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Write sorted list of authors back to the file.
|
// Write sorted list of authors back to the file.
|
||||||
sort.Sort(authors(list))
|
slices.SortFunc(list, func(a, b string) bool {
|
||||||
|
return strings.ToLower(a) < strings.ToLower(b)
|
||||||
|
})
|
||||||
content := new(bytes.Buffer)
|
content := new(bytes.Buffer)
|
||||||
content.WriteString(authorsFileHeader)
|
content.WriteString(authorsFileHeader)
|
||||||
for _, a := range list {
|
for _, a := range list {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -112,22 +113,20 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("-ListenUDP: %v", err)
|
utils.Fatalf("-ListenUDP: %v", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
realaddr := conn.LocalAddr().(*net.UDPAddr)
|
|
||||||
if natm != nil {
|
|
||||||
if !realaddr.IP.IsLoopback() {
|
|
||||||
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
|
||||||
}
|
|
||||||
|
|
||||||
if ext, err := natm.ExternalIP(); err == nil {
|
|
||||||
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printNotice(&nodeKey.PublicKey, *realaddr)
|
|
||||||
|
|
||||||
db, _ := enode.OpenDB("")
|
db, _ := enode.OpenDB("")
|
||||||
ln := enode.NewLocalNode(db, nodeKey)
|
ln := enode.NewLocalNode(db, nodeKey)
|
||||||
|
|
||||||
|
listenerAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
if natm != nil && !listenerAddr.IP.IsLoopback() {
|
||||||
|
natAddr := doPortMapping(natm, ln, listenerAddr)
|
||||||
|
if natAddr != nil {
|
||||||
|
listenerAddr = natAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printNotice(&nodeKey.PublicKey, *listenerAddr)
|
||||||
cfg := discover.Config{
|
cfg := discover.Config{
|
||||||
PrivateKey: nodeKey,
|
PrivateKey: nodeKey,
|
||||||
NetRestrict: restrictList,
|
NetRestrict: restrictList,
|
||||||
|
|
@ -156,3 +155,61 @@ func printNotice(nodeKey *ecdsa.PublicKey, addr net.UDPAddr) {
|
||||||
fmt.Println("Note: you're using cmd/bootnode, a developer tool.")
|
fmt.Println("Note: you're using cmd/bootnode, a developer tool.")
|
||||||
fmt.Println("We recommend using a regular node as bootstrap node for production deployments.")
|
fmt.Println("We recommend using a regular node as bootstrap node for production deployments.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doPortMapping(natm nat.Interface, ln *enode.LocalNode, addr *net.UDPAddr) *net.UDPAddr {
|
||||||
|
const (
|
||||||
|
protocol = "udp"
|
||||||
|
name = "ethereum discovery"
|
||||||
|
)
|
||||||
|
newLogger := func(external int, internal int) log.Logger {
|
||||||
|
return log.New("proto", protocol, "extport", external, "intport", internal, "interface", natm)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
intport = addr.Port
|
||||||
|
extaddr = &net.UDPAddr{IP: addr.IP, Port: addr.Port}
|
||||||
|
mapTimeout = nat.DefaultMapTimeout
|
||||||
|
log = newLogger(addr.Port, intport)
|
||||||
|
)
|
||||||
|
addMapping := func() {
|
||||||
|
// Get the external address.
|
||||||
|
var err error
|
||||||
|
extaddr.IP, err = natm.ExternalIP()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Couldn't get external IP", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Create the mapping.
|
||||||
|
p, err := natm.AddMapping(protocol, extaddr.Port, intport, name, mapTimeout)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Couldn't add port mapping", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p != uint16(extaddr.Port) {
|
||||||
|
extaddr.Port = int(p)
|
||||||
|
log = newLogger(extaddr.Port, intport)
|
||||||
|
log.Info("NAT mapped alternative port")
|
||||||
|
} else {
|
||||||
|
log.Info("NAT mapped port")
|
||||||
|
}
|
||||||
|
// Update IP/port information of the local node.
|
||||||
|
ln.SetStaticIP(extaddr.IP)
|
||||||
|
ln.SetFallbackUDP(extaddr.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform mapping once, synchronously.
|
||||||
|
log.Info("Attempting port mapping")
|
||||||
|
addMapping()
|
||||||
|
|
||||||
|
// Refresh the mapping periodically.
|
||||||
|
go func() {
|
||||||
|
refresh := time.NewTimer(mapTimeout)
|
||||||
|
defer refresh.Stop()
|
||||||
|
for range refresh.C {
|
||||||
|
addMapping()
|
||||||
|
refresh.Reset(mapTimeout)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return extaddr
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
## Checkpoint-admin
|
|
||||||
|
|
||||||
Checkpoint-admin is a tool for updating checkpoint oracle status. It provides a series of functions including deploying checkpoint oracle contract, signing for new checkpoints, and updating checkpoints in the checkpoint oracle contract.
|
|
||||||
|
|
||||||
### Checkpoint
|
|
||||||
|
|
||||||
In the LES protocol, there is an important concept called checkpoint. In simple terms, whenever a certain number of blocks are generated on the blockchain, a new checkpoint is generated which contains some important information such as
|
|
||||||
|
|
||||||
* Block hash at checkpoint
|
|
||||||
* Canonical hash trie root at checkpoint
|
|
||||||
* Bloom trie root at checkpoint
|
|
||||||
|
|
||||||
*For a more detailed introduction to checkpoint, please see the LES [spec](https://github.com/ethereum/devp2p/blob/master/caps/les.md).*
|
|
||||||
|
|
||||||
Using this information, light clients can skip all historical block headers when synchronizing data and start synchronization from this checkpoint. Therefore, as long as the light client can obtain some latest and correct checkpoints, the amount of data and time for synchronization will be greatly reduced.
|
|
||||||
|
|
||||||
However, from a security perspective, the most critical step in a synchronization algorithm based on checkpoints is to determine whether the checkpoint used by the light client is correct. Otherwise, all blockchain data synchronized based on this checkpoint may be wrong. For this we provide two different ways to ensure the correctness of the checkpoint used by the light client.
|
|
||||||
|
|
||||||
#### Hardcoded checkpoint
|
|
||||||
|
|
||||||
There are several hardcoded checkpoints in the [source code](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L38) of the go-ethereum project. These checkpoints are updated by go-ethereum developers when new versions of software are released. Because light client users trust Geth developers to some extent, hardcoded checkpoints in the code can also be considered correct.
|
|
||||||
|
|
||||||
#### Checkpoint oracle
|
|
||||||
|
|
||||||
Hardcoded checkpoints can solve the problem of verifying the correctness of checkpoints (although this is a more centralized solution). But the pain point of this solution is that developers can only update checkpoints when a new version of software is released. In addition, light client users usually do not keep the Geth version they use always up to date. So hardcoded checkpoints used by users are generally stale. Therefore, it still needs to download a large amount of blockchain data during synchronization.
|
|
||||||
|
|
||||||
Checkpoint oracle is a more flexible solution. In simple terms, this is a smart contract that is deployed on the blockchain. The smart contract records several designated trusted signers. Whenever enough trusted signers have issued their signatures for the same checkpoint, it can be considered that the checkpoint has been authenticated by the signers. Checkpoints authenticated by trusted signers can be considered correct.
|
|
||||||
|
|
||||||
So this way, even without updating the software version, as long as the trusted signers regularly update the checkpoint in oracle on time, the light client can always use the latest and verified checkpoint for data synchronization.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
Checkpoint-admin is a command line tool designed for checkpoint oracle. Users can easily deploy contracts and update checkpoints through this tool.
|
|
||||||
|
|
||||||
#### Install
|
|
||||||
|
|
||||||
```shell
|
|
||||||
go get github.com/ethereum/go-ethereum/cmd/checkpoint-admin
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Deploy
|
|
||||||
|
|
||||||
Deploy checkpoint oracle contract. `--signers` indicates the specified trusted signer, and `--threshold` indicates the minimum number of signatures required by trusted signers to update a checkpoint.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
checkpoint-admin deploy --rpc <NODE_RPC_ENDPOINT> --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --signers <TRUSTED_SIGNER_LIST> --threshold 1
|
|
||||||
```
|
|
||||||
|
|
||||||
It is worth noting that checkpoint-admin only supports clef as a signer for transactions and plain text(checkpoint). For more clef usage, please see the clef [tutorial](https://geth.ethereum.org/docs/tools/clef/tutorial) .
|
|
||||||
|
|
||||||
#### Sign
|
|
||||||
|
|
||||||
Checkpoint-admin provides two different modes of signing. You can automatically obtain the current stable checkpoint and sign it interactively, and you can also use the information provided by the command line flags to sign checkpoint offline.
|
|
||||||
|
|
||||||
**Interactive mode**
|
|
||||||
|
|
||||||
```shell
|
|
||||||
checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --rpc <NODE_RPC_ENDPOINT>
|
|
||||||
```
|
|
||||||
|
|
||||||
*It is worth noting that the connected Geth node can be a fullnode or a light client. If it is fullnode, you must enable the LES protocol. E.G. add `--light.serv 50` to the startup command line flags*.
|
|
||||||
|
|
||||||
**Offline mode**
|
|
||||||
|
|
||||||
```shell
|
|
||||||
checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --index <CHECKPOINT_INDEX> --hash <CHECKPOINT_HASH> --oracle <CHECKPOINT_ORACLE_ADDRESS>
|
|
||||||
```
|
|
||||||
|
|
||||||
*CHECKPOINT_HASH is obtained based on this [calculation method](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L251).*
|
|
||||||
|
|
||||||
#### Publish
|
|
||||||
|
|
||||||
Collect enough signatures from different trusted signers for the same checkpoint and submit them to oracle to update the "authenticated" checkpoint in the contract.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
checkpoint-admin publish --clef <CLEF_ENDPOINT> --rpc <NODE_RPC_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --index <CHECKPOINT_INDEX> --signatures <CHECKPOINT_SIGNATURE_LIST>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Status query
|
|
||||||
|
|
||||||
Check the latest status of checkpoint oracle.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
checkpoint-admin status --rpc <NODE_RPC_ENDPOINT>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enable checkpoint oracle in your private network
|
|
||||||
|
|
||||||
Currently, only the Ethereum mainnet and the default supported test networks (goerli) activate this feature. If you want to activate this feature in your private network, you can overwrite the relevant checkpoint oracle settings through the configuration file after deploying the oracle contract.
|
|
||||||
|
|
||||||
* Get your node configuration file `geth dumpconfig OTHER_COMMAND_LINE_OPTIONS > config.toml`
|
|
||||||
* Edit the configuration file and add the following information
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[Eth.CheckpointOracle]
|
|
||||||
Address = CHECKPOINT_ORACLE_ADDRESS
|
|
||||||
Signers = [TRUSTED_SIGNER_1, ..., TRUSTED_SIGNER_N]
|
|
||||||
Threshold = THRESHOLD
|
|
||||||
```
|
|
||||||
|
|
||||||
* Start geth with the modified configuration file
|
|
||||||
|
|
||||||
*In the private network, all fullnodes and light clients need to be started using the same checkpoint oracle settings.*
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/external"
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/contracts/checkpointoracle"
|
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// newClient creates a client with specified remote URL.
|
|
||||||
func newClient(ctx *cli.Context) *ethclient.Client {
|
|
||||||
client, err := ethclient.Dial(ctx.String(nodeURLFlag.Name))
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to connect to Ethereum node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
// newRPCClient creates a rpc client with specified node URL.
|
|
||||||
func newRPCClient(url string) *rpc.Client {
|
|
||||||
client, err := rpc.Dial(url)
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to connect to Ethereum node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
// getContractAddr retrieves the register contract address through
|
|
||||||
// rpc request.
|
|
||||||
func getContractAddr(client *rpc.Client) common.Address {
|
|
||||||
var addr string
|
|
||||||
if err := client.Call(&addr, "les_getCheckpointContractAddress"); err != nil {
|
|
||||||
utils.Fatalf("Failed to fetch checkpoint oracle address: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return common.HexToAddress(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCheckpoint retrieves the specified checkpoint or the latest one
|
|
||||||
// through rpc request.
|
|
||||||
func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoint {
|
|
||||||
var checkpoint *params.TrustedCheckpoint
|
|
||||||
|
|
||||||
if ctx.IsSet(indexFlag.Name) {
|
|
||||||
var result [3]string
|
|
||||||
|
|
||||||
index := uint64(ctx.Int64(indexFlag.Name))
|
|
||||||
if err := client.Call(&result, "les_getCheckpoint", index); err != nil {
|
|
||||||
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkpoint = ¶ms.TrustedCheckpoint{
|
|
||||||
SectionIndex: index,
|
|
||||||
SectionHead: common.HexToHash(result[0]),
|
|
||||||
CHTRoot: common.HexToHash(result[1]),
|
|
||||||
BloomRoot: common.HexToHash(result[2]),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var result [4]string
|
|
||||||
|
|
||||||
err := client.Call(&result, "les_latestCheckpoint")
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
index, err := strconv.ParseUint(result[0], 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to parse checkpoint index %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkpoint = ¶ms.TrustedCheckpoint{
|
|
||||||
SectionIndex: index,
|
|
||||||
SectionHead: common.HexToHash(result[1]),
|
|
||||||
CHTRoot: common.HexToHash(result[2]),
|
|
||||||
BloomRoot: common.HexToHash(result[3]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
// newContract creates a registrar contract instance with specified
|
|
||||||
// contract address or the default contracts for mainnet or testnet.
|
|
||||||
func newContract(client *rpc.Client) (common.Address, *checkpointoracle.CheckpointOracle) {
|
|
||||||
addr := getContractAddr(client)
|
|
||||||
if addr == (common.Address{}) {
|
|
||||||
utils.Fatalf("No specified registrar contract address")
|
|
||||||
}
|
|
||||||
|
|
||||||
contract, err := checkpointoracle.NewCheckpointOracle(addr, ethclient.NewClient(client))
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to setup registrar contract %s: %v", addr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addr, contract
|
|
||||||
}
|
|
||||||
|
|
||||||
// newClefSigner sets up a clef backend and returns a clef transaction signer.
|
|
||||||
func newClefSigner(ctx *cli.Context) *bind.TransactOpts {
|
|
||||||
clef, err := external.NewExternalSigner(ctx.String(clefURLFlag.Name))
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to create clef signer %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bind.NewClefTransactor(clef, accounts.Account{Address: common.HexToAddress(ctx.String(signerFlag.Name))})
|
|
||||||
}
|
|
||||||
|
|
@ -1,349 +0,0 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/contracts/checkpointoracle"
|
|
||||||
"github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var commandDeploy = &cli.Command{
|
|
||||||
Name: "deploy",
|
|
||||||
Usage: "Deploy a new checkpoint oracle contract",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
nodeURLFlag,
|
|
||||||
clefURLFlag,
|
|
||||||
signerFlag,
|
|
||||||
signersFlag,
|
|
||||||
thresholdFlag,
|
|
||||||
},
|
|
||||||
Action: deploy,
|
|
||||||
}
|
|
||||||
|
|
||||||
var commandSign = &cli.Command{
|
|
||||||
Name: "sign",
|
|
||||||
Usage: "Sign the checkpoint with the specified key",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
nodeURLFlag,
|
|
||||||
clefURLFlag,
|
|
||||||
signerFlag,
|
|
||||||
indexFlag,
|
|
||||||
hashFlag,
|
|
||||||
oracleFlag,
|
|
||||||
},
|
|
||||||
Action: sign,
|
|
||||||
}
|
|
||||||
|
|
||||||
var commandPublish = &cli.Command{
|
|
||||||
Name: "publish",
|
|
||||||
Usage: "Publish a checkpoint into the oracle",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
nodeURLFlag,
|
|
||||||
clefURLFlag,
|
|
||||||
signerFlag,
|
|
||||||
indexFlag,
|
|
||||||
signaturesFlag,
|
|
||||||
},
|
|
||||||
Action: publish,
|
|
||||||
}
|
|
||||||
|
|
||||||
// deploy deploys the checkpoint registrar contract.
|
|
||||||
//
|
|
||||||
// Note the network where the contract is deployed depends on
|
|
||||||
// the network where the connected node is located.
|
|
||||||
func deploy(ctx *cli.Context) error {
|
|
||||||
// Gather all the addresses that should be permitted to sign
|
|
||||||
var addrs []common.Address
|
|
||||||
|
|
||||||
for _, account := range strings.Split(ctx.String(signersFlag.Name), ",") {
|
|
||||||
if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) {
|
|
||||||
utils.Fatalf("Invalid account in --signers: '%s'", trimmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
addrs = append(addrs, common.HexToAddress(account))
|
|
||||||
}
|
|
||||||
// Retrieve and validate the signing threshold
|
|
||||||
needed := ctx.Int(thresholdFlag.Name)
|
|
||||||
if needed == 0 || needed > len(addrs) {
|
|
||||||
utils.Fatalf("Invalid signature threshold %d", needed)
|
|
||||||
}
|
|
||||||
// Print a summary to ensure the user understands what they're signing
|
|
||||||
fmt.Printf("Deploying new checkpoint oracle:\n\n")
|
|
||||||
|
|
||||||
for i, addr := range addrs {
|
|
||||||
fmt.Printf("Admin %d => %s\n", i+1, addr.Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\nSignatures needed to publish: %d\n", needed)
|
|
||||||
|
|
||||||
// setup clef signer, create an abigen transactor and an RPC client
|
|
||||||
transactor, client := newClefSigner(ctx), newClient(ctx)
|
|
||||||
|
|
||||||
// Deploy the checkpoint oracle
|
|
||||||
fmt.Println("Sending deploy request to Clef...")
|
|
||||||
|
|
||||||
oracle, tx, _, err := contract.DeployCheckpointOracle(transactor, client, addrs, big.NewInt(int64(params.CheckpointFrequency)),
|
|
||||||
big.NewInt(int64(params.CheckpointProcessConfirmations)), big.NewInt(int64(needed)))
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to deploy checkpoint oracle %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Deployed checkpoint oracle", "address", oracle, "tx", tx.Hash().Hex())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sign creates the signature for specific checkpoint
|
|
||||||
// with local key. Only contract admins have the permission to
|
|
||||||
// sign checkpoint.
|
|
||||||
func sign(ctx *cli.Context) error {
|
|
||||||
var (
|
|
||||||
offline bool // The indicator whether we sign checkpoint by offline.
|
|
||||||
chash common.Hash
|
|
||||||
cindex uint64
|
|
||||||
address common.Address
|
|
||||||
|
|
||||||
node *rpc.Client
|
|
||||||
oracle *checkpointoracle.CheckpointOracle
|
|
||||||
)
|
|
||||||
|
|
||||||
// nolint:nestif
|
|
||||||
if !ctx.IsSet(nodeURLFlag.Name) {
|
|
||||||
// Offline mode signing
|
|
||||||
offline = true
|
|
||||||
|
|
||||||
if !ctx.IsSet(hashFlag.Name) {
|
|
||||||
utils.Fatalf("Please specify the checkpoint hash (--hash) to sign in offline mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
chash = common.HexToHash(ctx.String(hashFlag.Name))
|
|
||||||
|
|
||||||
if !ctx.IsSet(indexFlag.Name) {
|
|
||||||
utils.Fatalf("Please specify checkpoint index (--index) to sign in offline mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
cindex = ctx.Uint64(indexFlag.Name)
|
|
||||||
|
|
||||||
if !ctx.IsSet(oracleFlag.Name) {
|
|
||||||
utils.Fatalf("Please specify oracle address (--oracle) to sign in offline mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
address = common.HexToAddress(ctx.String(oracleFlag.Name))
|
|
||||||
} else {
|
|
||||||
// Interactive mode signing, retrieve the data from the remote node
|
|
||||||
node = newRPCClient(ctx.String(nodeURLFlag.Name))
|
|
||||||
|
|
||||||
checkpoint := getCheckpoint(ctx, node)
|
|
||||||
chash, cindex, address = checkpoint.Hash(), checkpoint.SectionIndex, getContractAddr(node)
|
|
||||||
|
|
||||||
// Check the validity of checkpoint
|
|
||||||
reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
defer cancelFn()
|
|
||||||
|
|
||||||
head, err := ethclient.NewClient(node).HeaderByNumber(reqCtx, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
num := head.Number.Uint64()
|
|
||||||
if num < ((cindex+1)*params.CheckpointFrequency + params.CheckpointProcessConfirmations) {
|
|
||||||
utils.Fatalf("Invalid future checkpoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, oracle = newContract(node)
|
|
||||||
|
|
||||||
latest, _, h, err := oracle.Contract().GetLatestCheckpoint(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cindex < latest {
|
|
||||||
utils.Fatalf("Checkpoint is too old")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cindex == latest && (latest != 0 || h.Uint64() != 0) {
|
|
||||||
utils.Fatalf("Stale checkpoint, latest registered %d, given %d", latest, cindex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
signature string
|
|
||||||
signer string
|
|
||||||
)
|
|
||||||
// isAdmin checks whether the specified signer is admin.
|
|
||||||
isAdmin := func(addr common.Address) error {
|
|
||||||
signers, err := oracle.Contract().GetAllAdmin(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range signers {
|
|
||||||
if s == addr {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("signer %v is not the admin", addr.Hex())
|
|
||||||
}
|
|
||||||
// Print to the user the data thy are about to sign
|
|
||||||
fmt.Printf("Oracle => %s\n", address.Hex())
|
|
||||||
fmt.Printf("Index %4d => %s\n", cindex, chash.Hex())
|
|
||||||
|
|
||||||
// Sign checkpoint in clef mode.
|
|
||||||
signer = ctx.String(signerFlag.Name)
|
|
||||||
|
|
||||||
if !offline {
|
|
||||||
if err := isAdmin(common.HexToAddress(signer)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clef := newRPCClient(ctx.String(clefURLFlag.Name))
|
|
||||||
p := make(map[string]string)
|
|
||||||
buf := make([]byte, 8)
|
|
||||||
binary.BigEndian.PutUint64(buf, cindex)
|
|
||||||
|
|
||||||
p["address"] = address.Hex()
|
|
||||||
p["message"] = hexutil.Encode(append(buf, chash.Bytes()...))
|
|
||||||
|
|
||||||
fmt.Println("Sending signing request to Clef...")
|
|
||||||
|
|
||||||
if err := clef.Call(&signature, "account_signData", accounts.MimetypeDataWithValidator, signer, p); err != nil {
|
|
||||||
utils.Fatalf("Failed to sign checkpoint, err %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Signer => %s\n", signer)
|
|
||||||
fmt.Printf("Signature => %s\n", signature)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sighash calculates the hash of the data to sign for the checkpoint oracle.
|
|
||||||
func sighash(index uint64, oracle common.Address, hash common.Hash) []byte {
|
|
||||||
buf := make([]byte, 8)
|
|
||||||
binary.BigEndian.PutUint64(buf, index)
|
|
||||||
|
|
||||||
data := append([]byte{0x19, 0x00}, append(oracle[:], append(buf, hash[:]...)...)...)
|
|
||||||
|
|
||||||
return crypto.Keccak256(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ecrecover calculates the sender address from a sighash and signature combo.
|
|
||||||
func ecrecover(sighash []byte, sig []byte) common.Address {
|
|
||||||
sig[64] -= 27
|
|
||||||
defer func() { sig[64] += 27 }()
|
|
||||||
|
|
||||||
signer, err := crypto.SigToPub(sighash, sig)
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to recover sender from signature %x: %v", sig, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.PubkeyToAddress(*signer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// publish registers the specified checkpoint which generated by connected node
|
|
||||||
// with a authorised private key.
|
|
||||||
func publish(ctx *cli.Context) error {
|
|
||||||
// Print the checkpoint oracle's current status to make sure we're interacting
|
|
||||||
// with the correct network and contract.
|
|
||||||
status(ctx)
|
|
||||||
|
|
||||||
// Gather the signatures from the CLI
|
|
||||||
var sigs [][]byte
|
|
||||||
|
|
||||||
for _, sig := range strings.Split(ctx.String(signaturesFlag.Name), ",") {
|
|
||||||
trimmed := strings.TrimPrefix(strings.TrimSpace(sig), "0x")
|
|
||||||
if len(trimmed) != 130 {
|
|
||||||
utils.Fatalf("Invalid signature in --signature: '%s'", trimmed)
|
|
||||||
} else {
|
|
||||||
sigs = append(sigs, common.Hex2Bytes(trimmed))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve the checkpoint we want to sign to sort the signatures
|
|
||||||
var (
|
|
||||||
client = newRPCClient(ctx.String(nodeURLFlag.Name))
|
|
||||||
addr, oracle = newContract(client)
|
|
||||||
checkpoint = getCheckpoint(ctx, client)
|
|
||||||
sighash = sighash(checkpoint.SectionIndex, addr, checkpoint.Hash())
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := 0; i < len(sigs); i++ {
|
|
||||||
for j := i + 1; j < len(sigs); j++ {
|
|
||||||
signerA := ecrecover(sighash, sigs[i])
|
|
||||||
signerB := ecrecover(sighash, sigs[j])
|
|
||||||
|
|
||||||
if bytes.Compare(signerA.Bytes(), signerB.Bytes()) > 0 {
|
|
||||||
sigs[i], sigs[j] = sigs[j], sigs[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve recent header info to protect replay attack
|
|
||||||
reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
defer cancelFn()
|
|
||||||
|
|
||||||
head, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
num := head.Number.Uint64()
|
|
||||||
|
|
||||||
recent, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, big.NewInt(int64(num-128)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Print a summary of the operation that's going to be performed
|
|
||||||
fmt.Printf("Publishing %d => %s:\n\n", checkpoint.SectionIndex, checkpoint.Hash().Hex())
|
|
||||||
|
|
||||||
for i, sig := range sigs {
|
|
||||||
fmt.Printf("Signer %d => %s\n", i+1, ecrecover(sighash, sig).Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Printf("Sentry number => %d\nSentry hash => %s\n", recent.Number, recent.Hash().Hex())
|
|
||||||
|
|
||||||
// Publish the checkpoint into the oracle
|
|
||||||
fmt.Println("Sending publish request to Clef...")
|
|
||||||
|
|
||||||
tx, err := oracle.RegisterCheckpoint(newClefSigner(ctx), checkpoint.SectionIndex, checkpoint.Hash().Bytes(), recent.Number, recent.Hash(), sigs)
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Register contract failed %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Successfully registered checkpoint", "tx", tx.Hash().Hex())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// checkpoint-admin is a utility that can be used to query checkpoint information
|
|
||||||
// and register stable checkpoints into an oracle contract.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var app = flags.NewApp("ethereum checkpoint helper tool")
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
app.Commands = []*cli.Command{
|
|
||||||
commandStatus,
|
|
||||||
commandDeploy,
|
|
||||||
commandSign,
|
|
||||||
commandPublish,
|
|
||||||
}
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
oracleFlag,
|
|
||||||
nodeURLFlag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commonly used command line flags.
|
|
||||||
var (
|
|
||||||
indexFlag = &cli.Int64Flag{
|
|
||||||
Name: "index",
|
|
||||||
Usage: "Checkpoint index (query latest from remote node if not specified)",
|
|
||||||
}
|
|
||||||
hashFlag = &cli.StringFlag{
|
|
||||||
Name: "hash",
|
|
||||||
Usage: "Checkpoint hash (query latest from remote node if not specified)",
|
|
||||||
}
|
|
||||||
oracleFlag = &cli.StringFlag{
|
|
||||||
Name: "oracle",
|
|
||||||
Usage: "Checkpoint oracle address (query from remote node if not specified)",
|
|
||||||
}
|
|
||||||
thresholdFlag = &cli.Int64Flag{
|
|
||||||
Name: "threshold",
|
|
||||||
Usage: "Minimal number of signatures required to approve a checkpoint",
|
|
||||||
}
|
|
||||||
nodeURLFlag = &cli.StringFlag{
|
|
||||||
Name: "rpc",
|
|
||||||
Value: "http://localhost:8545",
|
|
||||||
Usage: "The rpc endpoint of a local or remote geth node",
|
|
||||||
}
|
|
||||||
clefURLFlag = &cli.StringFlag{
|
|
||||||
Name: "clef",
|
|
||||||
Value: "http://localhost:8550",
|
|
||||||
Usage: "The rpc endpoint of clef",
|
|
||||||
}
|
|
||||||
signerFlag = &cli.StringFlag{
|
|
||||||
Name: "signer",
|
|
||||||
Usage: "Signer address for clef signing",
|
|
||||||
}
|
|
||||||
signersFlag = &cli.StringFlag{
|
|
||||||
Name: "signers",
|
|
||||||
Usage: "Comma separated accounts of trusted checkpoint signers",
|
|
||||||
}
|
|
||||||
signaturesFlag = &cli.StringFlag{
|
|
||||||
Name: "signatures",
|
|
||||||
Usage: "Comma separated checkpoint signatures to submit",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
|
||||||
fdlimit.Raise(2048)
|
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
var commandStatus = &cli.Command{
|
|
||||||
Name: "status",
|
|
||||||
Usage: "Fetches the signers and checkpoint status of the oracle contract",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
nodeURLFlag,
|
|
||||||
},
|
|
||||||
Action: status,
|
|
||||||
}
|
|
||||||
|
|
||||||
// status fetches the admin list of specified registrar contract.
|
|
||||||
func status(ctx *cli.Context) error {
|
|
||||||
// Create a wrapper around the checkpoint oracle contract
|
|
||||||
addr, oracle := newContract(newRPCClient(ctx.String(nodeURLFlag.Name)))
|
|
||||||
fmt.Printf("Oracle => %s\n", addr.Hex())
|
|
||||||
fmt.Println()
|
|
||||||
|
|
||||||
// Retrieve the list of authorized signers (admins)
|
|
||||||
admins, err := oracle.Contract().GetAllAdmin(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, admin := range admins {
|
|
||||||
fmt.Printf("Admin %d => %s\n", i+1, admin.Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println()
|
|
||||||
|
|
||||||
// Retrieve the latest checkpoint
|
|
||||||
index, checkpoint, height, err := oracle.Contract().GetLatestCheckpoint(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Checkpoint (published at #%d) %d => %s\n", height, index, common.Hash(checkpoint).Hex())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -29,7 +29,7 @@ GLOBAL OPTIONS:
|
||||||
--loglevel value log level to emit to the screen (default: 4)
|
--loglevel value log level to emit to the screen (default: 4)
|
||||||
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
|
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
|
||||||
--configdir value Directory for Clef configuration (default: "$HOME/.clef")
|
--configdir value Directory for Clef configuration (default: "$HOME/.clef")
|
||||||
--chainid value Chain id to use for signing (1=mainnet, 4=Rinkeby, 5=Goerli) (default: 1)
|
--chainid value Chain id to use for signing (1=mainnet, 5=Goerli) (default: 1)
|
||||||
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
|
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
|
||||||
--nousb Disables monitoring for and managing USB hardware wallets
|
--nousb Disables monitoring for and managing USB hardware wallets
|
||||||
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
|
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -99,7 +100,7 @@ var (
|
||||||
chainIdFlag = &cli.Int64Flag{
|
chainIdFlag = &cli.Int64Flag{
|
||||||
Name: "chainid",
|
Name: "chainid",
|
||||||
Value: params.MainnetChainConfig.ChainID.Int64(),
|
Value: params.MainnetChainConfig.ChainID.Int64(),
|
||||||
Usage: "Chain id to use for signing (1=mainnet, 4=Rinkeby, 5=Goerli)",
|
Usage: "Chain id to use for signing (1=mainnet, 5=Goerli)",
|
||||||
}
|
}
|
||||||
rpcPortFlag = &cli.IntFlag{
|
rpcPortFlag = &cli.IntFlag{
|
||||||
Name: "http.port",
|
Name: "http.port",
|
||||||
|
|
@ -329,7 +330,7 @@ func initializeSecrets(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if num != len(masterSeed) {
|
if num != len(masterSeed) {
|
||||||
return fmt.Errorf("failed to read enough random")
|
return errors.New("failed to read enough random")
|
||||||
}
|
}
|
||||||
|
|
||||||
n, p := keystore.StandardScryptN, keystore.StandardScryptP
|
n, p := keystore.StandardScryptN, keystore.StandardScryptP
|
||||||
|
|
@ -511,7 +512,7 @@ func initialize(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
} else if !c.Bool(acceptFlag.Name) {
|
} else if !c.Bool(acceptFlag.Name) {
|
||||||
if !confirm(legalWarning) {
|
if !confirm(legalWarning) {
|
||||||
return fmt.Errorf("aborted by user")
|
return errors.New("aborted by user")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
@ -816,6 +817,7 @@ func signer(c *cli.Context) error {
|
||||||
cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name))
|
cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name))
|
||||||
|
|
||||||
srv := rpc.NewServer("", 0, 0)
|
srv := rpc.NewServer("", 0, 0)
|
||||||
|
srv.SetBatchLimits(node.DefaultConfig.BatchRequestLimit, node.DefaultConfig.BatchResponseMaxSize)
|
||||||
err := node.RegisterApis(rpcAPI, []string{"account"}, srv)
|
err := node.RegisterApis(rpcAPI, []string{"account"}, srv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not register API: %w", err)
|
utils.Fatalf("Could not register API: %w", err)
|
||||||
|
|
@ -827,8 +829,7 @@ func signer(c *cli.Context) error {
|
||||||
port := c.Int(rpcPortFlag.Name)
|
port := c.Int(rpcPortFlag.Name)
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.HTTPListenAddrFlag.Name), port)
|
httpEndpoint := net.JoinHostPort(c.String(utils.HTTPListenAddrFlag.Name), fmt.Sprintf("%d", port))
|
||||||
|
|
||||||
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
|
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start RPC api: %v", err)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
|
|
@ -946,7 +947,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
|
||||||
|
|
||||||
masterSeed, err := decryptSeed(cipherKey, password)
|
masterSeed, err := decryptSeed(cipherKey, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to decrypt the master seed of clef")
|
return nil, errors.New("failed to decrypt the master seed of clef")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(masterSeed) < 256 {
|
if len(masterSeed) < 256 {
|
||||||
|
|
|
||||||
|
|
@ -93,11 +93,11 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
added uint64
|
added atomic.Uint64
|
||||||
updated uint64
|
updated atomic.Uint64
|
||||||
skipped uint64
|
skipped atomic.Uint64
|
||||||
recent uint64
|
recent atomic.Uint64
|
||||||
removed uint64
|
removed atomic.Uint64
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -112,15 +112,15 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
|
||||||
case n := <-c.ch:
|
case n := <-c.ch:
|
||||||
switch c.updateNode(n) {
|
switch c.updateNode(n) {
|
||||||
case nodeSkipIncompat:
|
case nodeSkipIncompat:
|
||||||
atomic.AddUint64(&skipped, 1)
|
skipped.Add(1)
|
||||||
case nodeSkipRecent:
|
case nodeSkipRecent:
|
||||||
atomic.AddUint64(&recent, 1)
|
recent.Add(1)
|
||||||
case nodeRemoved:
|
case nodeRemoved:
|
||||||
atomic.AddUint64(&removed, 1)
|
removed.Add(1)
|
||||||
case nodeAdded:
|
case nodeAdded:
|
||||||
atomic.AddUint64(&added, 1)
|
added.Add(1)
|
||||||
default:
|
default:
|
||||||
atomic.AddUint64(&updated, 1)
|
updated.Add(1)
|
||||||
}
|
}
|
||||||
case <-c.closed:
|
case <-c.closed:
|
||||||
return
|
return
|
||||||
|
|
@ -147,11 +147,11 @@ loop:
|
||||||
break loop
|
break loop
|
||||||
case <-statusTicker.C:
|
case <-statusTicker.C:
|
||||||
log.Info("Crawling in progress",
|
log.Info("Crawling in progress",
|
||||||
"added", atomic.LoadUint64(&added),
|
"added", added.Load(),
|
||||||
"updated", atomic.LoadUint64(&updated),
|
"updated", updated.Load(),
|
||||||
"removed", atomic.LoadUint64(&removed),
|
"removed", removed.Load(),
|
||||||
"ignored(recent)", atomic.LoadUint64(&recent),
|
"ignored(recent)", recent.Load(),
|
||||||
"ignored(incompatible)", atomic.LoadUint64(&skipped))
|
"ignored(incompatible)", skipped.Load())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -187,7 +188,7 @@ func discv4Resolve(ctx *cli.Context) error {
|
||||||
|
|
||||||
func discv4ResolveJSON(ctx *cli.Context) error {
|
func discv4ResolveJSON(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 1 {
|
if ctx.NArg() < 1 {
|
||||||
return fmt.Errorf("need nodes file as argument")
|
return errors.New("need nodes file as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
nodesFile := ctx.Args().Get(0)
|
nodesFile := ctx.Args().Get(0)
|
||||||
|
|
@ -222,7 +223,7 @@ func discv4ResolveJSON(ctx *cli.Context) error {
|
||||||
|
|
||||||
func discv4Crawl(ctx *cli.Context) error {
|
func discv4Crawl(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 1 {
|
if ctx.NArg() < 1 {
|
||||||
return fmt.Errorf("need nodes file as argument")
|
return errors.New("need nodes file as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
nodesFile := ctx.Args().First()
|
nodesFile := ctx.Args().First()
|
||||||
|
|
@ -376,8 +377,7 @@ func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
|
func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
|
||||||
s := params.RinkebyBootnodes
|
s := params.MainnetBootnodes
|
||||||
|
|
||||||
if ctx.IsSet(bootnodesFlag.Name) {
|
if ctx.IsSet(bootnodesFlag.Name) {
|
||||||
input := ctx.String(bootnodesFlag.Name)
|
input := ctx.String(bootnodesFlag.Name)
|
||||||
if input == "" {
|
if input == "" {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -103,7 +104,7 @@ func discv5Resolve(ctx *cli.Context) error {
|
||||||
|
|
||||||
func discv5Crawl(ctx *cli.Context) error {
|
func discv5Crawl(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 1 {
|
if ctx.NArg() < 1 {
|
||||||
return fmt.Errorf("need nodes file as argument")
|
return errors.New("need nodes file as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
nodesFile := ctx.Args().First()
|
nodesFile := ctx.Args().First()
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -49,7 +50,7 @@ type cloudflareClient struct {
|
||||||
func newCloudflareClient(ctx *cli.Context) *cloudflareClient {
|
func newCloudflareClient(ctx *cli.Context) *cloudflareClient {
|
||||||
token := ctx.String(cloudflareTokenFlag.Name)
|
token := ctx.String(cloudflareTokenFlag.Name)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
exit(fmt.Errorf("need cloudflare API token to proceed"))
|
exit(errors.New("need cloudflare API token to proceed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
api, err := cloudflare.NewWithAPIToken(token)
|
api, err := cloudflare.NewWithAPIToken(token)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -34,6 +33,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go-v2/service/route53/types"
|
"github.com/aws/aws-sdk-go-v2/service/route53/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
|
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -83,7 +83,7 @@ func newRoute53Client(ctx *cli.Context) *route53Client {
|
||||||
asec := ctx.String(route53AccessSecretFlag.Name)
|
asec := ctx.String(route53AccessSecretFlag.Name)
|
||||||
|
|
||||||
if akey == "" || asec == "" {
|
if akey == "" || asec == "" {
|
||||||
exit(fmt.Errorf("need Route53 Access Key ID and secret to proceed"))
|
exit(errors.New("need Route53 Access Key ID and secret to proceed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
creds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(akey, asec, ""))
|
creds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(akey, asec, ""))
|
||||||
|
|
@ -318,13 +318,17 @@ func makeDeletionChanges(records map[string]recordSet, keep map[string]string) [
|
||||||
// sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order.
|
// sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order.
|
||||||
func sortChanges(changes []types.Change) {
|
func sortChanges(changes []types.Change) {
|
||||||
score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3}
|
score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3}
|
||||||
|
slices.SortFunc(changes, func(a, b types.Change) int {
|
||||||
sort.Slice(changes, func(i, j int) bool {
|
if a.Action == b.Action {
|
||||||
if changes[i].Action == changes[j].Action {
|
return strings.Compare(*a.ResourceRecordSet.Name, *b.ResourceRecordSet.Name)
|
||||||
return *changes[i].ResourceRecordSet.Name < *changes[j].ResourceRecordSet.Name
|
|
||||||
}
|
}
|
||||||
|
if score[string(a.Action)] < score[string(b.Action)] {
|
||||||
return score[string(changes[i].Action)] < score[string(changes[j].Action)]
|
return -1
|
||||||
|
}
|
||||||
|
if score[string(a.Action)] > score[string(b.Action)] {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -152,7 +153,7 @@ func dnsSync(ctx *cli.Context) error {
|
||||||
|
|
||||||
func dnsSign(ctx *cli.Context) error {
|
func dnsSign(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 2 {
|
if ctx.NArg() < 2 {
|
||||||
return fmt.Errorf("need tree definition directory and key file as arguments")
|
return errors.New("need tree definition directory and key file as arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -215,7 +216,7 @@ func directoryName(dir string) string {
|
||||||
// dnsToTXT performs dnsTXTCommand.
|
// dnsToTXT performs dnsTXTCommand.
|
||||||
func dnsToTXT(ctx *cli.Context) error {
|
func dnsToTXT(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 1 {
|
if ctx.NArg() < 1 {
|
||||||
return fmt.Errorf("need tree definition directory as argument")
|
return errors.New("need tree definition directory as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
output := ctx.Args().Get(1)
|
output := ctx.Args().Get(1)
|
||||||
|
|
@ -236,7 +237,7 @@ func dnsToTXT(ctx *cli.Context) error {
|
||||||
// dnsToCloudflare performs dnsCloudflareCommand.
|
// dnsToCloudflare performs dnsCloudflareCommand.
|
||||||
func dnsToCloudflare(ctx *cli.Context) error {
|
func dnsToCloudflare(ctx *cli.Context) error {
|
||||||
if ctx.NArg() != 1 {
|
if ctx.NArg() != 1 {
|
||||||
return fmt.Errorf("need tree definition directory as argument")
|
return errors.New("need tree definition directory as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
|
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
|
||||||
|
|
@ -252,7 +253,7 @@ func dnsToCloudflare(ctx *cli.Context) error {
|
||||||
// dnsToRoute53 performs dnsRoute53Command.
|
// dnsToRoute53 performs dnsRoute53Command.
|
||||||
func dnsToRoute53(ctx *cli.Context) error {
|
func dnsToRoute53(ctx *cli.Context) error {
|
||||||
if ctx.NArg() != 1 {
|
if ctx.NArg() != 1 {
|
||||||
return fmt.Errorf("need tree definition directory as argument")
|
return errors.New("need tree definition directory as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
|
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
|
||||||
|
|
@ -268,7 +269,7 @@ func dnsToRoute53(ctx *cli.Context) error {
|
||||||
// dnsNukeRoute53 performs dnsRoute53NukeCommand.
|
// dnsNukeRoute53 performs dnsRoute53NukeCommand.
|
||||||
func dnsNukeRoute53(ctx *cli.Context) error {
|
func dnsNukeRoute53(ctx *cli.Context) error {
|
||||||
if ctx.NArg() != 1 {
|
if ctx.NArg() != 1 {
|
||||||
return fmt.Errorf("need domain name as argument")
|
return errors.New("need domain name as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
client := newRoute53Client(ctx)
|
client := newRoute53Client(ctx)
|
||||||
|
|
@ -404,11 +405,11 @@ func loadTreeDefinitionForExport(dir string) (domain string, t *dnsdisc.Tree, er
|
||||||
// tree's signature if valid.
|
// tree's signature if valid.
|
||||||
func ensureValidTreeSignature(t *dnsdisc.Tree, pubkey *ecdsa.PublicKey, sig string) error {
|
func ensureValidTreeSignature(t *dnsdisc.Tree, pubkey *ecdsa.PublicKey, sig string) error {
|
||||||
if sig == "" {
|
if sig == "" {
|
||||||
return fmt.Errorf("missing signature, run 'devp2p dns sign' first")
|
return errors.New("missing signature, run 'devp2p dns sign' first")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := t.SetSignature(pubkey, sig); err != nil {
|
if err := t.SetSignature(pubkey, sig); err != nil {
|
||||||
return fmt.Errorf("invalid signature on tree, run 'devp2p dns sign' to update it")
|
return errors.New("invalid signature on tree, run 'devp2p dns sign' to update it")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
@ -51,7 +52,7 @@ func enrdump(ctx *cli.Context) error {
|
||||||
// nolint:nestif
|
// nolint:nestif
|
||||||
if file := ctx.String(fileFlag.Name); file != "" {
|
if file := ctx.String(fileFlag.Name); file != "" {
|
||||||
if ctx.NArg() != 0 {
|
if ctx.NArg() != 0 {
|
||||||
return fmt.Errorf("can't dump record from command-line argument in -file mode")
|
return errors.New("can't dump record from command-line argument in -file mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
var b []byte
|
var b []byte
|
||||||
|
|
@ -71,7 +72,7 @@ func enrdump(ctx *cli.Context) error {
|
||||||
} else if ctx.NArg() == 1 {
|
} else if ctx.NArg() == 1 {
|
||||||
source = ctx.Args().First()
|
source = ctx.Args().First()
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("need record as argument")
|
return errors.New("need record as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := parseRecord(source)
|
r, err := parseRecord(source)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package ethtest
|
||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -104,7 +105,7 @@ func (c *Chain) Head() *types.Block {
|
||||||
// nolint:typecheck
|
// nolint:typecheck
|
||||||
func (c *Chain) GetHeaders(req *GetBlockHeaders) ([]*types.Header, error) {
|
func (c *Chain) GetHeaders(req *GetBlockHeaders) ([]*types.Header, error) {
|
||||||
if req.Amount < 1 {
|
if req.Amount < 1 {
|
||||||
return nil, fmt.Errorf("no block headers requested")
|
return nil, errors.New("no block headers requested")
|
||||||
}
|
}
|
||||||
|
|
||||||
headers := make([]*types.Header, req.Amount)
|
headers := make([]*types.Header, req.Amount)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package ethtest
|
package ethtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -199,7 +200,7 @@ loop:
|
||||||
}
|
}
|
||||||
// make sure eth protocol version is set for negotiation
|
// make sure eth protocol version is set for negotiation
|
||||||
if c.negotiatedProtoVersion == 0 {
|
if c.negotiatedProtoVersion == 0 {
|
||||||
return nil, fmt.Errorf("eth protocol version must be set in Conn")
|
return nil, errors.New("eth protocol version must be set in Conn")
|
||||||
}
|
}
|
||||||
|
|
||||||
if status == nil {
|
if status == nil {
|
||||||
|
|
|
||||||
|
|
@ -116,15 +116,13 @@ func setupGeth(stack *node.Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
backend, err := eth.New(stack, ðconfig.Config{
|
backend, err := eth.New(stack, ðconfig.Config{
|
||||||
Genesis: &chain.genesis,
|
Genesis: &chain.genesis,
|
||||||
NetworkId: chain.genesis.Config.ChainID.Uint64(), // 19763
|
NetworkId: chain.genesis.Config.ChainID.Uint64(), // 19763
|
||||||
DatabaseCache: 10,
|
DatabaseCache: 10,
|
||||||
TrieCleanCache: 10,
|
TrieCleanCache: 10,
|
||||||
TrieCleanCacheJournal: "",
|
TrieDirtyCache: 16,
|
||||||
TrieCleanCacheRejournal: 60 * time.Minute,
|
TrieTimeout: 60 * time.Minute,
|
||||||
TrieDirtyCache: 16,
|
SnapshotCache: 10,
|
||||||
TrieTimeout: 60 * time.Minute,
|
|
||||||
SnapshotCache: 10,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"eip155Block": 0,
|
"eip155Block": 0,
|
||||||
"eip158Block": 0,
|
"eip158Block": 0,
|
||||||
"byzantiumBlock": 0,
|
"byzantiumBlock": 0,
|
||||||
|
"terminalTotalDifficultyPassed": true,
|
||||||
"ethash": {}
|
"ethash": {}
|
||||||
},
|
},
|
||||||
"nonce": "0xdeadbeefdeadbeef",
|
"nonce": "0xdeadbeefdeadbeef",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package ethtest
|
package ethtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -39,7 +40,7 @@ func (s *Suite) sendSuccessfulTxs(t *utesting.T) error {
|
||||||
}
|
}
|
||||||
for i, tx := range tests {
|
for i, tx := range tests {
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
return fmt.Errorf("could not find tx to send")
|
return errors.New("could not find tx to send")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Testing tx propagation %d: sending tx %v %v %v\n", i, tx.Hash().String(), tx.GasPrice(), tx.Gas())
|
t.Logf("Testing tx propagation %d: sending tx %v %v %v\n", i, tx.Hash().String(), tx.GasPrice(), tx.Gas())
|
||||||
|
|
@ -383,7 +384,7 @@ func generateTxs(s *Suite, numTxs int) (map[common.Hash]common.Hash, []*types.Tr
|
||||||
|
|
||||||
nextTx := getNextTxFromChain(s)
|
nextTx := getNextTxFromChain(s)
|
||||||
if nextTx == nil {
|
if nextTx == nil {
|
||||||
return nil, nil, fmt.Errorf("failed to get the next transaction")
|
return nil, nil, errors.New("failed to get the next transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
gas := nextTx.Gas()
|
gas := nextTx.Gas()
|
||||||
|
|
@ -393,7 +394,7 @@ func generateTxs(s *Suite, numTxs int) (map[common.Hash]common.Hash, []*types.Tr
|
||||||
for i := 0; i < numTxs; i++ {
|
for i := 0; i < numTxs; i++ {
|
||||||
tx := generateTx(s.chain.chainConfig, nonce, gas)
|
tx := generateTx(s.chain.chainConfig, nonce, gas)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
return nil, nil, fmt.Errorf("failed to get the next transaction")
|
return nil, nil, errors.New("failed to get the next transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
txHashMap[tx.Hash()] = tx.Hash()
|
txHashMap[tx.Hash()] = tx.Hash()
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package ethtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -302,6 +303,5 @@ func (c *Conn) ReadSnap(id uint64) (Message, error) {
|
||||||
|
|
||||||
return snpMsg.(Message), nil
|
return snpMsg.(Message), nil
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("request timed out")
|
||||||
return nil, fmt.Errorf("request timed out")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package v4test
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -124,7 +125,7 @@ func (te *testenv) checkPingPong(pingHash []byte) error {
|
||||||
// and a PING. The two packets do not have to be in any particular order.
|
// and a PING. The two packets do not have to be in any particular order.
|
||||||
func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
|
func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
|
||||||
if reply == nil {
|
if reply == nil {
|
||||||
return fmt.Errorf("expected PONG reply, got nil")
|
return errors.New("expected PONG reply, got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if reply.Kind() != v4wire.PongPacket {
|
if reply.Kind() != v4wire.PongPacket {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
|
@ -87,7 +88,7 @@ var (
|
||||||
|
|
||||||
func genkey(ctx *cli.Context) error {
|
func genkey(ctx *cli.Context) error {
|
||||||
if ctx.NArg() != 1 {
|
if ctx.NArg() != 1 {
|
||||||
return fmt.Errorf("need key file as argument")
|
return errors.New("need key file as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
file := ctx.Args().Get(0)
|
file := ctx.Args().Get(0)
|
||||||
|
|
@ -135,7 +136,7 @@ func keyToRecord(ctx *cli.Context) error {
|
||||||
|
|
||||||
func makeRecord(ctx *cli.Context) (*enode.Node, error) {
|
func makeRecord(ctx *cli.Context) (*enode.Node, error) {
|
||||||
if ctx.NArg() != 1 {
|
if ctx.NArg() != 1 {
|
||||||
return nil, fmt.Errorf("need key file as argument")
|
return nil, errors.New("need key file as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
const jsonIndent = " "
|
const jsonIndent = " "
|
||||||
|
|
@ -80,8 +80,8 @@ func (ns nodeSet) nodes() []*enode.Node {
|
||||||
result = append(result, n.N)
|
result = append(result, n.N)
|
||||||
}
|
}
|
||||||
// Sort by ID.
|
// Sort by ID.
|
||||||
sort.Slice(result, func(i, j int) bool {
|
slices.SortFunc(result, func(a, b *enode.Node) int {
|
||||||
return bytes.Compare(result[i].ID().Bytes(), result[j].ID().Bytes()) < 0
|
return bytes.Compare(a.ID().Bytes(), b.ID().Bytes())
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
@ -107,9 +107,14 @@ func (ns nodeSet) topN(n int) nodeSet {
|
||||||
for _, v := range ns {
|
for _, v := range ns {
|
||||||
byscore = append(byscore, v)
|
byscore = append(byscore, v)
|
||||||
}
|
}
|
||||||
|
slices.SortFunc(byscore, func(a, b nodeJSON) int {
|
||||||
sort.Slice(byscore, func(i, j int) bool {
|
if a.Score > b.Score {
|
||||||
return byscore[i].Score >= byscore[j].Score
|
return -1
|
||||||
|
}
|
||||||
|
if a.Score < b.Score {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
result := make(nodeSet, n)
|
result := make(nodeSet, n)
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ var (
|
||||||
|
|
||||||
func nodesetInfo(ctx *cli.Context) error {
|
func nodesetInfo(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 1 {
|
if ctx.NArg() < 1 {
|
||||||
return fmt.Errorf("need nodes file as argument")
|
return errors.New("need nodes file as argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := loadNodesJSON(ctx.Args().First())
|
ns := loadNodesJSON(ctx.Args().First())
|
||||||
|
|
@ -108,7 +108,7 @@ func showAttributeCounts(ns nodeSet) {
|
||||||
|
|
||||||
func nodesetFilter(ctx *cli.Context) error {
|
func nodesetFilter(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 1 {
|
if ctx.NArg() < 1 {
|
||||||
return fmt.Errorf("need nodes file as argument")
|
return errors.New("need nodes file as argument")
|
||||||
}
|
}
|
||||||
// Parse -limit.
|
// Parse -limit.
|
||||||
limit, err := parseFilterLimit(ctx.Args().Tail())
|
limit, err := parseFilterLimit(ctx.Args().Tail())
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
|
@ -98,7 +99,7 @@ func rlpxPing(ctx *cli.Context) error {
|
||||||
case 1:
|
case 1:
|
||||||
var msg []p2p.DiscReason
|
var msg []p2p.DiscReason
|
||||||
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
||||||
return fmt.Errorf("invalid disconnect message")
|
return errors.New("invalid disconnect message")
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("received disconnect message: %v", msg[0])
|
return fmt.Errorf("received disconnect message: %v", msg[0])
|
||||||
|
|
|
||||||
|
|
@ -342,7 +342,7 @@ To make `t8n` apply these, the following inputs are required:
|
||||||
- For ethash, it is `5000000000000000000` `wei`,
|
- For ethash, it is `5000000000000000000` `wei`,
|
||||||
- If this is not defined, mining rewards are not applied,
|
- If this is not defined, mining rewards are not applied,
|
||||||
- A value of `0` is valid, and causes accounts to be 'touched'.
|
- A value of `0` is valid, and causes accounts to be 'touched'.
|
||||||
- For each ommer, the tool needs to be given an `addres\` and a `delta`. This
|
- For each ommer, the tool needs to be given an `address\` and a `delta`. This
|
||||||
is done via the `ommers` field in `env`.
|
is done via the `ommers` field in `env`.
|
||||||
|
|
||||||
Note: the tool does not verify that e.g. the normal uncle rules apply,
|
Note: the tool does not verify that e.g. the normal uncle rules apply,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ import (
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
)
|
)
|
||||||
|
|
@ -43,7 +45,16 @@ func blockTestCmd(ctx *cli.Context) error {
|
||||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
||||||
log.Root().SetHandler(glogger)
|
log.Root().SetHandler(glogger)
|
||||||
|
var tracer vm.EVMLogger
|
||||||
|
// Configure the EVM logger
|
||||||
|
if ctx.Bool(MachineFlag.Name) {
|
||||||
|
tracer = logger.NewJSONLogger(&logger.Config{
|
||||||
|
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
||||||
|
DisableStack: ctx.Bool(DisableStackFlag.Name),
|
||||||
|
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
|
||||||
|
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
|
||||||
|
}, os.Stderr)
|
||||||
|
}
|
||||||
// Load the test content from the input file
|
// Load the test content from the input file
|
||||||
src, err := os.ReadFile(ctx.Args().First())
|
src, err := os.ReadFile(ctx.Args().First())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -57,7 +68,7 @@ func blockTestCmd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
if err := test.Run(false); err != nil {
|
if err := test.Run(false, tracer); err != nil {
|
||||||
return fmt.Errorf("test %v: %w", i, err)
|
return fmt.Errorf("test %v: %w", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package t8ntool
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -31,7 +30,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -76,11 +74,9 @@ type bbInput struct {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
Clique *cliqueInput `json:"clique,omitempty"`
|
Clique *cliqueInput `json:"clique,omitempty"`
|
||||||
|
|
||||||
Ethash bool `json:"-"`
|
Ethash bool `json:"-"`
|
||||||
EthashDir string `json:"-"`
|
Txs []*types.Transaction `json:"-"`
|
||||||
PowMode ethash.Mode `json:"-"`
|
Ommers []*types.Header `json:"-"`
|
||||||
Txs []*types.Transaction `json:"-"`
|
|
||||||
Ommers []*types.Header `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cliqueInput struct {
|
type cliqueInput struct {
|
||||||
|
|
@ -175,8 +171,6 @@ func (i *bbInput) ToBlock() *types.Block {
|
||||||
// SealBlock seals the given block using the configured engine.
|
// SealBlock seals the given block using the configured engine.
|
||||||
func (i *bbInput) SealBlock(block *types.Block) (*types.Block, error) {
|
func (i *bbInput) SealBlock(block *types.Block) (*types.Block, error) {
|
||||||
switch {
|
switch {
|
||||||
case i.Ethash:
|
|
||||||
return i.sealEthash(block)
|
|
||||||
case i.Clique != nil:
|
case i.Clique != nil:
|
||||||
return i.sealClique(block)
|
return i.sealClique(block)
|
||||||
default:
|
default:
|
||||||
|
|
@ -184,50 +178,19 @@ func (i *bbInput) SealBlock(block *types.Block) (*types.Block, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sealEthash seals the given block using ethash.
|
|
||||||
func (i *bbInput) sealEthash(block *types.Block) (*types.Block, error) {
|
|
||||||
if i.Header.Nonce != nil {
|
|
||||||
return nil, NewError(ErrorConfig, fmt.Errorf("sealing with ethash will overwrite provided nonce"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ethashConfig := ethash.Config{
|
|
||||||
PowMode: i.PowMode,
|
|
||||||
DatasetDir: i.EthashDir,
|
|
||||||
CacheDir: i.EthashDir,
|
|
||||||
DatasetsInMem: 1,
|
|
||||||
DatasetsOnDisk: 2,
|
|
||||||
CachesInMem: 2,
|
|
||||||
CachesOnDisk: 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
engine := ethash.New(ethashConfig, nil, true)
|
|
||||||
defer engine.Close()
|
|
||||||
// Use a buffered chan for results.
|
|
||||||
// If the testmode is used, the sealer will return quickly, and complain
|
|
||||||
// "Sealing result is not read by miner" if it cannot write the result.
|
|
||||||
results := make(chan *types.Block, 1)
|
|
||||||
if err := engine.Seal(context.Background(), nil, block, results, nil); err != nil {
|
|
||||||
panic(fmt.Sprintf("failed to seal block: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
found := <-results
|
|
||||||
|
|
||||||
return block.WithSeal(found.Header()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sealClique seals the given block using clique.
|
// sealClique seals the given block using clique.
|
||||||
func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) {
|
func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) {
|
||||||
// If any clique value overwrites an explicit header value, fail
|
// If any clique value overwrites an explicit header value, fail
|
||||||
// to avoid silently building a block with unexpected values.
|
// to avoid silently building a block with unexpected values.
|
||||||
if i.Header.Extra != nil {
|
if i.Header.Extra != nil {
|
||||||
return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique will overwrite provided extra data"))
|
return nil, NewError(ErrorConfig, errors.New("sealing with clique will overwrite provided extra data"))
|
||||||
}
|
}
|
||||||
|
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
|
|
||||||
if i.Clique.Voted != nil {
|
if i.Clique.Voted != nil {
|
||||||
if i.Header.Coinbase != nil {
|
if i.Header.Coinbase != nil {
|
||||||
return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided coinbase"))
|
return nil, NewError(ErrorConfig, errors.New("sealing with clique and voting will overwrite provided coinbase"))
|
||||||
}
|
}
|
||||||
|
|
||||||
header.Coinbase = *i.Clique.Voted
|
header.Coinbase = *i.Clique.Voted
|
||||||
|
|
@ -235,7 +198,7 @@ func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) {
|
||||||
|
|
||||||
if i.Clique.Authorize != nil {
|
if i.Clique.Authorize != nil {
|
||||||
if i.Header.Nonce != nil {
|
if i.Header.Nonce != nil {
|
||||||
return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided nonce"))
|
return nil, NewError(ErrorConfig, errors.New("sealing with clique and voting will overwrite provided nonce"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if *i.Clique.Authorize {
|
if *i.Clique.Authorize {
|
||||||
|
|
@ -296,32 +259,8 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
||||||
withdrawalsStr = ctx.String(InputWithdrawalsFlag.Name)
|
withdrawalsStr = ctx.String(InputWithdrawalsFlag.Name)
|
||||||
txsStr = ctx.String(InputTxsRlpFlag.Name)
|
txsStr = ctx.String(InputTxsRlpFlag.Name)
|
||||||
cliqueStr = ctx.String(SealCliqueFlag.Name)
|
cliqueStr = ctx.String(SealCliqueFlag.Name)
|
||||||
ethashOn = ctx.Bool(SealEthashFlag.Name)
|
|
||||||
ethashDir = ctx.String(SealEthashDirFlag.Name)
|
|
||||||
ethashMode = ctx.String(SealEthashModeFlag.Name)
|
|
||||||
inputData = &bbInput{}
|
inputData = &bbInput{}
|
||||||
)
|
)
|
||||||
|
|
||||||
if ethashOn && cliqueStr != "" {
|
|
||||||
return nil, NewError(ErrorConfig, fmt.Errorf("both ethash and clique sealing specified, only one may be chosen"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ethashOn {
|
|
||||||
inputData.Ethash = ethashOn
|
|
||||||
inputData.EthashDir = ethashDir
|
|
||||||
|
|
||||||
switch ethashMode {
|
|
||||||
case "normal":
|
|
||||||
inputData.PowMode = ethash.ModeNormal
|
|
||||||
case "test":
|
|
||||||
inputData.PowMode = ethash.ModeTest
|
|
||||||
case "fake":
|
|
||||||
inputData.PowMode = ethash.ModeFake
|
|
||||||
default:
|
|
||||||
return nil, NewError(ErrorConfig, fmt.Errorf("unknown pow mode: %s, supported modes: test, fake, normal", ethashMode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if headerStr == stdinSelector || ommersStr == stdinSelector || txsStr == stdinSelector || cliqueStr == stdinSelector {
|
if headerStr == stdinSelector || ommersStr == stdinSelector || txsStr == stdinSelector || cliqueStr == stdinSelector {
|
||||||
decoder := json.NewDecoder(os.Stdin)
|
decoder := json.NewDecoder(os.Stdin)
|
||||||
if err := decoder.Decode(inputData); err != nil {
|
if err := decoder.Decode(inputData); err != nil {
|
||||||
|
|
@ -407,7 +346,7 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
||||||
return inputData, nil
|
return inputData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatchOutput writes the output data to either stderr or stdout, or to the specified
|
// dispatchBlock writes the output data to either stderr or stdout, or to the specified
|
||||||
// files
|
// files
|
||||||
func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error {
|
func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error {
|
||||||
raw, _ := rlp.EncodeToBytes(block)
|
raw, _ := rlp.EncodeToBytes(block)
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,12 @@ package t8ntool
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -47,17 +47,19 @@ type Prestate struct {
|
||||||
// ExecutionResult contains the execution status after running a state test, any
|
// ExecutionResult contains the execution status after running a state test, any
|
||||||
// error that might have occurred and a dump of the final state if requested.
|
// error that might have occurred and a dump of the final state if requested.
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
StateRoot common.Hash `json:"stateRoot"`
|
StateRoot common.Hash `json:"stateRoot"`
|
||||||
TxRoot common.Hash `json:"txRoot"`
|
TxRoot common.Hash `json:"txRoot"`
|
||||||
ReceiptRoot common.Hash `json:"receiptsRoot"`
|
ReceiptRoot common.Hash `json:"receiptsRoot"`
|
||||||
LogsHash common.Hash `json:"logsHash"`
|
LogsHash common.Hash `json:"logsHash"`
|
||||||
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Receipts types.Receipts `json:"receipts"`
|
Receipts types.Receipts `json:"receipts"`
|
||||||
Rejected []*rejectedTx `json:"rejected,omitempty"`
|
Rejected []*rejectedTx `json:"rejected,omitempty"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
|
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
|
||||||
|
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
|
||||||
|
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"currentBlobGasUsed,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ommer struct {
|
type ommer struct {
|
||||||
|
|
@ -67,37 +69,43 @@ type ommer struct {
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
|
//go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
|
||||||
type stEnv struct {
|
type stEnv struct {
|
||||||
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
|
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
|
||||||
Difficulty *big.Int `json:"currentDifficulty"`
|
Difficulty *big.Int `json:"currentDifficulty"`
|
||||||
Random *big.Int `json:"currentRandom"`
|
Random *big.Int `json:"currentRandom"`
|
||||||
ParentDifficulty *big.Int `json:"parentDifficulty"`
|
ParentDifficulty *big.Int `json:"parentDifficulty"`
|
||||||
ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"`
|
ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"`
|
||||||
ParentGasUsed uint64 `json:"parentGasUsed,omitempty"`
|
ParentGasUsed uint64 `json:"parentGasUsed,omitempty"`
|
||||||
ParentGasLimit uint64 `json:"parentGasLimit,omitempty"`
|
ParentGasLimit uint64 `json:"parentGasLimit,omitempty"`
|
||||||
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
|
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
|
||||||
Number uint64 `json:"currentNumber" gencodec:"required"`
|
Number uint64 `json:"currentNumber" gencodec:"required"`
|
||||||
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
|
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
|
||||||
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
|
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
|
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
|
||||||
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
||||||
|
ExcessBlobGas *uint64 `json:"excessBlobGas,omitempty"`
|
||||||
|
ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"`
|
||||||
|
ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type stEnvMarshaling struct {
|
type stEnvMarshaling struct {
|
||||||
Coinbase common.UnprefixedAddress
|
Coinbase common.UnprefixedAddress
|
||||||
Difficulty *math.HexOrDecimal256
|
Difficulty *math.HexOrDecimal256
|
||||||
Random *math.HexOrDecimal256
|
Random *math.HexOrDecimal256
|
||||||
ParentDifficulty *math.HexOrDecimal256
|
ParentDifficulty *math.HexOrDecimal256
|
||||||
ParentBaseFee *math.HexOrDecimal256
|
ParentBaseFee *math.HexOrDecimal256
|
||||||
ParentGasUsed math.HexOrDecimal64
|
ParentGasUsed math.HexOrDecimal64
|
||||||
ParentGasLimit math.HexOrDecimal64
|
ParentGasLimit math.HexOrDecimal64
|
||||||
GasLimit math.HexOrDecimal64
|
GasLimit math.HexOrDecimal64
|
||||||
Number math.HexOrDecimal64
|
Number math.HexOrDecimal64
|
||||||
Timestamp math.HexOrDecimal64
|
Timestamp math.HexOrDecimal64
|
||||||
ParentTimestamp math.HexOrDecimal64
|
ParentTimestamp math.HexOrDecimal64
|
||||||
BaseFee *math.HexOrDecimal256
|
BaseFee *math.HexOrDecimal256
|
||||||
|
ExcessBlobGas *math.HexOrDecimal64
|
||||||
|
ParentExcessBlobGas *math.HexOrDecimal64
|
||||||
|
ParentBlobGasUsed *math.HexOrDecimal64
|
||||||
}
|
}
|
||||||
|
|
||||||
type rejectedTx struct {
|
type rejectedTx struct {
|
||||||
|
|
@ -129,7 +137,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
|
|
||||||
var (
|
var (
|
||||||
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
|
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
|
||||||
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number))
|
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
|
||||||
gaspool = new(core.GasPool)
|
gaspool = new(core.GasPool)
|
||||||
blockHash = common.Hash{0x13, 0x37}
|
blockHash = common.Hash{0x13, 0x37}
|
||||||
rejectedTxs []*rejectedTx
|
rejectedTxs []*rejectedTx
|
||||||
|
|
@ -159,6 +167,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
rnd := common.BigToHash(pre.Env.Random)
|
rnd := common.BigToHash(pre.Env.Random)
|
||||||
vmContext.Random = &rnd
|
vmContext.Random = &rnd
|
||||||
}
|
}
|
||||||
|
// If excessBlobGas is defined, add it to the vmContext.
|
||||||
|
if pre.Env.ExcessBlobGas != nil {
|
||||||
|
vmContext.ExcessBlobGas = pre.Env.ExcessBlobGas
|
||||||
|
} else {
|
||||||
|
// If it is not explicitly defined, but we have the parent values, we try
|
||||||
|
// to calculate it ourselves.
|
||||||
|
parentExcessBlobGas := pre.Env.ParentExcessBlobGas
|
||||||
|
parentBlobGasUsed := pre.Env.ParentBlobGasUsed
|
||||||
|
if parentExcessBlobGas != nil && parentBlobGasUsed != nil {
|
||||||
|
excessBlobGas := eip4844.CalcExcessBlobGas(*parentExcessBlobGas, *parentBlobGasUsed)
|
||||||
|
vmContext.ExcessBlobGas = &excessBlobGas
|
||||||
|
}
|
||||||
|
}
|
||||||
// If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
|
// If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
|
||||||
// done in StateProcessor.Process(block, ...), right before transactions are applied.
|
// done in StateProcessor.Process(block, ...), right before transactions are applied.
|
||||||
if chainConfig.DAOForkSupport &&
|
if chainConfig.DAOForkSupport &&
|
||||||
|
|
@ -166,8 +187,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
|
chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
|
||||||
misc.ApplyDAOHardFork(statedb)
|
misc.ApplyDAOHardFork(statedb)
|
||||||
}
|
}
|
||||||
|
var blobGasUsed uint64
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
|
if tx.Type() == types.BlobTxType && vmContext.ExcessBlobGas == nil {
|
||||||
|
errMsg := "blob tx used but field env.ExcessBlobGas missing"
|
||||||
|
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", errMsg)
|
||||||
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg})
|
||||||
|
continue
|
||||||
|
}
|
||||||
msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
|
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
|
||||||
|
|
@ -204,7 +231,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if tx.Type() == types.BlobTxType {
|
||||||
|
blobGasUsed += params.BlobTxBlobGasPerBlob
|
||||||
|
}
|
||||||
includedTxs = append(includedTxs, tx)
|
includedTxs = append(includedTxs, tx)
|
||||||
|
|
||||||
if hashError != nil {
|
if hashError != nil {
|
||||||
|
|
@ -258,7 +287,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
if miningReward >= 0 {
|
if miningReward >= 0 {
|
||||||
// Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
|
// Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
|
||||||
// where
|
// where
|
||||||
// - the coinbase suicided, or
|
// - the coinbase self-destructed, or
|
||||||
// - there are only 'bad' transactions, which aren't executed. In those cases,
|
// - there are only 'bad' transactions, which aren't executed. In those cases,
|
||||||
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
|
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
|
||||||
var (
|
var (
|
||||||
|
|
@ -287,9 +316,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
statedb.AddBalance(w.Address, amount)
|
statedb.AddBalance(w.Address, amount)
|
||||||
}
|
}
|
||||||
// Commit block
|
// Commit block
|
||||||
root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
|
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Could not commit state: %v", err)
|
|
||||||
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
|
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,14 +338,22 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
|
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
|
||||||
execRs.WithdrawalsRoot = &h
|
execRs.WithdrawalsRoot = &h
|
||||||
}
|
}
|
||||||
|
if vmContext.ExcessBlobGas != nil {
|
||||||
|
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(vmContext.ExcessBlobGas)
|
||||||
|
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
|
||||||
|
}
|
||||||
|
// Re-create statedb instance with new root upon the updated database
|
||||||
|
// for accessing latest states.
|
||||||
|
statedb, err = state.New(root, statedb.Database(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err))
|
||||||
|
}
|
||||||
return statedb, execRs, nil
|
return statedb, execRs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||||
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
||||||
statedb, _ := state.New(common.Hash{}, sdb, nil)
|
|
||||||
for addr, a := range accounts {
|
for addr, a := range accounts {
|
||||||
statedb.SetCode(addr, a.Code)
|
statedb.SetCode(addr, a.Code)
|
||||||
statedb.SetNonce(addr, a.Nonce)
|
statedb.SetNonce(addr, a.Nonce)
|
||||||
|
|
@ -328,7 +364,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Commit and re-open to start with a clean state.
|
// Commit and re-open to start with a clean state.
|
||||||
root, _ := statedb.Commit(false)
|
root, _ := statedb.Commit(0, false)
|
||||||
statedb, _ = state.New(root, sdb, nil)
|
statedb, _ = state.New(root, sdb, nil)
|
||||||
|
|
||||||
return statedb
|
return statedb
|
||||||
|
|
|
||||||
|
|
@ -126,19 +126,6 @@ var (
|
||||||
Name: "seal.clique",
|
Name: "seal.clique",
|
||||||
Usage: "Seal block with Clique. `stdin` or file name of where to find the Clique sealing data.",
|
Usage: "Seal block with Clique. `stdin` or file name of where to find the Clique sealing data.",
|
||||||
}
|
}
|
||||||
SealEthashFlag = &cli.BoolFlag{
|
|
||||||
Name: "seal.ethash",
|
|
||||||
Usage: "Seal block with ethash.",
|
|
||||||
}
|
|
||||||
SealEthashDirFlag = &cli.StringFlag{
|
|
||||||
Name: "seal.ethash.dir",
|
|
||||||
Usage: "Path to ethash DAG. If none exists, a new DAG will be generated.",
|
|
||||||
}
|
|
||||||
SealEthashModeFlag = &cli.StringFlag{
|
|
||||||
Name: "seal.ethash.mode",
|
|
||||||
Usage: "Defines the type and amount of PoW verification an ethash engine makes.",
|
|
||||||
Value: "normal",
|
|
||||||
}
|
|
||||||
RewardFlag = &cli.Int64Flag{
|
RewardFlag = &cli.Int64Flag{
|
||||||
Name: "state.reward",
|
Name: "state.reward",
|
||||||
Usage: "Mining reward. Set to -1 to disable",
|
Usage: "Mining reward. Set to -1 to disable",
|
||||||
|
|
|
||||||
|
|
@ -17,22 +17,25 @@ var _ = (*stEnvMarshaling)(nil)
|
||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (s stEnv) MarshalJSON() ([]byte, error) {
|
func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||||
type stEnv struct {
|
type stEnv struct {
|
||||||
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
||||||
Random *math.HexOrDecimal256 `json:"currentRandom"`
|
Random *math.HexOrDecimal256 `json:"currentRandom"`
|
||||||
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
||||||
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
|
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
|
||||||
ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
|
ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
|
||||||
ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
|
ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
|
||||||
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||||
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||||
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||||
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
||||||
|
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"`
|
||||||
|
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
|
||||||
|
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc stEnv
|
var enc stEnv
|
||||||
|
|
@ -52,29 +55,38 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||||
enc.Withdrawals = s.Withdrawals
|
enc.Withdrawals = s.Withdrawals
|
||||||
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
||||||
enc.ParentUncleHash = s.ParentUncleHash
|
enc.ParentUncleHash = s.ParentUncleHash
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
|
=======
|
||||||
|
enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas)
|
||||||
|
enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas)
|
||||||
|
enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed)
|
||||||
|
>>>>>>> bed84606583893fdb698cc1b5058cc47b4dbd837
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (s *stEnv) UnmarshalJSON(input []byte) error {
|
func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||||
type stEnv struct {
|
type stEnv struct {
|
||||||
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
||||||
Random *math.HexOrDecimal256 `json:"currentRandom"`
|
Random *math.HexOrDecimal256 `json:"currentRandom"`
|
||||||
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
||||||
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
|
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
|
||||||
ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
|
ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
|
||||||
ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
|
ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
|
||||||
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||||
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||||
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||||
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
ParentUncleHash *common.Hash `json:"parentUncleHash"`
|
ParentUncleHash *common.Hash `json:"parentUncleHash"`
|
||||||
|
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"`
|
||||||
|
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
|
||||||
|
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var dec stEnv
|
var dec stEnv
|
||||||
|
|
@ -151,6 +163,18 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||||
if dec.ParentUncleHash != nil {
|
if dec.ParentUncleHash != nil {
|
||||||
s.ParentUncleHash = *dec.ParentUncleHash
|
s.ParentUncleHash = *dec.ParentUncleHash
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
|
=======
|
||||||
|
if dec.ExcessBlobGas != nil {
|
||||||
|
s.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||||
|
}
|
||||||
|
if dec.ParentExcessBlobGas != nil {
|
||||||
|
s.ParentExcessBlobGas = (*uint64)(dec.ParentExcessBlobGas)
|
||||||
|
}
|
||||||
|
if dec.ParentBlobGasUsed != nil {
|
||||||
|
s.ParentBlobGasUsed = (*uint64)(dec.ParentBlobGasUsed)
|
||||||
|
}
|
||||||
|
>>>>>>> bed84606583893fdb698cc1b5058cc47b4dbd837
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,8 +122,12 @@ func Transaction(ctx *cli.Context) error {
|
||||||
return NewError(ErrorIO, errors.New("only rlp supported"))
|
return NewError(ErrorIO, errors.New("only rlp supported"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
signer := types.MakeSigner(chainConfig, new(big.Int))
|
signer := types.MakeSigner(chainConfig, new(big.Int))
|
||||||
|
=======
|
||||||
|
signer := types.MakeSigner(chainConfig, new(big.Int), 0)
|
||||||
|
>>>>>>> bed84606583893fdb698cc1b5058cc47b4dbd837
|
||||||
// We now have the transactions in 'body', which is supposed to be an
|
// We now have the transactions in 'body', which is supposed to be an
|
||||||
// rlp list of transactions
|
// rlp list of transactions
|
||||||
it, err := rlp.NewListIterator([]byte(body))
|
it, err := rlp.NewListIterator([]byte(body))
|
||||||
|
|
@ -157,7 +161,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
// Check intrinsic gas
|
// Check intrinsic gas
|
||||||
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
|
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
|
||||||
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(0)); err != nil {
|
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int), 0)); err != nil {
|
||||||
r.Error = err
|
r.Error = err
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
|
|
||||||
|
|
@ -192,7 +196,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
// TODO marcello double check
|
// TODO marcello double check
|
||||||
// Check whether the init code size has been exceeded.
|
// Check whether the init code size has been exceeded.
|
||||||
if chainConfig.IsShanghai(0) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
|
if chainConfig.IsShanghai(new(big.Int), 0) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
|
||||||
r.Error = errors.New("max initcode size exceeded")
|
r.Error = errors.New("max initcode size exceeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -264,7 +264,7 @@ func Transition(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We may have to sign the transactions.
|
// We may have to sign the transactions.
|
||||||
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
|
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
|
||||||
|
|
||||||
if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
|
if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
|
||||||
return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err))
|
return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err))
|
||||||
|
|
@ -280,13 +280,17 @@ func Transition(ctx *cli.Context) error {
|
||||||
GasUsed: prestate.Env.ParentGasUsed,
|
GasUsed: prestate.Env.ParentGasUsed,
|
||||||
GasLimit: prestate.Env.ParentGasLimit,
|
GasLimit: prestate.Env.ParentGasLimit,
|
||||||
}
|
}
|
||||||
prestate.Env.BaseFee = misc.CalcBaseFee(chainConfig, parent)
|
prestate.Env.BaseFee = eip1559.CalcBaseFee(chainConfig, parent)
|
||||||
} else {
|
} else {
|
||||||
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
// TODO marcello double check
|
// TODO marcello double check
|
||||||
if chainConfig.IsShanghai(prestate.Env.Number) && prestate.Env.Withdrawals == nil {
|
if chainConfig.IsShanghai(prestate.Env.Number) && prestate.Env.Withdrawals == nil {
|
||||||
|
=======
|
||||||
|
if chainConfig.IsShanghai(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp) && prestate.Env.Withdrawals == nil {
|
||||||
|
>>>>>>> bed84606583893fdb698cc1b5058cc47b4dbd837
|
||||||
return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section"))
|
return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,7 +435,10 @@ type Alloc map[common.Address]core.GenesisAccount
|
||||||
|
|
||||||
func (g Alloc) OnRoot(common.Hash) {}
|
func (g Alloc) OnRoot(common.Hash) {}
|
||||||
|
|
||||||
func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
|
func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||||
|
if addr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
|
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
|
||||||
|
|
||||||
var storage map[common.Hash]common.Hash
|
var storage map[common.Hash]common.Hash
|
||||||
|
|
@ -448,7 +455,7 @@ func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
|
||||||
Balance: balance,
|
Balance: balance,
|
||||||
Nonce: dumpAccount.Nonce,
|
Nonce: dumpAccount.Nonce,
|
||||||
}
|
}
|
||||||
g[addr] = genesisAccount
|
g[*addr] = genesisAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveFile marshals the object to the given file
|
// saveFile marshals the object to the given file
|
||||||
|
|
|
||||||
|
|
@ -180,9 +180,6 @@ var blockBuilderCommand = &cli.Command{
|
||||||
t8ntool.InputWithdrawalsFlag,
|
t8ntool.InputWithdrawalsFlag,
|
||||||
t8ntool.InputTxsRlpFlag,
|
t8ntool.InputTxsRlpFlag,
|
||||||
t8ntool.SealCliqueFlag,
|
t8ntool.SealCliqueFlag,
|
||||||
t8ntool.SealEthashFlag,
|
|
||||||
t8ntool.SealEthashDirFlag,
|
|
||||||
t8ntool.SealEthashModeFlag,
|
|
||||||
t8ntool.VerbosityFlag,
|
t8ntool.VerbosityFlag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
|
|
@ -135,6 +136,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
receiver = common.BytesToAddress([]byte("receiver"))
|
receiver = common.BytesToAddress([]byte("receiver"))
|
||||||
genesisConfig *core.Genesis
|
genesisConfig *core.Genesis
|
||||||
preimages = ctx.Bool(DumpFlag.Name)
|
preimages = ctx.Bool(DumpFlag.Name)
|
||||||
|
blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests
|
||||||
)
|
)
|
||||||
|
|
||||||
if ctx.Bool(MachineFlag.Name) {
|
if ctx.Bool(MachineFlag.Name) {
|
||||||
|
|
@ -156,7 +158,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
chainConfig = gen.Config
|
chainConfig = gen.Config
|
||||||
} else {
|
} else {
|
||||||
sdb := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: preimages})
|
sdb := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: preimages})
|
||||||
statedb, _ = state.New(common.Hash{}, sdb, nil)
|
statedb, _ = state.New(types.EmptyRootHash, sdb, nil)
|
||||||
genesisConfig = new(core.Genesis)
|
genesisConfig = new(core.Genesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,6 +238,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
Time: genesisConfig.Timestamp,
|
Time: genesisConfig.Timestamp,
|
||||||
Coinbase: genesisConfig.Coinbase,
|
Coinbase: genesisConfig.Coinbase,
|
||||||
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
||||||
|
BlobHashes: blobHashes,
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
Tracer: tracer,
|
Tracer: tracer,
|
||||||
},
|
},
|
||||||
|
|
@ -305,8 +308,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
output, leftOverGas, stats, err := timedExec(bench, execFunc)
|
output, leftOverGas, stats, err := timedExec(bench, execFunc)
|
||||||
|
|
||||||
if ctx.Bool(DumpFlag.Name) {
|
if ctx.Bool(DumpFlag.Name) {
|
||||||
statedb.Commit(true)
|
statedb.Commit(genesisConfig.Number, true)
|
||||||
statedb.IntermediateRoot(true)
|
|
||||||
fmt.Println(string(statedb.Dump(nil)))
|
fmt.Println(string(statedb.Dump(nil)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ import (
|
||||||
var stateTestCommand = &cli.Command{
|
var stateTestCommand = &cli.Command{
|
||||||
Action: stateTestCmd,
|
Action: stateTestCmd,
|
||||||
Name: "statetest",
|
Name: "statetest",
|
||||||
Usage: "executes the given state tests",
|
Usage: "Executes the given state tests. Filenames can be fed via standard input (batch mode) or as an argument (one-off execution).",
|
||||||
ArgsUsage: "<file>",
|
ArgsUsage: "<file>",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,9 +51,6 @@ type StatetestResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func stateTestCmd(ctx *cli.Context) error {
|
func stateTestCmd(ctx *cli.Context) error {
|
||||||
if len(ctx.Args().First()) == 0 {
|
|
||||||
return errors.New("path-to-test argument required")
|
|
||||||
}
|
|
||||||
// Configure the go-ethereum logger
|
// Configure the go-ethereum logger
|
||||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
||||||
|
|
@ -66,37 +63,44 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||||
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
|
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
|
||||||
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
|
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
|
||||||
}
|
}
|
||||||
|
var cfg vm.Config
|
||||||
var (
|
|
||||||
tracer vm.EVMLogger
|
|
||||||
debugger *logger.StructLogger
|
|
||||||
)
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(MachineFlag.Name):
|
case ctx.Bool(MachineFlag.Name):
|
||||||
tracer = logger.NewJSONLogger(config, os.Stderr)
|
cfg.Tracer = logger.NewJSONLogger(config, os.Stderr)
|
||||||
|
|
||||||
case ctx.Bool(DebugFlag.Name):
|
case ctx.Bool(DebugFlag.Name):
|
||||||
debugger = logger.NewStructLogger(config)
|
cfg.Tracer = logger.NewStructLogger(config)
|
||||||
tracer = debugger
|
|
||||||
|
|
||||||
default:
|
|
||||||
debugger = logger.NewStructLogger(config)
|
|
||||||
}
|
}
|
||||||
// Load the test content from the input file
|
// Load the test content from the input file
|
||||||
src, err := os.ReadFile(ctx.Args().First())
|
if len(ctx.Args().First()) != 0 {
|
||||||
|
return runStateTest(ctx.Args().First(), cfg, ctx.Bool(MachineFlag.Name), ctx.Bool(DumpFlag.Name))
|
||||||
|
}
|
||||||
|
// Read filenames from stdin and execute back-to-back
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
for scanner.Scan() {
|
||||||
|
fname := scanner.Text()
|
||||||
|
if len(fname) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := runStateTest(fname, cfg, ctx.Bool(MachineFlag.Name), ctx.Bool(DumpFlag.Name)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runStateTest loads the state-test given by fname, and executes the test.
|
||||||
|
func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
|
||||||
|
src, err := os.ReadFile(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var tests map[string]tests.StateTest
|
var tests map[string]tests.StateTest
|
||||||
if err = json.Unmarshal(src, &tests); err != nil {
|
if err := json.Unmarshal(src, &tests); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Iterate over all the tests, run them and aggregate the results
|
// Iterate over all the tests, run them and aggregate the results
|
||||||
cfg := vm.Config{
|
|
||||||
Tracer: tracer,
|
|
||||||
}
|
|
||||||
results := make([]StatetestResult, 0, len(tests))
|
results := make([]StatetestResult, 0, len(tests))
|
||||||
|
|
||||||
for key, test := range tests {
|
for key, test := range tests {
|
||||||
|
|
@ -108,8 +112,7 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
root := s.IntermediateRoot(false)
|
root := s.IntermediateRoot(false)
|
||||||
result.Root = &root
|
result.Root = &root
|
||||||
|
if jsonOut {
|
||||||
if ctx.Bool(MachineFlag.Name) {
|
|
||||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -117,22 +120,14 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Test failed, mark as so and dump any state to aid debugging
|
// Test failed, mark as so and dump any state to aid debugging
|
||||||
result.Pass, result.Error = false, err.Error()
|
result.Pass, result.Error = false, err.Error()
|
||||||
|
if dump && s != nil {
|
||||||
if ctx.Bool(DumpFlag.Name) && s != nil {
|
s, _ = state.New(*result.Root, s.Database(), nil)
|
||||||
dump := s.RawDump(nil)
|
dump := s.RawDump(nil)
|
||||||
result.State = &dump
|
result.State = &dump
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, *result)
|
results = append(results, *result)
|
||||||
|
|
||||||
// Print any structured logs collected
|
|
||||||
if ctx.Bool(DebugFlag.Name) {
|
|
||||||
if debugger != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "#### TRACE ####")
|
|
||||||
logger.WriteTrace(os.Stderr, debugger.StructLogs())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,14 @@ func TestT8n(t *testing.T) {
|
||||||
output: t8nOutput{alloc: true, result: true},
|
output: t8nOutput{alloc: true, result: true},
|
||||||
expOut: "exp.json",
|
expOut: "exp.json",
|
||||||
},
|
},
|
||||||
|
{ // Cancun tests
|
||||||
|
base: "./testdata/28",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.rlp", "env.json", "Cancun", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
args := []string{"t8n"}
|
args := []string{"t8n"}
|
||||||
args = append(args, tc.output.get()...)
|
args = append(args, tc.output.get()...)
|
||||||
|
|
@ -290,7 +298,8 @@ func TestT8n(t *testing.T) {
|
||||||
tt.Run("evm-test", args...)
|
tt.Run("evm-test", args...)
|
||||||
// Compare the expected output, if provided
|
// Compare the expected output, if provided
|
||||||
if tc.expOut != "" {
|
if tc.expOut != "" {
|
||||||
want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
|
file := fmt.Sprintf("%v/%v", tc.base, tc.expOut)
|
||||||
|
want, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d: could not read expected output: %v", i, err)
|
t.Fatalf("test %d: could not read expected output: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -300,9 +309,9 @@ func TestT8n(t *testing.T) {
|
||||||
ok, err := cmpJson(have, want)
|
ok, err := cmpJson(have, want)
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
t.Fatalf("test %d, json parsing failed: %v", i, err)
|
t.Fatalf("test %d, file %v: json parsing failed: %v", i, file, err)
|
||||||
case !ok:
|
case !ok:
|
||||||
t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
|
t.Fatalf("test %d, file %v: output wrong, have \n%v\nwant\n%v\n", i, file, string(have), string(want))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
cmd/evm/testdata/1/exp.json
vendored
1
cmd/evm/testdata/1/exp.json
vendored
|
|
@ -28,6 +28,7 @@
|
||||||
"transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
|
"transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0x5208",
|
"gasUsed": "0x5208",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x0"
|
"transactionIndex": "0x0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
cmd/evm/testdata/13/exp2.json
vendored
2
cmd/evm/testdata/13/exp2.json
vendored
|
|
@ -16,6 +16,7 @@
|
||||||
"transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
"transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0x84d0",
|
"gasUsed": "0x84d0",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x0"
|
"transactionIndex": "0x0"
|
||||||
},
|
},
|
||||||
|
|
@ -29,6 +30,7 @@
|
||||||
"transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
"transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0x84d0",
|
"gasUsed": "0x84d0",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x1"
|
"transactionIndex": "0x1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
cmd/evm/testdata/13/readme.md
vendored
2
cmd/evm/testdata/13/readme.md
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
## Input transactions in RLP form
|
## Input transactions in RLP form
|
||||||
|
|
||||||
This testdata folder is used to examplify how transaction input can be provided in rlp form.
|
This testdata folder is used to exemplify how transaction input can be provided in rlp form.
|
||||||
Please see the README in `evm` folder for how this is performed.
|
Please see the README in `evm` folder for how this is performed.
|
||||||
2
cmd/evm/testdata/2/readme.md
vendored
2
cmd/evm/testdata/2/readme.md
vendored
|
|
@ -1 +1 @@
|
||||||
These files examplify a selfdestruct to the `0`-address.
|
These files exemplify a selfdestruct to the `0`-address.
|
||||||
1
cmd/evm/testdata/23/exp.json
vendored
1
cmd/evm/testdata/23/exp.json
vendored
|
|
@ -15,6 +15,7 @@
|
||||||
"transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
|
"transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0x520b",
|
"gasUsed": "0x520b",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x0"
|
"transactionIndex": "0x0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
cmd/evm/testdata/23/readme.md
vendored
2
cmd/evm/testdata/23/readme.md
vendored
|
|
@ -1 +1 @@
|
||||||
These files examplify how to sign a transaction using the pre-EIP155 scheme.
|
These files exemplify how to sign a transaction using the pre-EIP155 scheme.
|
||||||
|
|
|
||||||
2
cmd/evm/testdata/24/exp.json
vendored
2
cmd/evm/testdata/24/exp.json
vendored
|
|
@ -31,6 +31,7 @@
|
||||||
"transactionHash": "0x92ea4a28224d033afb20e0cc2b290d4c7c2d61f6a4800a680e4e19ac962ee941",
|
"transactionHash": "0x92ea4a28224d033afb20e0cc2b290d4c7c2d61f6a4800a680e4e19ac962ee941",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0xa861",
|
"gasUsed": "0xa861",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x0"
|
"transactionIndex": "0x0"
|
||||||
},
|
},
|
||||||
|
|
@ -43,6 +44,7 @@
|
||||||
"transactionHash": "0x16b1d912f1d664f3f60f4e1b5f296f3c82a64a1a253117b4851d18bc03c4f1da",
|
"transactionHash": "0x16b1d912f1d664f3f60f4e1b5f296f3c82a64a1a253117b4851d18bc03c4f1da",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0x5aa5",
|
"gasUsed": "0x5aa5",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x1"
|
"transactionIndex": "0x1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
cmd/evm/testdata/25/exp.json
vendored
1
cmd/evm/testdata/25/exp.json
vendored
|
|
@ -27,6 +27,7 @@
|
||||||
"transactionHash": "0x92ea4a28224d033afb20e0cc2b290d4c7c2d61f6a4800a680e4e19ac962ee941",
|
"transactionHash": "0x92ea4a28224d033afb20e0cc2b290d4c7c2d61f6a4800a680e4e19ac962ee941",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0x5208",
|
"gasUsed": "0x5208",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x0"
|
"transactionIndex": "0x0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
cmd/evm/testdata/28/alloc.json
vendored
Normal file
16
cmd/evm/testdata/28/alloc.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "0x016345785d8a0000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "0x016345785d8a0000",
|
||||||
|
"code" : "0x60004960015500",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
cmd/evm/testdata/28/env.json
vendored
Normal file
22
cmd/evm/testdata/28/env.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentNumber" : "0x01",
|
||||||
|
"currentTimestamp" : "0x079e",
|
||||||
|
"currentGasLimit" : "0x7fffffffffffffff",
|
||||||
|
"previousHash" : "0x3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6",
|
||||||
|
"currentBlobGasUsed" : "0x00",
|
||||||
|
"parentTimestamp" : "0x03b6",
|
||||||
|
"parentDifficulty" : "0x00",
|
||||||
|
"parentUncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"currentRandom" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"withdrawals" : [
|
||||||
|
],
|
||||||
|
"parentBaseFee" : "0x0a",
|
||||||
|
"parentGasUsed" : "0x00",
|
||||||
|
"parentGasLimit" : "0x7fffffffffffffff",
|
||||||
|
"parentExcessBlobGas" : "0x00",
|
||||||
|
"parentBlobGasUsed" : "0x00",
|
||||||
|
"blockHashes" : {
|
||||||
|
"0" : "0x3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6"
|
||||||
|
}
|
||||||
|
}
|
||||||
47
cmd/evm/testdata/28/exp.json
vendored
Normal file
47
cmd/evm/testdata/28/exp.json
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": {
|
||||||
|
"balance": "0x150ca"
|
||||||
|
},
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0x16345785d80c3a9",
|
||||||
|
"nonce": "0x1"
|
||||||
|
},
|
||||||
|
"0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"code": "0x60004960015500",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x01a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
||||||
|
},
|
||||||
|
"balance": "0x16345785d8a0000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0xa40cb3fab01848e922a48bd24191815df9f721ad4b60376edac75161517663e8",
|
||||||
|
"txRoot": "0x4409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9ce",
|
||||||
|
"receiptsRoot": "0xbff643da765981266133094092d98c81d2ac8e9a83a7bbda46c3d736f1f874ac",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [
|
||||||
|
{
|
||||||
|
"type": "0x3",
|
||||||
|
"root": "0x",
|
||||||
|
"status": "0x1",
|
||||||
|
"cumulativeGasUsed": "0xa865",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"logs": null,
|
||||||
|
"transactionHash": "0x7508d7139d002a4b3a26a4f12dec0d87cb46075c78bf77a38b569a133b509262",
|
||||||
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
|
"gasUsed": "0xa865",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"transactionIndex": "0x0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currentDifficulty": null,
|
||||||
|
"gasUsed": "0xa865",
|
||||||
|
"currentBaseFee": "0x9",
|
||||||
|
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"currentExcessBlobGas": "0x0",
|
||||||
|
"currentBlobGasUsed": "0x20000"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
cmd/evm/testdata/28/txs.rlp
vendored
Normal file
1
cmd/evm/testdata/28/txs.rlp
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
"0xf88bb88903f8860180026483061a8094b94f5374fce5edbc8e2a8697c15331677e6ebf0b8080c00ae1a001a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d801a025e16bb498552165016751911c3608d79000ab89dc3100776e729e6ea13091c7a03acacff7fc0cff6eda8a927dec93ca17765e1ee6cbc06c5954ce102e097c01d2"
|
||||||
1
cmd/evm/testdata/3/exp.json
vendored
1
cmd/evm/testdata/3/exp.json
vendored
|
|
@ -28,6 +28,7 @@
|
||||||
"transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
|
"transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
|
||||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||||
"gasUsed": "0x521f",
|
"gasUsed": "0x521f",
|
||||||
|
"effectiveGasPrice": null,
|
||||||
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"transactionIndex": "0x0"
|
"transactionIndex": "0x0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
cmd/evm/testdata/3/readme.md
vendored
4
cmd/evm/testdata/3/readme.md
vendored
|
|
@ -1,2 +1,6 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
These files examplify a transition where a transaction (executed on block 5) requests
|
These files examplify a transition where a transaction (executed on block 5) requests
|
||||||
|
=======
|
||||||
|
These files exemplify a transition where a transaction (executed on block 5) requests
|
||||||
|
>>>>>>> bed84606583893fdb698cc1b5058cc47b4dbd837
|
||||||
the blockhash for block `1`.
|
the blockhash for block `1`.
|
||||||
|
|
|
||||||
4
cmd/evm/testdata/4/readme.md
vendored
4
cmd/evm/testdata/4/readme.md
vendored
|
|
@ -1,3 +1,7 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
These files examplify a transition where a transaction (executed on block 5) requests
|
These files examplify a transition where a transaction (executed on block 5) requests
|
||||||
|
=======
|
||||||
|
These files exemplify a transition where a transaction (executed on block 5) requests
|
||||||
|
>>>>>>> bed84606583893fdb698cc1b5058cc47b4dbd837
|
||||||
the blockhash for block `4`, but where the hash for that block is missing.
|
the blockhash for block `4`, but where the hash for that block is missing.
|
||||||
It's expected that executing these should cause `exit` with errorcode `4`.
|
It's expected that executing these should cause `exit` with errorcode `4`.
|
||||||
|
|
|
||||||
2
cmd/evm/testdata/5/readme.md
vendored
2
cmd/evm/testdata/5/readme.md
vendored
|
|
@ -1 +1 @@
|
||||||
These files examplify a transition where there are no transcations, two ommers, at block `N-1` (delta 1) and `N-2` (delta 2).
|
These files exemplify a transition where there are no transactions, two ommers, at block `N-1` (delta 1) and `N-2` (delta 2).
|
||||||
|
|
@ -280,7 +280,7 @@ To make `t8n` apply these, the following inputs are required:
|
||||||
- For ethash, it is `5000000000000000000` `wei`,
|
- For ethash, it is `5000000000000000000` `wei`,
|
||||||
- If this is not defined, mining rewards are not applied,
|
- If this is not defined, mining rewards are not applied,
|
||||||
- A value of `0` is valid, and causes accounts to be 'touched'.
|
- A value of `0` is valid, and causes accounts to be 'touched'.
|
||||||
- For each ommer, the tool needs to be given an `addres\` and a `delta`. This
|
- For each ommer, the tool needs to be given an `address\` and a `delta`. This
|
||||||
is done via the `ommers` field in `env`.
|
is done via the `ommers` field in `env`.
|
||||||
|
|
||||||
Note: the tool does not verify that e.g. the normal uncle rules apply,
|
Note: the tool does not verify that e.g. the normal uncle rules apply,
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,11 @@ The `faucet` is a single binary app (everything included) with all configuration
|
||||||
|
|
||||||
First things first, the `faucet` needs to connect to an Ethereum network, for which it needs the necessary genesis and network infos. Each of the following flags must be set:
|
First things first, the `faucet` needs to connect to an Ethereum network, for which it needs the necessary genesis and network infos. Each of the following flags must be set:
|
||||||
|
|
||||||
- `--genesis` is a path to a file containing the network `genesis.json`
|
- `-genesis` is a path to a file containing the network `genesis.json`. or using:
|
||||||
- `--network` is the devp2p network id used during connection
|
- `-goerli` with the faucet with Görli network config
|
||||||
- `--bootnodes` is a list of `enode://` ids to join the network through
|
- `-sepolia` with the faucet with Sepolia network config
|
||||||
|
- `-network` is the devp2p network id used during connection
|
||||||
|
- `-bootnodes` is a list of `enode://` ids to join the network through
|
||||||
|
|
||||||
The `faucet` will use the `les` protocol to join the configured Ethereum network and will store its data in `$HOME/.faucet` (currently not configurable).
|
The `faucet` will use the `les` protocol to join the configured Ethereum network and will store its data in `$HOME/.faucet` (currently not configurable).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -288,12 +288,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*enode.Node, network ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Attach to the client and retrieve and interesting metadatas
|
// Attach to the client and retrieve and interesting metadatas
|
||||||
api, err := stack.Attach()
|
api := stack.Attach()
|
||||||
if err != nil {
|
|
||||||
stack.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := ethclient.NewClient(api)
|
client := ethclient.NewClient(api)
|
||||||
|
|
||||||
return &faucet{
|
return &faucet{
|
||||||
|
|
@ -981,6 +976,6 @@ func getGenesis(genesisFlag string, goerliFlag bool, sepoliaFlag bool, mumbaiFla
|
||||||
case mumbaiFlag:
|
case mumbaiFlag:
|
||||||
return core.DefaultMumbaiGenesisBlock(), nil
|
return core.DefaultMumbaiGenesisBlock(), nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("no genesis flag provided")
|
return nil, errors.New("no genesis flag provided")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,12 +189,28 @@ nodes.
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// makeAccountManager creates an account manager with backends
|
||||||
|
func makeAccountManager(ctx *cli.Context) *accounts.Manager {
|
||||||
|
cfg := loadBaseConfig(ctx)
|
||||||
|
am := accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: cfg.Node.InsecureUnlockAllowed})
|
||||||
|
keydir, isEphemeral, err := cfg.Node.GetKeyStoreDir()
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to get the keystore directory: %v", err)
|
||||||
|
}
|
||||||
|
if isEphemeral {
|
||||||
|
utils.Fatalf("Can't use ephemeral directory as keystore path")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setAccountManagerBackends(&cfg.Node, am, keydir); err != nil {
|
||||||
|
utils.Fatalf("Failed to set account manager backends: %v", err)
|
||||||
|
}
|
||||||
|
return am
|
||||||
|
}
|
||||||
|
|
||||||
func accountList(ctx *cli.Context) error {
|
func accountList(ctx *cli.Context) error {
|
||||||
stack, _ := makeConfigNode(ctx)
|
am := makeAccountManager(ctx)
|
||||||
|
|
||||||
var index int
|
var index int
|
||||||
|
for _, wallet := range am.Wallets() {
|
||||||
for _, wallet := range stack.AccountManager().Wallets() {
|
|
||||||
for _, account := range wallet.Accounts() {
|
for _, account := range wallet.Accounts() {
|
||||||
fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
|
fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
|
||||||
|
|
||||||
|
|
@ -275,19 +291,13 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr
|
||||||
|
|
||||||
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
||||||
func accountCreate(ctx *cli.Context) error {
|
func accountCreate(ctx *cli.Context) error {
|
||||||
cfg := gethConfig{Node: defaultNodeConfig()}
|
cfg := loadBaseConfig(ctx)
|
||||||
// Load config file.
|
keydir, isEphemeral, err := cfg.Node.GetKeyStoreDir()
|
||||||
if file := ctx.String(configFileFlag.Name); file != "" {
|
|
||||||
if err := loadConfig(file, &cfg); err != nil {
|
|
||||||
utils.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SetNodeConfig(ctx, &cfg.Node)
|
|
||||||
|
|
||||||
keydir, err := cfg.Node.KeyDirConfig()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read configuration: %v", err)
|
utils.Fatalf("Failed to get the keystore directory: %v", err)
|
||||||
|
}
|
||||||
|
if isEphemeral {
|
||||||
|
utils.Fatalf("Can't use ephemeral directory as keystore path")
|
||||||
}
|
}
|
||||||
|
|
||||||
scryptN := keystore.StandardScryptN
|
scryptN := keystore.StandardScryptN
|
||||||
|
|
@ -323,10 +333,8 @@ func accountUpdate(ctx *cli.Context) error {
|
||||||
if ctx.Args().Len() == 0 {
|
if ctx.Args().Len() == 0 {
|
||||||
utils.Fatalf("No accounts specified to update")
|
utils.Fatalf("No accounts specified to update")
|
||||||
}
|
}
|
||||||
|
am := makeAccountManager(ctx)
|
||||||
stack, _ := makeConfigNode(ctx)
|
backends := am.Backends(keystore.KeyStoreType)
|
||||||
backends := stack.AccountManager().Backends(keystore.KeyStoreType)
|
|
||||||
|
|
||||||
if len(backends) == 0 {
|
if len(backends) == 0 {
|
||||||
utils.Fatalf("Keystore is not available")
|
utils.Fatalf("Keystore is not available")
|
||||||
}
|
}
|
||||||
|
|
@ -357,15 +365,14 @@ func importWallet(ctx *cli.Context) error {
|
||||||
utils.Fatalf("Could not read wallet file: %v", err)
|
utils.Fatalf("Could not read wallet file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _ := makeConfigNode(ctx)
|
am := makeAccountManager(ctx)
|
||||||
passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx))
|
backends := am.Backends(keystore.KeyStoreType)
|
||||||
|
|
||||||
backends := stack.AccountManager().Backends(keystore.KeyStoreType)
|
|
||||||
if len(backends) == 0 {
|
if len(backends) == 0 {
|
||||||
utils.Fatalf("Keystore is not available")
|
utils.Fatalf("Keystore is not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
ks := backends[0].(*keystore.KeyStore)
|
ks := backends[0].(*keystore.KeyStore)
|
||||||
|
passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
|
acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -388,16 +395,14 @@ func accountImport(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to load the private key: %v", err)
|
utils.Fatalf("Failed to load the private key: %v", err)
|
||||||
}
|
}
|
||||||
|
am := makeAccountManager(ctx)
|
||||||
stack, _ := makeConfigNode(ctx)
|
backends := am.Backends(keystore.KeyStoreType)
|
||||||
passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
|
||||||
|
|
||||||
backends := stack.AccountManager().Backends(keystore.KeyStoreType)
|
|
||||||
if len(backends) == 0 {
|
if len(backends) == 0 {
|
||||||
utils.Fatalf("Keystore is not available")
|
utils.Fatalf("Keystore is not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
ks := backends[0].(*keystore.KeyStore)
|
ks := backends[0].(*keystore.KeyStore)
|
||||||
|
passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
acct, err := ks.ImportECDSA(key, passphrase)
|
acct, err := ks.ImportECDSA(key, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,7 @@ func TestRemoteDbWithHeaders(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testReceiveHeaders(t *testing.T, ln net.Listener, gethArgs ...string) {
|
func testReceiveHeaders(t *testing.T, ln net.Listener, gethArgs ...string) {
|
||||||
t.Helper()
|
var ok atomic.Uint32
|
||||||
|
|
||||||
var ok uint32
|
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: "localhost:0",
|
Addr: "localhost:0",
|
||||||
Handler: &testHandler{func(w http.ResponseWriter, r *http.Request) {
|
Handler: &testHandler{func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -80,15 +77,14 @@ func testReceiveHeaders(t *testing.T, ln net.Listener, gethArgs ...string) {
|
||||||
if have, want := r.Header.Get("second"), "two"; have != want {
|
if have, want := r.Header.Get("second"), "two"; have != want {
|
||||||
t.Fatalf("missing header, have %v want %v", have, want)
|
t.Fatalf("missing header, have %v want %v", have, want)
|
||||||
}
|
}
|
||||||
atomic.StoreUint32(&ok, 1)
|
ok.Store(1)
|
||||||
}}}
|
}}}
|
||||||
//nolint:errcheck
|
//nolint:errcheck
|
||||||
go server.Serve(ln)
|
go server.Serve(ln)
|
||||||
|
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
runGeth(t, gethArgs...).WaitExit()
|
runGeth(t, gethArgs...).WaitExit()
|
||||||
|
if ok.Load() != 1 {
|
||||||
if atomic.LoadUint32(&ok) != 1 {
|
|
||||||
t.Fatal("Test fail, expected invocation to succeed")
|
t.Fatal("Test fail, expected invocation to succeed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -279,20 +279,17 @@ func importChain(ctx *cli.Context) error {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Start periodically gathering memory profiles
|
// Start periodically gathering memory profiles
|
||||||
var peakMemAlloc, peakMemSys uint64
|
var peakMemAlloc, peakMemSys atomic.Uint64
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
stats := new(runtime.MemStats)
|
stats := new(runtime.MemStats)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
runtime.ReadMemStats(stats)
|
runtime.ReadMemStats(stats)
|
||||||
|
if peakMemAlloc.Load() < stats.Alloc {
|
||||||
if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc {
|
peakMemAlloc.Store(stats.Alloc)
|
||||||
atomic.StoreUint64(&peakMemAlloc, stats.Alloc)
|
|
||||||
}
|
}
|
||||||
|
if peakMemSys.Load() < stats.Sys {
|
||||||
if atomic.LoadUint64(&peakMemSys) < stats.Sys {
|
peakMemSys.Store(stats.Sys)
|
||||||
atomic.StoreUint64(&peakMemSys, stats.Sys)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
@ -327,8 +324,8 @@ func importChain(ctx *cli.Context) error {
|
||||||
mem := new(runtime.MemStats)
|
mem := new(runtime.MemStats)
|
||||||
runtime.ReadMemStats(mem)
|
runtime.ReadMemStats(mem)
|
||||||
|
|
||||||
fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024)
|
fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(peakMemAlloc.Load())/1024/1024)
|
||||||
fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024)
|
fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(peakMemSys.Load())/1024/1024)
|
||||||
fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
|
fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
|
||||||
fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
|
fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
|
||||||
|
|
||||||
|
|
@ -533,7 +530,7 @@ func dump(ctx *cli.Context) error {
|
||||||
if conf.OnlyWithAddresses {
|
if conf.OnlyWithAddresses {
|
||||||
fmt.Fprintf(os.Stderr, "If you want to include accounts with missing preimages, you need iterative output, since"+
|
fmt.Fprintf(os.Stderr, "If you want to include accounts with missing preimages, you need iterative output, since"+
|
||||||
" otherwise the accounts will overwrite each other in the resulting mapping.")
|
" otherwise the accounts will overwrite each other in the resulting mapping.")
|
||||||
return fmt.Errorf("incompatible options")
|
return errors.New("incompatible options")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(string(state.Dump(conf)))
|
fmt.Println(string(state.Dump(conf)))
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,13 @@ import (
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/external"
|
"github.com/ethereum/go-ethereum/accounts/external"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/accounts/scwallet"
|
"github.com/ethereum/go-ethereum/accounts/scwallet"
|
||||||
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
|
@ -94,8 +96,9 @@ func defaultNodeConfig() node.Config {
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeConfigNode loads geth configuration and creates a blank node instance.
|
// loadBaseConfig loads the gethConfig based on the given command line
|
||||||
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
// parameters and config file.
|
||||||
|
func loadBaseConfig(ctx *cli.Context) gethConfig {
|
||||||
// Load defaults.
|
// Load defaults.
|
||||||
cfg := gethConfig{
|
cfg := gethConfig{
|
||||||
Eth: ethconfig.Defaults,
|
Eth: ethconfig.Defaults,
|
||||||
|
|
@ -120,13 +123,18 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
|
|
||||||
// Apply flags.
|
// Apply flags.
|
||||||
utils.SetNodeConfig(ctx, &cfg.Node)
|
utils.SetNodeConfig(ctx, &cfg.Node)
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeConfigNode loads geth configuration and creates a blank node instance.
|
||||||
|
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
|
cfg := loadBaseConfig(ctx)
|
||||||
stack, err := node.New(&cfg.Node)
|
stack, err := node.New(&cfg.Node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to create the protocol stack: %v", err)
|
utils.Fatalf("Failed to create the protocol stack: %v", err)
|
||||||
}
|
}
|
||||||
// Node doesn't by default populate account manager backends
|
// Node doesn't by default populate account manager backends
|
||||||
if err := setAccountManagerBackends(stack); err != nil {
|
if err := setAccountManagerBackends(stack.Config(), stack.AccountManager(), stack.KeyStoreDir()); err != nil {
|
||||||
utils.Fatalf("Failed to set account manager backends: %v", err)
|
utils.Fatalf("Failed to set account manager backends: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,10 +155,13 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
// makeFullNode loads geth configuration and creates the Ethereum backend.
|
// makeFullNode loads geth configuration and creates the Ethereum backend.
|
||||||
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||||
stack, cfg := makeConfigNode(ctx)
|
stack, cfg := makeConfigNode(ctx)
|
||||||
// TODO marcello double check
|
if ctx.IsSet(utils.OverrideCancun.Name) {
|
||||||
if ctx.IsSet(utils.OverrideShanghai.Name) {
|
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||||
v := ctx.Uint64(utils.OverrideShanghai.Name)
|
cfg.Eth.OverrideCancun = &v
|
||||||
cfg.Eth.OverrideShanghai = &v
|
}
|
||||||
|
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||||
|
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||||
|
cfg.Eth.OverrideVerkle = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
@ -173,6 +184,21 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||||
utils.RegisterFullSyncTester(stack, eth, ctx.Path(utils.SyncTargetFlag.Name))
|
utils.RegisterFullSyncTester(stack, eth, ctx.Path(utils.SyncTargetFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the dev mode if requested, or launch the engine API for
|
||||||
|
// interacting with external consensus client.
|
||||||
|
if ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||||
|
simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), eth)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("failed to register dev mode catalyst service: %v", err)
|
||||||
|
}
|
||||||
|
catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
|
||||||
|
stack.RegisterLifecycle(simBeacon)
|
||||||
|
} else if cfg.Eth.SyncMode != downloader.LightSync {
|
||||||
|
err := catalyst.Register(stack, eth)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("failed to register catalyst service: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return stack, backend
|
return stack, backend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,15 +283,16 @@ func deprecated(field string) bool {
|
||||||
return true
|
return true
|
||||||
case "ethconfig.Config.EWASMInterpreter":
|
case "ethconfig.Config.EWASMInterpreter":
|
||||||
return true
|
return true
|
||||||
|
case "ethconfig.Config.TrieCleanCacheJournal":
|
||||||
|
return true
|
||||||
|
case "ethconfig.Config.TrieCleanCacheRejournal":
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAccountManagerBackends(stack *node.Node) error {
|
func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error {
|
||||||
conf := stack.Config()
|
|
||||||
am := stack.AccountManager()
|
|
||||||
keydir := stack.KeyStoreDir()
|
|
||||||
scryptN := keystore.StandardScryptN
|
scryptN := keystore.StandardScryptN
|
||||||
scryptP := keystore.StandardScryptP
|
scryptP := keystore.StandardScryptP
|
||||||
|
|
||||||
|
|
@ -277,9 +304,8 @@ func setAccountManagerBackends(stack *node.Node) error {
|
||||||
// Assemble the supported backends
|
// Assemble the supported backends
|
||||||
if len(conf.ExternalSigner) > 0 {
|
if len(conf.ExternalSigner) > 0 {
|
||||||
log.Info("Using external signer", "url", conf.ExternalSigner)
|
log.Info("Using external signer", "url", conf.ExternalSigner)
|
||||||
|
if extBackend, err := external.NewExternalBackend(conf.ExternalSigner); err == nil {
|
||||||
if extapi, err := external.NewExternalBackend(conf.ExternalSigner); err == nil {
|
am.AddBackend(extBackend)
|
||||||
am.AddBackend(extapi)
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("error connecting to external signer: %v", err)
|
return fmt.Errorf("error connecting to external signer: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -80,11 +80,7 @@ func localConsole(ctx *cli.Context) error {
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
// Attach to the newly started node and create the JavaScript console.
|
// Attach to the newly started node and create the JavaScript console.
|
||||||
client, err := stack.Attach()
|
client := stack.Attach()
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to attach to the inproc geth: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config := console.Config{
|
config := console.Config{
|
||||||
DataDir: utils.MakeDataDir(ctx),
|
DataDir: utils.MakeDataDir(ctx),
|
||||||
DocRoot: ctx.String(utils.JSpathFlag.Name),
|
DocRoot: ctx.String(utils.JSpathFlag.Name),
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ func TestAttachWelcome(t *testing.T) {
|
||||||
waitForEndpoint(t, endpoint, 3*time.Second)
|
waitForEndpoint(t, endpoint, 3*time.Second)
|
||||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
})
|
})
|
||||||
geth.ExpectExit()
|
geth.Kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue