mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Merge branch 'master' into lock
This commit is contained in:
commit
bee71391dd
72 changed files with 1511 additions and 631 deletions
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The ABI holds information about a contract's context and available
|
// The ABI holds information about a contract's context and available
|
||||||
|
|
@ -76,7 +77,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Pack up the method ID too if not a constructor and return
|
// Pack up the method ID too if not a constructor and return
|
||||||
return append(method.ID(), arguments...), nil
|
return append(method.ID, arguments...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack output in v according to the abi specification
|
// Unpack output in v according to the abi specification
|
||||||
|
|
@ -139,59 +140,17 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
switch field.Type {
|
switch field.Type {
|
||||||
case "constructor":
|
case "constructor":
|
||||||
abi.Constructor = Method{
|
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
||||||
Inputs: field.Inputs,
|
|
||||||
|
|
||||||
// Note for constructor the `StateMutability` can only
|
|
||||||
// be payable or nonpayable according to the output of
|
|
||||||
// compiler. So constant is always false.
|
|
||||||
StateMutability: field.StateMutability,
|
|
||||||
|
|
||||||
// Legacy fields, keep them for backward compatibility
|
|
||||||
Constant: field.Constant,
|
|
||||||
Payable: field.Payable,
|
|
||||||
}
|
|
||||||
case "function":
|
case "function":
|
||||||
name := field.Name
|
name := abi.overloadedMethodName(field.Name)
|
||||||
_, ok := abi.Methods[name]
|
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
||||||
for idx := 0; ok; idx++ {
|
|
||||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
|
||||||
_, ok = abi.Methods[name]
|
|
||||||
}
|
|
||||||
abi.Methods[name] = Method{
|
|
||||||
Name: name,
|
|
||||||
RawName: field.Name,
|
|
||||||
StateMutability: field.StateMutability,
|
|
||||||
Inputs: field.Inputs,
|
|
||||||
Outputs: field.Outputs,
|
|
||||||
|
|
||||||
// Legacy fields, keep them for backward compatibility
|
|
||||||
Constant: field.Constant,
|
|
||||||
Payable: field.Payable,
|
|
||||||
}
|
|
||||||
case "fallback":
|
case "fallback":
|
||||||
// New introduced function type in v0.6.0, check more detail
|
// New introduced function type in v0.6.0, check more detail
|
||||||
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
||||||
if abi.HasFallback() {
|
if abi.HasFallback() {
|
||||||
return errors.New("only single fallback is allowed")
|
return errors.New("only single fallback is allowed")
|
||||||
}
|
}
|
||||||
abi.Fallback = Method{
|
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
Name: "",
|
|
||||||
RawName: "",
|
|
||||||
|
|
||||||
// The `StateMutability` can only be payable or nonpayable,
|
|
||||||
// so the constant is always false.
|
|
||||||
StateMutability: field.StateMutability,
|
|
||||||
IsFallback: true,
|
|
||||||
|
|
||||||
// Fallback doesn't have any input or output
|
|
||||||
Inputs: nil,
|
|
||||||
Outputs: nil,
|
|
||||||
|
|
||||||
// Legacy fields, keep them for backward compatibility
|
|
||||||
Constant: field.Constant,
|
|
||||||
Payable: field.Payable,
|
|
||||||
}
|
|
||||||
case "receive":
|
case "receive":
|
||||||
// New introduced function type in v0.6.0, check more detail
|
// New introduced function type in v0.6.0, check more detail
|
||||||
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
||||||
|
|
@ -201,41 +160,47 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
if field.StateMutability != "payable" {
|
if field.StateMutability != "payable" {
|
||||||
return errors.New("the statemutability of receive can only be payable")
|
return errors.New("the statemutability of receive can only be payable")
|
||||||
}
|
}
|
||||||
abi.Receive = Method{
|
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
Name: "",
|
|
||||||
RawName: "",
|
|
||||||
|
|
||||||
// The `StateMutability` can only be payable, so constant
|
|
||||||
// is always true while payable is always false.
|
|
||||||
StateMutability: field.StateMutability,
|
|
||||||
IsReceive: true,
|
|
||||||
|
|
||||||
// Receive doesn't have any input or output
|
|
||||||
Inputs: nil,
|
|
||||||
Outputs: nil,
|
|
||||||
|
|
||||||
// Legacy fields, keep them for backward compatibility
|
|
||||||
Constant: field.Constant,
|
|
||||||
Payable: field.Payable,
|
|
||||||
}
|
|
||||||
case "event":
|
case "event":
|
||||||
name := field.Name
|
name := abi.overloadedEventName(field.Name)
|
||||||
_, ok := abi.Events[name]
|
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
||||||
for idx := 0; ok; idx++ {
|
default:
|
||||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
||||||
_, ok = abi.Events[name]
|
|
||||||
}
|
|
||||||
abi.Events[name] = Event{
|
|
||||||
Name: name,
|
|
||||||
RawName: field.Name,
|
|
||||||
Anonymous: field.Anonymous,
|
|
||||||
Inputs: field.Inputs,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overloadedMethodName returns the next available name for a given function.
|
||||||
|
// Needed since solidity allows for function overload.
|
||||||
|
//
|
||||||
|
// e.g. if the abi contains Methods send, send1
|
||||||
|
// overloadedMethodName would return send2 for input send.
|
||||||
|
func (abi *ABI) overloadedMethodName(rawName string) string {
|
||||||
|
name := rawName
|
||||||
|
_, ok := abi.Methods[name]
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
_, ok = abi.Methods[name]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// overloadedEventName returns the next available name for a given event.
|
||||||
|
// Needed since solidity allows for event overload.
|
||||||
|
//
|
||||||
|
// e.g. if the abi contains events received, received1
|
||||||
|
// overloadedEventName would return received2 for input received.
|
||||||
|
func (abi *ABI) overloadedEventName(rawName string) string {
|
||||||
|
name := rawName
|
||||||
|
_, ok := abi.Events[name]
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
_, ok = abi.Events[name]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
// MethodById looks up a method by the 4-byte id
|
// MethodById looks up a method by the 4-byte id
|
||||||
// returns nil if none found
|
// returns nil if none found
|
||||||
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
|
|
@ -243,7 +208,7 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
||||||
}
|
}
|
||||||
for _, method := range abi.Methods {
|
for _, method := range abi.Methods {
|
||||||
if bytes.Equal(method.ID(), sigdata[:4]) {
|
if bytes.Equal(method.ID, sigdata[:4]) {
|
||||||
return &method, nil
|
return &method, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -254,7 +219,7 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
// ABI and returns nil if none found.
|
// ABI and returns nil if none found.
|
||||||
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
||||||
for _, event := range abi.Events {
|
for _, event := range abi.Events {
|
||||||
if bytes.Equal(event.ID().Bytes(), topic.Bytes()) {
|
if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
|
||||||
return &event, nil
|
return &event, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -263,10 +228,32 @@ func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
||||||
|
|
||||||
// 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.IsFallback
|
return abi.Fallback.Type == Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasReceive returns an indicator whether a receive function is included.
|
// HasReceive returns an indicator whether a receive function is included.
|
||||||
func (abi *ABI) HasReceive() bool {
|
func (abi *ABI) HasReceive() bool {
|
||||||
return abi.Receive.IsReceive
|
return abi.Receive.Type == Receive
|
||||||
|
}
|
||||||
|
|
||||||
|
// revertSelector is a special function selector for revert reason unpacking.
|
||||||
|
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
|
||||||
|
|
||||||
|
// UnpackRevert resolves the abi-encoded revert reason. According to the solidity
|
||||||
|
// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
|
||||||
|
// the provided revert reason is abi-encoded as if it were a call to a function
|
||||||
|
// `Error(string)`. So it's a special tool for it.
|
||||||
|
func UnpackRevert(data []byte) (string, error) {
|
||||||
|
if len(data) < 4 {
|
||||||
|
return "", errors.New("invalid data for unpacking")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(data[:4], revertSelector) {
|
||||||
|
return "", errors.New("invalid data for unpacking")
|
||||||
|
}
|
||||||
|
var reason string
|
||||||
|
typ, _ := NewType("string", "", nil)
|
||||||
|
if err := (Arguments{{Type: typ}}).Unpack(&reason, data[4:]); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return reason, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package abi
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -58,20 +59,14 @@ const jsondata2 = `
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
Uint256, _ := NewType("uint256", "", nil)
|
Uint256, _ := NewType("uint256", "", nil)
|
||||||
exp := ABI{
|
abi := ABI{
|
||||||
Methods: map[string]Method{
|
Methods: map[string]Method{
|
||||||
"balance": {
|
"balance": NewMethod("balance", "balance", Function, "view", false, false, nil, nil),
|
||||||
"balance", "balance", "view", false, false, false, false, nil, nil,
|
"send": NewMethod("send", "send", Function, "", false, false, []Argument{{"amount", Uint256, false}}, nil),
|
||||||
},
|
|
||||||
"send": {
|
|
||||||
"send", "send", "", false, false, false, false, []Argument{
|
|
||||||
{"amount", Uint256, false},
|
|
||||||
}, nil,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
exp, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
@ -173,22 +168,22 @@ func TestTestSlice(t *testing.T) {
|
||||||
|
|
||||||
func TestMethodSignature(t *testing.T) {
|
func TestMethodSignature(t *testing.T) {
|
||||||
String, _ := NewType("string", "", nil)
|
String, _ := NewType("string", "", nil)
|
||||||
m := Method{"foo", "foo", "", false, false, false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
|
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
|
||||||
exp := "foo(string,string)"
|
exp := "foo(string,string)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
idexp := crypto.Keccak256([]byte(exp))[:4]
|
idexp := crypto.Keccak256([]byte(exp))[:4]
|
||||||
if !bytes.Equal(m.ID(), idexp) {
|
if !bytes.Equal(m.ID, idexp) {
|
||||||
t.Errorf("expected ids to match %x != %x", m.ID(), idexp)
|
t.Errorf("expected ids to match %x != %x", m.ID, idexp)
|
||||||
}
|
}
|
||||||
|
|
||||||
uintt, _ := NewType("uint256", "", nil)
|
uintt, _ := NewType("uint256", "", nil)
|
||||||
m = Method{"foo", "foo", "", false, false, false, false, []Argument{{"bar", uintt, false}}, nil}
|
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", uintt, false}}, nil)
|
||||||
exp = "foo(uint256)"
|
exp = "foo(uint256)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method with tuple arguments
|
// Method with tuple arguments
|
||||||
|
|
@ -204,10 +199,10 @@ func TestMethodSignature(t *testing.T) {
|
||||||
{Name: "y", Type: "int256"},
|
{Name: "y", Type: "int256"},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
m = Method{"foo", "foo", "", false, false, false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
|
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil)
|
||||||
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,12 +214,12 @@ func TestOverloadedMethodSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
check := func(name string, expect string, method bool) {
|
check := func(name string, expect string, method bool) {
|
||||||
if method {
|
if method {
|
||||||
if abi.Methods[name].Sig() != expect {
|
if abi.Methods[name].Sig != expect {
|
||||||
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig())
|
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if abi.Events[name].Sig() != expect {
|
if abi.Events[name].Sig != expect {
|
||||||
t.Fatalf("The signature of overloaded event mismatch, want %s, have %s", expect, abi.Events[name].Sig())
|
t.Fatalf("The signature of overloaded event mismatch, want %s, have %s", expect, abi.Events[name].Sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -921,13 +916,13 @@ func TestABI_MethodById(t *testing.T) {
|
||||||
}
|
}
|
||||||
for name, m := range abi.Methods {
|
for name, m := range abi.Methods {
|
||||||
a := fmt.Sprintf("%v", m)
|
a := fmt.Sprintf("%v", m)
|
||||||
m2, err := abi.MethodById(m.ID())
|
m2, err := abi.MethodById(m.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to look up ABI method: %v", err)
|
t.Fatalf("Failed to look up ABI method: %v", err)
|
||||||
}
|
}
|
||||||
b := fmt.Sprintf("%v", m2)
|
b := fmt.Sprintf("%v", m2)
|
||||||
if a != b {
|
if a != b {
|
||||||
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID())
|
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Also test empty
|
// Also test empty
|
||||||
|
|
@ -995,8 +990,8 @@ func TestABI_EventById(t *testing.T) {
|
||||||
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
|
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.ID() != topicID {
|
if event.ID != topicID {
|
||||||
t.Errorf("Event id %s does not match topic %s, test #%d", event.ID().Hex(), topicID.Hex(), testnum)
|
t.Errorf("Event id %s does not match topic %s, test #%d", event.ID.Hex(), topicID.Hex(), testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
|
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
|
||||||
|
|
@ -1051,3 +1046,59 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
t.Fatalf("Should not have found extra method")
|
t.Fatalf("Should not have found extra method")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestUnnamedEventParam checks that an event with unnamed parameters is
|
||||||
|
// correctly handled
|
||||||
|
// The test runs the abi of the following contract.
|
||||||
|
// contract TestEvent {
|
||||||
|
// event send(uint256, uint256);
|
||||||
|
// }
|
||||||
|
func TestUnnamedEventParam(t *testing.T) {
|
||||||
|
abiJSON := `[{ "anonymous": false, "inputs": [{ "indexed": false,"internalType": "uint256", "name": "","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "","type": "uint256"}],"name": "send","type": "event"}]`
|
||||||
|
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
event, ok := contractAbi.Events["send"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Could not find event")
|
||||||
|
}
|
||||||
|
if event.Inputs[0].Name != "arg0" {
|
||||||
|
t.Fatalf("Could not find input")
|
||||||
|
}
|
||||||
|
if event.Inputs[1].Name != "arg1" {
|
||||||
|
t.Fatalf("Could not find input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpackRevert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var cases = []struct {
|
||||||
|
input string
|
||||||
|
expect string
|
||||||
|
expectErr error
|
||||||
|
}{
|
||||||
|
{"", "", errors.New("invalid data for unpacking")},
|
||||||
|
{"08c379a1", "", errors.New("invalid data for unpacking")},
|
||||||
|
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
|
||||||
|
}
|
||||||
|
for index, c := range cases {
|
||||||
|
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
|
||||||
|
got, err := UnpackRevert(common.Hex2Bytes(c.input))
|
||||||
|
if c.expectErr != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected non-nil error")
|
||||||
|
}
|
||||||
|
if err.Error() != c.expectErr.Error() {
|
||||||
|
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.expect != got {
|
||||||
|
t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,9 +92,8 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
if len(arguments) != 0 {
|
if len(arguments) != 0 {
|
||||||
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
} else {
|
|
||||||
return nil // Nothing to unmarshal, return
|
|
||||||
}
|
}
|
||||||
|
return nil // Nothing to unmarshal, return
|
||||||
}
|
}
|
||||||
// make sure the passed value is arguments pointer
|
// make sure the passed value is arguments pointer
|
||||||
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||||
|
|
@ -104,6 +103,9 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(marshalledValues) == 0 {
|
||||||
|
return fmt.Errorf("abi: Unpack(no-values unmarshalled %T)", v)
|
||||||
|
}
|
||||||
if arguments.isTuple() {
|
if arguments.isTuple() {
|
||||||
return arguments.unpackTuple(v, marshalledValues)
|
return arguments.unpackTuple(v, marshalledValues)
|
||||||
}
|
}
|
||||||
|
|
@ -112,18 +114,24 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
|
|
||||||
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
|
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
|
||||||
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
||||||
|
// Make sure map is not nil
|
||||||
|
if v == nil {
|
||||||
|
return fmt.Errorf("abi: cannot unpack into a nil map")
|
||||||
|
}
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
if len(arguments) != 0 {
|
if len(arguments) != 0 {
|
||||||
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
} else {
|
|
||||||
return nil // Nothing to unmarshal, return
|
|
||||||
}
|
}
|
||||||
|
return nil // Nothing to unmarshal, return
|
||||||
}
|
}
|
||||||
marshalledValues, err := arguments.UnpackValues(data)
|
marshalledValues, err := arguments.UnpackValues(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return arguments.unpackIntoMap(v, marshalledValues)
|
for i, arg := range arguments.NonIndexed() {
|
||||||
|
v[arg.Name] = marshalledValues[i]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpack sets the unmarshalled value to go format.
|
// unpack sets the unmarshalled value to go format.
|
||||||
|
|
@ -195,19 +203,6 @@ func unpack(t *Type, dst interface{}, src interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpackIntoMap unpacks marshalledValues into the provided map[string]interface{}
|
|
||||||
func (arguments Arguments) unpackIntoMap(v map[string]interface{}, marshalledValues []interface{}) error {
|
|
||||||
// Make sure map is not nil
|
|
||||||
if v == nil {
|
|
||||||
return fmt.Errorf("abi: cannot unpack into a nil map")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, arg := range arguments.NonIndexed() {
|
|
||||||
v[arg.Name] = marshalledValues[i]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
||||||
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
|
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
|
||||||
if arguments.LengthNonIndexed() == 0 {
|
if arguments.LengthNonIndexed() == 0 {
|
||||||
|
|
@ -233,30 +228,28 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interfac
|
||||||
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
|
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
|
||||||
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
||||||
var (
|
var (
|
||||||
value = reflect.ValueOf(v).Elem()
|
value = reflect.ValueOf(v).Elem()
|
||||||
typ = value.Type()
|
typ = value.Type()
|
||||||
kind = value.Kind()
|
kind = value.Kind()
|
||||||
|
nonIndexedArgs = arguments.NonIndexed()
|
||||||
)
|
)
|
||||||
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
|
if err := requireUnpackKind(value, len(nonIndexedArgs), arguments); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the interface is a struct, get of abi->struct_field mapping
|
// If the interface is a struct, get of abi->struct_field mapping
|
||||||
var abi2struct map[string]string
|
var abi2struct map[string]string
|
||||||
if kind == reflect.Struct {
|
if kind == reflect.Struct {
|
||||||
var (
|
argNames := make([]string, len(nonIndexedArgs))
|
||||||
argNames []string
|
for i, arg := range nonIndexedArgs {
|
||||||
err error
|
argNames[i] = arg.Name
|
||||||
)
|
|
||||||
for _, arg := range arguments.NonIndexed() {
|
|
||||||
argNames = append(argNames, arg.Name)
|
|
||||||
}
|
}
|
||||||
abi2struct, err = mapArgNamesToStructFields(argNames, value)
|
var err error
|
||||||
if err != nil {
|
if abi2struct, err = mapArgNamesToStructFields(argNames, value); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, arg := range arguments.NonIndexed() {
|
for i, arg := range nonIndexedArgs {
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
field := value.FieldByName(abi2struct[arg.Name])
|
field := value.FieldByName(abi2struct[arg.Name])
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"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"
|
||||||
|
|
@ -49,7 +50,6 @@ var (
|
||||||
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
||||||
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
||||||
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
||||||
errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
||||||
|
|
@ -349,8 +349,11 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Return(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingCallContract executes a contract call on the pending state.
|
// PendingCallContract executes a contract call on the pending state.
|
||||||
|
|
@ -359,8 +362,11 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||||
|
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Return(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
||||||
|
|
@ -398,22 +404,33 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
cap = hi
|
cap = hi
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
call.Gas = gas
|
call.Gas = gas
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
snapshot := b.pendingState.Snapshot()
|
||||||
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
b.pendingState.RevertToSnapshot(snapshot)
|
b.pendingState.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
if err != nil || failed {
|
if err != nil {
|
||||||
return false
|
if err == core.ErrIntrinsicGas {
|
||||||
|
return true, nil, nil // Special case, raise gas limit
|
||||||
|
}
|
||||||
|
return true, nil, err // Bail out
|
||||||
}
|
}
|
||||||
return true
|
return res.Failed(), res, nil
|
||||||
}
|
}
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
// Execute the binary search and hone in on an executable gas limit
|
||||||
for lo+1 < hi {
|
for lo+1 < hi {
|
||||||
mid := (hi + lo) / 2
|
mid := (hi + lo) / 2
|
||||||
if !executable(mid) {
|
failed, _, err := executable(mid)
|
||||||
|
|
||||||
|
// If the error is not nil(consensus error), it means the provided message
|
||||||
|
// call or transaction will never be accepted no matter how much gas it is
|
||||||
|
// assigned. Return the error directly, don't struggle any more
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
} else {
|
} else {
|
||||||
hi = mid
|
hi = mid
|
||||||
|
|
@ -421,8 +438,25 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
}
|
}
|
||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||||
if hi == cap {
|
if hi == cap {
|
||||||
if !executable(hi) {
|
failed, result, err := executable(hi)
|
||||||
return 0, errGasEstimationFailed
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||||
|
errMsg := fmt.Sprintf("always failing transaction (%v)", result.Err)
|
||||||
|
if len(result.Revert()) > 0 {
|
||||||
|
ret, err := abi.UnpackRevert(result.Revert())
|
||||||
|
if err != nil {
|
||||||
|
errMsg += fmt.Sprintf(" (%#x)", result.Revert())
|
||||||
|
} else {
|
||||||
|
errMsg += fmt.Sprintf(" (%s)", ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
// Otherwise, the specified gas cap is too low
|
||||||
|
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hi, nil
|
return hi, nil
|
||||||
|
|
@ -430,7 +464,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
|
|
||||||
// callContract implements common code between normal and pending contract calls.
|
// callContract implements common code between normal and pending contract calls.
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
// state is modified during execution, make sure to copy it if necessary.
|
||||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
|
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) {
|
||||||
// Ensure message is initialized properly.
|
// Ensure message is initialized properly.
|
||||||
if call.GasPrice == nil {
|
if call.GasPrice == nil {
|
||||||
call.GasPrice = big.NewInt(1)
|
call.GasPrice = big.NewInt(1)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package backends
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -356,25 +357,112 @@ func TestSimulatedBackend_TransactionByHash(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimulatedBackend_EstimateGas(t *testing.T) {
|
func TestSimulatedBackend_EstimateGas(t *testing.T) {
|
||||||
sim := NewSimulatedBackend(
|
/*
|
||||||
core.GenesisAlloc{}, 10000000,
|
pragma solidity ^0.6.4;
|
||||||
)
|
contract GasEstimation {
|
||||||
|
function PureRevert() public { revert(); }
|
||||||
|
function Revert() public { revert("revert reason");}
|
||||||
|
function OOG() public { for (uint i = 0; ; i++) {}}
|
||||||
|
function Assert() public { assert(false);}
|
||||||
|
function Valid() public {}
|
||||||
|
}*/
|
||||||
|
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
||||||
|
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
||||||
|
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
opts := bind.NewKeyedTransactor(key)
|
||||||
|
|
||||||
|
sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
bgCtx := context.Background()
|
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
||||||
|
|
||||||
gas, err := sim.EstimateGas(bgCtx, ethereum.CallMsg{
|
parsed, _ := abi.JSON(strings.NewReader(contractAbi))
|
||||||
From: testAddr,
|
contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim)
|
||||||
To: &testAddr,
|
sim.Commit()
|
||||||
Value: big.NewInt(1000),
|
|
||||||
Data: []byte{},
|
var cases = []struct {
|
||||||
})
|
name string
|
||||||
if err != nil {
|
message ethereum.CallMsg
|
||||||
t.Errorf("could not estimate gas: %v", err)
|
expect uint64
|
||||||
|
expectError error
|
||||||
|
}{
|
||||||
|
{"plain transfer(valid)", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &addr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Data: nil,
|
||||||
|
}, params.TxGas, nil},
|
||||||
|
|
||||||
|
{"plain transfer(invalid)", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Data: nil,
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
||||||
|
|
||||||
|
{"Revert", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("d8b98391"),
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted) (revert reason)")},
|
||||||
|
|
||||||
|
{"PureRevert", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("aa8b1d30"),
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
||||||
|
|
||||||
|
{"OOG", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("50f6fe34"),
|
||||||
|
}, 0, errors.New("gas required exceeds allowance (100000)")},
|
||||||
|
|
||||||
|
{"Assert", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("b9b046f9"),
|
||||||
|
}, 0, errors.New("always failing transaction (invalid opcode: opcode 0xfe not defined)")},
|
||||||
|
|
||||||
|
{"Valid", ethereum.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("e09fface"),
|
||||||
|
}, 21275, nil},
|
||||||
}
|
}
|
||||||
|
for _, c := range cases {
|
||||||
if gas != params.TxGas {
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||||
t.Errorf("expected 21000 gas cost for a transaction got %v", gas)
|
if c.expectError != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expect error, got nil")
|
||||||
|
}
|
||||||
|
if c.expectError.Error() != err.Error() {
|
||||||
|
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if got != c.expect {
|
||||||
|
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
||||||
opts = new(FilterOpts)
|
opts = new(FilterOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
||||||
|
|
||||||
topics, err := makeTopics(query...)
|
topics, err := makeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -313,7 +313,7 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
||||||
opts = new(WatchOpts)
|
opts = new(WatchOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
||||||
|
|
||||||
topics, err := makeTopics(query...)
|
topics, err := makeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -639,9 +639,9 @@ func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
|
||||||
state = state + " "
|
state = state + " "
|
||||||
}
|
}
|
||||||
identity := fmt.Sprintf("function %v", method.RawName)
|
identity := fmt.Sprintf("function %v", method.RawName)
|
||||||
if method.IsFallback {
|
if method.Type == abi.Fallback {
|
||||||
identity = "fallback"
|
identity = "fallback"
|
||||||
} else if method.IsReceive {
|
} else if method.Type == abi.Receive {
|
||||||
identity = "receive"
|
identity = "receive"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", "))
|
return fmt.Sprintf("%s(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", "))
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,8 @@ var bindTests = []struct {
|
||||||
{"type":"event","name":"indexed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256","indexed":true}]},
|
{"type":"event","name":"indexed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256","indexed":true}]},
|
||||||
{"type":"event","name":"mixed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256"}]},
|
{"type":"event","name":"mixed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256"}]},
|
||||||
{"type":"event","name":"anonymous","anonymous":true,"inputs":[]},
|
{"type":"event","name":"anonymous","anonymous":true,"inputs":[]},
|
||||||
{"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]}
|
{"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]},
|
||||||
|
{"type":"event","name":"unnamed","inputs":[{"name":"","type":"uint256","indexed": true},{"name":"","type":"uint256","indexed":true}]}
|
||||||
]
|
]
|
||||||
`},
|
`},
|
||||||
`
|
`
|
||||||
|
|
@ -249,6 +250,12 @@ var bindTests = []struct {
|
||||||
fmt.Println(event.Addr) // Make sure the reconstructed indexed fields are present
|
fmt.Println(event.Addr) // Make sure the reconstructed indexed fields are present
|
||||||
|
|
||||||
fmt.Println(res, str, dat, hash, err)
|
fmt.Println(res, str, dat, hash, err)
|
||||||
|
|
||||||
|
oit, err := e.FilterUnnamed(nil, []*big.Int{}, []*big.Int{})
|
||||||
|
|
||||||
|
arg0 := oit.Event.Arg0 // Make sure unnamed arguments are handled correctly
|
||||||
|
arg1 := oit.Event.Arg1 // Make sure unnamed arguments are handled correctly
|
||||||
|
fmt.Println(arg0, arg1)
|
||||||
}
|
}
|
||||||
// Run a tiny reflection test to ensure disallowed methods don't appear
|
// Run a tiny reflection test to ensure disallowed methods don't appear
|
||||||
if _, ok := reflect.TypeOf(&EventChecker{}).MethodByName("FilterAnonymous"); ok {
|
if _, ok := reflect.TypeOf(&EventChecker{}).MethodByName("FilterAnonymous"); ok {
|
||||||
|
|
|
||||||
|
|
@ -42,36 +42,59 @@ type Event struct {
|
||||||
RawName string
|
RawName string
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
|
str string
|
||||||
|
// Sig contains the string signature according to the ABI spec.
|
||||||
|
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||||
|
// Please note that "int" is substitute for its canonical representation "int256"
|
||||||
|
Sig string
|
||||||
|
// ID returns the canonical representation of the event's signature used by the
|
||||||
|
// abi definition to identify event names and types.
|
||||||
|
ID common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEvent creates a new Event.
|
||||||
|
// It sanitizes the input arguments to remove unnamed arguments.
|
||||||
|
// It also precomputes the id, signature and string representation
|
||||||
|
// of the event.
|
||||||
|
func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
|
||||||
|
// sanitize inputs to remove inputs without names
|
||||||
|
// and precompute string and sig representation.
|
||||||
|
names := make([]string, len(inputs))
|
||||||
|
types := make([]string, len(inputs))
|
||||||
|
for i, input := range inputs {
|
||||||
|
if input.Name == "" {
|
||||||
|
inputs[i] = Argument{
|
||||||
|
Name: fmt.Sprintf("arg%d", i),
|
||||||
|
Indexed: input.Indexed,
|
||||||
|
Type: input.Type,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inputs[i] = input
|
||||||
|
}
|
||||||
|
// string representation
|
||||||
|
names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
|
||||||
|
if input.Indexed {
|
||||||
|
names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
|
||||||
|
}
|
||||||
|
// sig representation
|
||||||
|
types[i] = input.Type.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("event %v(%v)", rawName, strings.Join(names, ", "))
|
||||||
|
sig := fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
||||||
|
id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
|
||||||
|
|
||||||
|
return Event{
|
||||||
|
Name: name,
|
||||||
|
RawName: rawName,
|
||||||
|
Anonymous: anonymous,
|
||||||
|
Inputs: inputs,
|
||||||
|
str: str,
|
||||||
|
Sig: sig,
|
||||||
|
ID: id,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Event) String() string {
|
func (e Event) String() string {
|
||||||
inputs := make([]string, len(e.Inputs))
|
return e.str
|
||||||
for i, input := range e.Inputs {
|
|
||||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
|
||||||
if input.Indexed {
|
|
||||||
inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("event %v(%v)", e.RawName, strings.Join(inputs, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sig returns the event string signature according to the ABI spec.
|
|
||||||
//
|
|
||||||
// Example
|
|
||||||
//
|
|
||||||
// event foo(uint32 a, int b) = "foo(uint32,int256)"
|
|
||||||
//
|
|
||||||
// Please note that "int" is substitute for its canonical representation "int256"
|
|
||||||
func (e Event) Sig() string {
|
|
||||||
types := make([]string, len(e.Inputs))
|
|
||||||
for i, input := range e.Inputs {
|
|
||||||
types[i] = input.Type.String()
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%v(%v)", e.RawName, strings.Join(types, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns the canonical representation of the event's signature used by the
|
|
||||||
// abi definition to identify event names and types.
|
|
||||||
func (e Event) ID() common.Hash {
|
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(e.Sig())))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,8 @@ func TestEventId(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, event := range abi.Events {
|
for name, event := range abi.Events {
|
||||||
if event.ID() != test.expectations[name] {
|
if event.ID != test.expectations[name] {
|
||||||
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.ID())
|
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,24 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FunctionType represents different types of functions a contract might have.
|
||||||
|
type FunctionType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Constructor represents the constructor of the contract.
|
||||||
|
// The constructor function is called while deploying a contract.
|
||||||
|
Constructor FunctionType = iota
|
||||||
|
// Fallback represents the fallback function.
|
||||||
|
// This function is executed if no other function matches the given function
|
||||||
|
// signature and no receive function is specified.
|
||||||
|
Fallback
|
||||||
|
// Receive represents the receive function.
|
||||||
|
// This function is executed on plain Ether transfers.
|
||||||
|
Receive
|
||||||
|
// Function represents a normal function.
|
||||||
|
Function
|
||||||
|
)
|
||||||
|
|
||||||
// Method represents a callable given a `Name` and whether the method is a constant.
|
// Method represents a callable given a `Name` and whether the method is a constant.
|
||||||
// If the method is `Const` no transaction needs to be created for this
|
// If the method is `Const` no transaction needs to be created for this
|
||||||
// particular Method call. It can easily be simulated using a local VM.
|
// particular Method call. It can easily be simulated using a local VM.
|
||||||
|
|
@ -44,6 +62,10 @@ type Method struct {
|
||||||
Name string
|
Name string
|
||||||
RawName string // RawName is the raw method name parsed from ABI
|
RawName string // RawName is the raw method name parsed from ABI
|
||||||
|
|
||||||
|
// Type indicates whether the method is a
|
||||||
|
// special fallback introduced in solidity v0.6.0
|
||||||
|
Type FunctionType
|
||||||
|
|
||||||
// StateMutability indicates the mutability state of method,
|
// StateMutability indicates the mutability state of method,
|
||||||
// the default value is nonpayable. It can be empty if the abi
|
// the default value is nonpayable. It can be empty if the abi
|
||||||
// is generated by legacy compiler.
|
// is generated by legacy compiler.
|
||||||
|
|
@ -53,69 +75,84 @@ type Method struct {
|
||||||
Constant bool
|
Constant bool
|
||||||
Payable bool
|
Payable bool
|
||||||
|
|
||||||
// The following two flags indicates whether the method is a
|
|
||||||
// special fallback introduced in solidity v0.6.0
|
|
||||||
IsFallback bool
|
|
||||||
IsReceive bool
|
|
||||||
|
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
Outputs Arguments
|
Outputs Arguments
|
||||||
|
str string
|
||||||
|
// Sig returns the methods string signature according to the ABI spec.
|
||||||
|
// e.g. function foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||||
|
// Please note that "int" is substitute for its canonical representation "int256"
|
||||||
|
Sig string
|
||||||
|
// ID returns the canonical representation of the method's signature used by the
|
||||||
|
// abi definition to identify method names and types.
|
||||||
|
ID []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sig returns the methods string signature according to the ABI spec.
|
// NewMethod creates a new Method.
|
||||||
//
|
// A method should always be created using NewMethod.
|
||||||
// Example
|
// It also precomputes the sig representation and the string representation
|
||||||
//
|
// of the method.
|
||||||
// function foo(uint32 a, int b) = "foo(uint32,int256)"
|
func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method {
|
||||||
//
|
var (
|
||||||
// Please note that "int" is substitute for its canonical representation "int256"
|
types = make([]string, len(inputs))
|
||||||
func (method Method) Sig() string {
|
inputNames = make([]string, len(inputs))
|
||||||
// Short circuit if the method is special. Fallback
|
outputNames = make([]string, len(outputs))
|
||||||
// and Receive don't have signature at all.
|
)
|
||||||
if method.IsFallback || method.IsReceive {
|
for i, input := range inputs {
|
||||||
return ""
|
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||||
}
|
|
||||||
types := make([]string, len(method.Inputs))
|
|
||||||
for i, input := range method.Inputs {
|
|
||||||
types[i] = input.Type.String()
|
types[i] = input.Type.String()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%v(%v)", method.RawName, strings.Join(types, ","))
|
for i, output := range outputs {
|
||||||
}
|
outputNames[i] = output.Type.String()
|
||||||
|
|
||||||
func (method Method) String() string {
|
|
||||||
inputs := make([]string, len(method.Inputs))
|
|
||||||
for i, input := range method.Inputs {
|
|
||||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
|
||||||
}
|
|
||||||
outputs := make([]string, len(method.Outputs))
|
|
||||||
for i, output := range method.Outputs {
|
|
||||||
outputs[i] = output.Type.String()
|
|
||||||
if len(output.Name) > 0 {
|
if len(output.Name) > 0 {
|
||||||
outputs[i] += fmt.Sprintf(" %v", output.Name)
|
outputNames[i] += fmt.Sprintf(" %v", output.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// calculate the signature and method id. Note only function
|
||||||
|
// has meaningful signature and id.
|
||||||
|
var (
|
||||||
|
sig string
|
||||||
|
id []byte
|
||||||
|
)
|
||||||
|
if funType == Function {
|
||||||
|
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
||||||
|
id = crypto.Keccak256([]byte(sig))[:4]
|
||||||
|
}
|
||||||
// Extract meaningful state mutability of solidity method.
|
// Extract meaningful state mutability of solidity method.
|
||||||
// If it's default value, never print it.
|
// If it's default value, never print it.
|
||||||
state := method.StateMutability
|
state := mutability
|
||||||
if state == "nonpayable" {
|
if state == "nonpayable" {
|
||||||
state = ""
|
state = ""
|
||||||
}
|
}
|
||||||
if state != "" {
|
if state != "" {
|
||||||
state = state + " "
|
state = state + " "
|
||||||
}
|
}
|
||||||
identity := fmt.Sprintf("function %v", method.RawName)
|
identity := fmt.Sprintf("function %v", rawName)
|
||||||
if method.IsFallback {
|
if funType == Fallback {
|
||||||
identity = "fallback"
|
identity = "fallback"
|
||||||
} else if method.IsReceive {
|
} else if funType == Receive {
|
||||||
identity = "receive"
|
identity = "receive"
|
||||||
|
} else if funType == Constructor {
|
||||||
|
identity = "constructor"
|
||||||
|
}
|
||||||
|
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
|
||||||
|
|
||||||
|
return Method{
|
||||||
|
Name: name,
|
||||||
|
RawName: rawName,
|
||||||
|
Type: funType,
|
||||||
|
StateMutability: mutability,
|
||||||
|
Constant: isConst,
|
||||||
|
Payable: isPayable,
|
||||||
|
Inputs: inputs,
|
||||||
|
Outputs: outputs,
|
||||||
|
str: str,
|
||||||
|
Sig: sig,
|
||||||
|
ID: id,
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns the canonical representation of the method's signature used by the
|
func (method Method) String() string {
|
||||||
// abi definition to identify method names and types.
|
return method.str
|
||||||
func (method Method) ID() []byte {
|
|
||||||
return crypto.Keccak256([]byte(method.Sig()))[:4]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsConstant returns the indicator whether the method is read-only.
|
// IsConstant returns the indicator whether the method is read-only.
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ func TestMethodSig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range cases {
|
for _, test := range cases {
|
||||||
got := abi.Methods[test.method].Sig()
|
got := abi.Methods[test.method].Sig
|
||||||
if got != test.expect {
|
if got != test.expect {
|
||||||
t.Errorf("expected string to be %s, got %s", test.expect, got)
|
t.Errorf("expected string to be %s, got %s", test.expect, got)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -634,7 +634,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := abi.Methods["slice"].ID()
|
sig := abi.Methods["slice"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
||||||
|
|
@ -648,7 +648,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrA, addrB = common.Address{1}, common.Address{2}
|
var addrA, addrB = common.Address{1}, common.Address{2}
|
||||||
sig = abi.Methods["sliceAddress"].ID()
|
sig = abi.Methods["sliceAddress"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
||||||
|
|
@ -663,7 +663,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrC, addrD = common.Address{3}, common.Address{4}
|
var addrC, addrD = common.Address{3}, common.Address{4}
|
||||||
sig = abi.Methods["sliceMultiAddress"].ID()
|
sig = abi.Methods["sliceMultiAddress"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
@ -681,7 +681,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["slice256"].ID()
|
sig = abi.Methods["slice256"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
||||||
|
|
@ -695,7 +695,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
||||||
sig = abi.Methods["nestedArray"].ID()
|
sig = abi.Methods["nestedArray"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
@ -712,7 +712,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["nestedArray2"].ID()
|
sig = abi.Methods["nestedArray2"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
||||||
|
|
@ -728,7 +728,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["nestedSlice"].ID()
|
sig = abi.Methods["nestedSlice"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
||||||
|
|
|
||||||
|
|
@ -118,18 +118,16 @@ func requireAssignable(dst, src reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
|
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
|
||||||
func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
|
func requireUnpackKind(v reflect.Value, minLength int, args Arguments) error {
|
||||||
args Arguments) error {
|
switch v.Kind() {
|
||||||
|
|
||||||
switch k {
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
case reflect.Slice, reflect.Array:
|
case reflect.Slice, reflect.Array:
|
||||||
if minLen := args.LengthNonIndexed(); v.Len() < minLen {
|
if v.Len() < minLength {
|
||||||
return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
|
return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
|
||||||
minLen, v.Len())
|
minLength, v.Len())
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi: cannot unmarshal tuple into %v", t)
|
return fmt.Errorf("abi: cannot unmarshal tuple into %v", v.Type())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -156,9 +154,8 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// skip fields that have no abi:"" tag.
|
// skip fields that have no abi:"" tag.
|
||||||
var ok bool
|
tagName, ok := typ.Field(i).Tag.Lookup("abi")
|
||||||
var tagName string
|
if !ok {
|
||||||
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// check if tag is empty.
|
// check if tag is empty.
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -67,7 +66,8 @@ type KeyStore struct {
|
||||||
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
|
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
|
||||||
updating bool // Whether the event notification loop is running
|
updating bool // Whether the event notification loop is running
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
importMu sync.Mutex // Import Mutex locks the import to prevent two insertions from racing
|
||||||
}
|
}
|
||||||
|
|
||||||
type unlocked struct {
|
type unlocked struct {
|
||||||
|
|
@ -443,14 +443,21 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
ks.importMu.Lock()
|
||||||
|
defer ks.importMu.Unlock()
|
||||||
|
if ks.cache.hasAddress(key.Address) {
|
||||||
|
return accounts.Account{}, errors.New("account already exists")
|
||||||
|
}
|
||||||
return ks.importKey(key, newPassphrase)
|
return ks.importKey(key, newPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
|
// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
|
||||||
func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
|
func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
|
||||||
key := newKeyFromECDSA(priv)
|
key := newKeyFromECDSA(priv)
|
||||||
|
ks.importMu.Lock()
|
||||||
|
defer ks.importMu.Unlock()
|
||||||
if ks.cache.hasAddress(key.Address) {
|
if ks.cache.hasAddress(key.Address) {
|
||||||
return accounts.Account{}, fmt.Errorf("account already exists")
|
return accounts.Account{}, errors.New("account already exists")
|
||||||
}
|
}
|
||||||
return ks.importKey(key, passphrase)
|
return ks.importKey(key, passphrase)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -338,6 +341,88 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
checkEvents(t, wantEvents, events)
|
checkEvents(t, wantEvents, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestImportExport tests the import functionality of a keystore.
|
||||||
|
func TestImportECDSA(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate key: %v", key)
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "old"); err != nil {
|
||||||
|
t.Errorf("importing failed: %v", err)
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "old"); err == nil {
|
||||||
|
t.Errorf("importing same key twice succeeded")
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "new"); err == nil {
|
||||||
|
t.Errorf("importing same key twice succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||||
|
func TestImportExport(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
acc, err := ks.NewAccount("old")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
|
}
|
||||||
|
json, err := ks.Export(acc, "old", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
|
}
|
||||||
|
dir2, ks2 := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir2)
|
||||||
|
if _, err = ks2.Import(json, "old", "old"); err == nil {
|
||||||
|
t.Errorf("importing with invalid password succeeded")
|
||||||
|
}
|
||||||
|
acc2, err := ks2.Import(json, "new", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("importing failed: %v", err)
|
||||||
|
}
|
||||||
|
if acc.Address != acc2.Address {
|
||||||
|
t.Error("imported account does not match exported account")
|
||||||
|
}
|
||||||
|
if _, err = ks2.Import(json, "new", "new"); err == nil {
|
||||||
|
t.Errorf("importing a key twice succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestImportRace tests the keystore on races.
|
||||||
|
// This test should fail under -race if importing races.
|
||||||
|
func TestImportRace(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
acc, err := ks.NewAccount("old")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
|
}
|
||||||
|
json, err := ks.Export(acc, "old", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
|
}
|
||||||
|
dir2, ks2 := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir2)
|
||||||
|
var atom uint32
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
||||||
|
atomic.AddUint32(&atom, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
if atom != 1 {
|
||||||
|
t.Errorf("Import is racy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// checkAccounts checks that all known live accounts are present in the wallet list.
|
// checkAccounts checks that all known live accounts are present in the wallet list.
|
||||||
func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) {
|
func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) {
|
||||||
if len(live) != len(wallets) {
|
if len(live) != len(wallets) {
|
||||||
|
|
|
||||||
|
|
@ -592,15 +592,16 @@ func signer(c *cli.Context) error {
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
||||||
listener, 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)
|
||||||
}
|
}
|
||||||
extapiURL = fmt.Sprintf("http://%v/", listener.Addr())
|
extapiURL = fmt.Sprintf("http://%v/", addr)
|
||||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
listener.Close()
|
// Don't bother imposing a timeout here.
|
||||||
|
httpServer.Shutdown(context.Background())
|
||||||
log.Info("HTTP endpoint closed", "url", extapiURL)
|
log.Info("HTTP endpoint closed", "url", extapiURL)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,18 +89,23 @@ Path of the secret key file: .*UTC--.+--[0-9a-f]{40}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountImport(t *testing.T) {
|
func TestAccountImport(t *testing.T) {
|
||||||
tests := []struct{ key, output string }{
|
tests := []struct{ name, key, output string }{
|
||||||
{
|
{
|
||||||
|
name: "correct account",
|
||||||
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||||
output: "Address: {fcad0b19bb29d4674531d6f115237e16afce377c}\n",
|
output: "Address: {fcad0b19bb29d4674531d6f115237e16afce377c}\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "invalid character",
|
||||||
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1",
|
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1",
|
||||||
output: "Fatal: Failed to load the private key: invalid character '1' at end of key file\n",
|
output: "Fatal: Failed to load the private key: invalid character '1' at end of key file\n",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
importAccountWithExpect(t, test.key, test.output)
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
importAccountWithExpect(t, test.key, test.output)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
@ -147,12 +146,6 @@ func enableWhisper(ctx *cli.Context) bool {
|
||||||
|
|
||||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
stack, cfg := makeConfigNode(ctx)
|
stack, cfg := makeConfigNode(ctx)
|
||||||
if ctx.GlobalIsSet(utils.OverrideIstanbulFlag.Name) {
|
|
||||||
cfg.Eth.OverrideIstanbul = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideIstanbulFlag.Name))
|
|
||||||
}
|
|
||||||
if ctx.GlobalIsSet(utils.OverrideMuirGlacierFlag.Name) {
|
|
||||||
cfg.Eth.OverrideMuirGlacier = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideMuirGlacierFlag.Name))
|
|
||||||
}
|
|
||||||
utils.RegisterEthService(stack, &cfg.Eth)
|
utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
||||||
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,6 @@ var (
|
||||||
utils.ExternalSignerFlag,
|
utils.ExternalSignerFlag,
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.SmartCardDaemonPathFlag,
|
utils.SmartCardDaemonPathFlag,
|
||||||
utils.OverrideIstanbulFlag,
|
|
||||||
utils.OverrideMuirGlacierFlag,
|
|
||||||
utils.EthashCacheDirFlag,
|
utils.EthashCacheDirFlag,
|
||||||
utils.EthashCachesInMemoryFlag,
|
utils.EthashCachesInMemoryFlag,
|
||||||
utils.EthashCachesOnDiskFlag,
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
|
|
||||||
|
|
@ -678,7 +678,7 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
|
||||||
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
@ -788,7 +788,7 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
|
||||||
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
@ -905,7 +905,7 @@ func retesteth(ctx *cli.Context) error {
|
||||||
IdleTimeout: 120 * time.Second,
|
IdleTimeout: 120 * time.Second,
|
||||||
}
|
}
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.RPCListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name))
|
httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.RPCListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name))
|
||||||
listener, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, handler)
|
httpServer, _, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, 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)
|
||||||
}
|
}
|
||||||
|
|
@ -913,7 +913,8 @@ func retesteth(ctx *cli.Context) error {
|
||||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
listener.Close()
|
// Don't bother imposing a timeout here.
|
||||||
|
httpServer.Shutdown(context.Background())
|
||||||
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,14 +241,6 @@ var (
|
||||||
Name: "whitelist",
|
Name: "whitelist",
|
||||||
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
||||||
}
|
}
|
||||||
OverrideIstanbulFlag = cli.Uint64Flag{
|
|
||||||
Name: "override.istanbul",
|
|
||||||
Usage: "Manually specify Istanbul fork-block, overriding the bundled setting",
|
|
||||||
}
|
|
||||||
OverrideMuirGlacierFlag = cli.Uint64Flag{
|
|
||||||
Name: "override.muirglacier",
|
|
||||||
Usage: "Manually specify Muir Glacier fork-block, overriding the bundled setting",
|
|
||||||
}
|
|
||||||
// Light server and client settings
|
// Light server and client settings
|
||||||
LightLegacyServFlag = cli.IntFlag{ // Deprecated in favor of light.serve, remove in 2021
|
LightLegacyServFlag = cli.IntFlag{ // Deprecated in favor of light.serve, remove in 2021
|
||||||
Name: "lightserv",
|
Name: "lightserv",
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,45 @@ var (
|
||||||
// ErrKnownBlock is returned when a block to import is already known locally.
|
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||||
ErrKnownBlock = errors.New("block already known")
|
ErrKnownBlock = errors.New("block already known")
|
||||||
|
|
||||||
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
|
|
||||||
// by a transaction is higher than what's left in the block.
|
|
||||||
ErrGasLimitReached = errors.New("gas limit reached")
|
|
||||||
|
|
||||||
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
||||||
ErrBlacklistedHash = errors.New("blacklisted hash")
|
ErrBlacklistedHash = errors.New("blacklisted hash")
|
||||||
|
|
||||||
|
// ErrNoGenesis is returned when there is no Genesis Block.
|
||||||
|
ErrNoGenesis = errors.New("genesis not found in chain")
|
||||||
|
)
|
||||||
|
|
||||||
|
// List of evm-call-message pre-checking errors. All state transtion messages will
|
||||||
|
// be pre-checked before execution. If any invalidation detected, the corresponding
|
||||||
|
// error should be returned which is defined here.
|
||||||
|
//
|
||||||
|
// - If the pre-checking happens in the miner, then the transaction won't be packed.
|
||||||
|
// - If the pre-checking happens in the block processing procedure, then a "BAD BLOCk"
|
||||||
|
// error should be emitted.
|
||||||
|
var (
|
||||||
|
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
||||||
|
// one present in the local chain.
|
||||||
|
ErrNonceTooLow = errors.New("nonce too low")
|
||||||
|
|
||||||
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
||||||
// next one expected based on the local chain.
|
// next one expected based on the local chain.
|
||||||
ErrNonceTooHigh = errors.New("nonce too high")
|
ErrNonceTooHigh = errors.New("nonce too high")
|
||||||
|
|
||||||
// ErrNoGenesis is returned when there is no Genesis Block.
|
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
|
||||||
ErrNoGenesis = errors.New("genesis not found in chain")
|
// by a transaction is higher than what's left in the block.
|
||||||
|
ErrGasLimitReached = errors.New("gas limit reached")
|
||||||
|
|
||||||
|
// ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't
|
||||||
|
// have enough funds for transfer(topmost call only).
|
||||||
|
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
||||||
|
|
||||||
|
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||||
|
// is higher than the balance of the user's account.
|
||||||
|
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||||
|
|
||||||
|
// ErrGasUintOverflow is returned when calculating gas usage.
|
||||||
|
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
|
|
||||||
|
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
|
||||||
|
// than required to start the invocation.
|
||||||
|
ErrIntrinsicGas = errors.New("intrinsic gas too low")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,6 @@ func (e *GenesisMismatchError) Error() string {
|
||||||
//
|
//
|
||||||
// The returned chain configuration is never nil.
|
// The returned chain configuration is never nil.
|
||||||
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
||||||
return SetupGenesisBlockWithOverride(db, genesis, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideIstanbul, overrideMuirGlacier *big.Int) (*params.ChainConfig, common.Hash, error) {
|
|
||||||
if genesis != nil && genesis.Config == nil {
|
if genesis != nil && genesis.Config == nil {
|
||||||
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
||||||
}
|
}
|
||||||
|
|
@ -204,12 +200,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
|
||||||
|
|
||||||
// Get the existing chain configuration.
|
// Get the existing chain configuration.
|
||||||
newcfg := genesis.configOrDefault(stored)
|
newcfg := genesis.configOrDefault(stored)
|
||||||
if overrideIstanbul != nil {
|
|
||||||
newcfg.IstanbulBlock = overrideIstanbul
|
|
||||||
}
|
|
||||||
if overrideMuirGlacier != nil {
|
|
||||||
newcfg.MuirGlacierBlock = overrideMuirGlacier
|
|
||||||
}
|
|
||||||
if err := newcfg.CheckConfigForkOrder(); err != nil {
|
if err := newcfg.CheckConfigForkOrder(); err != nil {
|
||||||
return newcfg, common.Hash{}, err
|
return newcfg, common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,8 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
if ptd == nil {
|
if ptd == nil {
|
||||||
return NonStatTy, consensus.ErrUnknownAncestor
|
return NonStatTy, consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64())
|
head := hc.CurrentHeader().Number.Uint64()
|
||||||
|
localTd := hc.GetTd(hc.currentHeaderHash, head)
|
||||||
externTd := new(big.Int).Add(header.Difficulty, ptd)
|
externTd := new(big.Int).Add(header.Difficulty, ptd)
|
||||||
|
|
||||||
// Irrelevant of the canonical status, write the td and header to the database
|
// Irrelevant of the canonical status, write the td and header to the database
|
||||||
|
|
@ -165,7 +166,15 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
||||||
if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) {
|
reorg := externTd.Cmp(localTd) > 0
|
||||||
|
if !reorg && externTd.Cmp(localTd) == 0 {
|
||||||
|
if header.Number.Uint64() < head {
|
||||||
|
reorg = true
|
||||||
|
} else if header.Number.Uint64() == head {
|
||||||
|
reorg = mrand.Float64() < 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reorg {
|
||||||
// If the header can be added into canonical chain, adjust the
|
// If the header can be added into canonical chain, adjust the
|
||||||
// header chain markers(canonical indexes and head header flag).
|
// header chain markers(canonical indexes and head header flag).
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ func TestMergeDelete(t *testing.T) {
|
||||||
|
|
||||||
flipDrops := func() map[common.Hash]struct{} {
|
flipDrops := func() map[common.Hash]struct{} {
|
||||||
return map[common.Hash]struct{}{
|
return map[common.Hash]struct{}{
|
||||||
h2: struct{}{},
|
h2: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flipAccs := func() map[common.Hash][]byte {
|
flipAccs := func() map[common.Hash][]byte {
|
||||||
|
|
@ -141,7 +141,7 @@ func TestMergeDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
flopDrops := func() map[common.Hash]struct{} {
|
flopDrops := func() map[common.Hash]struct{} {
|
||||||
return map[common.Hash]struct{}{
|
return map[common.Hash]struct{}{
|
||||||
h1: struct{}{},
|
h1: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flopAccs := func() map[common.Hash][]byte {
|
flopAccs := func() map[common.Hash][]byte {
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,10 @@ func TestDiskMerge(t *testing.T) {
|
||||||
|
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
||||||
accDelNoCache: struct{}{},
|
accDelNoCache: {},
|
||||||
accDelCache: struct{}{},
|
accDelCache: {},
|
||||||
conNukeNoCache: struct{}{},
|
conNukeNoCache: {},
|
||||||
conNukeCache: struct{}{},
|
conNukeCache: {},
|
||||||
}, map[common.Hash][]byte{
|
}, map[common.Hash][]byte{
|
||||||
accModNoCache: reverse(accModNoCache[:]),
|
accModNoCache: reverse(accModNoCache[:]),
|
||||||
accModCache: reverse(accModCache[:]),
|
accModCache: reverse(accModCache[:]),
|
||||||
|
|
@ -344,10 +344,10 @@ func TestDiskPartialMerge(t *testing.T) {
|
||||||
|
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
||||||
accDelNoCache: struct{}{},
|
accDelNoCache: {},
|
||||||
accDelCache: struct{}{},
|
accDelCache: {},
|
||||||
conNukeNoCache: struct{}{},
|
conNukeNoCache: {},
|
||||||
conNukeCache: struct{}{},
|
conNukeCache: {},
|
||||||
}, map[common.Hash][]byte{
|
}, map[common.Hash][]byte{
|
||||||
accModNoCache: reverse(accModNoCache[:]),
|
accModNoCache: reverse(accModNoCache[:]),
|
||||||
accModCache: reverse(accModCache[:]),
|
accModCache: reverse(accModCache[:]),
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ func (dl *diffLayer) AccountIterator(seek common.Hash) AccountIterator {
|
||||||
// Seek out the requested starting account
|
// Seek out the requested starting account
|
||||||
hashes := dl.AccountList()
|
hashes := dl.AccountList()
|
||||||
index := sort.Search(len(hashes), func(i int) bool {
|
index := sort.Search(len(hashes), func(i int) bool {
|
||||||
return bytes.Compare(seek[:], hashes[i][:]) < 0
|
return bytes.Compare(seek[:], hashes[i][:]) <= 0
|
||||||
})
|
})
|
||||||
// Assemble and returned the already seeked iterator
|
// Assemble and returned the already seeked iterator
|
||||||
return &diffAccountIterator{
|
return &diffAccountIterator{
|
||||||
|
|
@ -125,6 +125,7 @@ func (it *diffAccountIterator) Account() []byte {
|
||||||
blob, ok := it.layer.accountData[it.curHash]
|
blob, ok := it.layer.accountData[it.curHash]
|
||||||
if !ok {
|
if !ok {
|
||||||
if _, ok := it.layer.destructSet[it.curHash]; ok {
|
if _, ok := it.layer.destructSet[it.curHash]; ok {
|
||||||
|
it.layer.lock.RUnlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash))
|
panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash))
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
// a snapshot, which may or may npt be composed of multiple layers. Performance
|
// a snapshot, which may or may npt be composed of multiple layers. Performance
|
||||||
// wise this iterator is slow, it's meant for cross validating the fast one,
|
// wise this iterator is slow, it's meant for cross validating the fast one,
|
||||||
type binaryAccountIterator struct {
|
type binaryAccountIterator struct {
|
||||||
a *diffAccountIterator
|
a AccountIterator
|
||||||
b AccountIterator
|
b AccountIterator
|
||||||
aDone bool
|
aDone bool
|
||||||
bDone bool
|
bDone bool
|
||||||
|
|
@ -40,10 +40,16 @@ func (dl *diffLayer) newBinaryAccountIterator() AccountIterator {
|
||||||
parent, ok := dl.parent.(*diffLayer)
|
parent, ok := dl.parent.(*diffLayer)
|
||||||
if !ok {
|
if !ok {
|
||||||
// parent is the disk layer
|
// parent is the disk layer
|
||||||
return dl.AccountIterator(common.Hash{})
|
l := &binaryAccountIterator{
|
||||||
|
a: dl.AccountIterator(common.Hash{}),
|
||||||
|
b: dl.Parent().AccountIterator(common.Hash{}),
|
||||||
|
}
|
||||||
|
l.aDone = !l.a.Next()
|
||||||
|
l.bDone = !l.b.Next()
|
||||||
|
return l
|
||||||
}
|
}
|
||||||
l := &binaryAccountIterator{
|
l := &binaryAccountIterator{
|
||||||
a: dl.AccountIterator(common.Hash{}).(*diffAccountIterator),
|
a: dl.AccountIterator(common.Hash{}),
|
||||||
b: parent.newBinaryAccountIterator(),
|
b: parent.newBinaryAccountIterator(),
|
||||||
}
|
}
|
||||||
l.aDone = !l.a.Next()
|
l.aDone = !l.a.Next()
|
||||||
|
|
@ -58,19 +64,18 @@ func (it *binaryAccountIterator) Next() bool {
|
||||||
if it.aDone && it.bDone {
|
if it.aDone && it.bDone {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
nextB := it.b.Hash()
|
|
||||||
first:
|
first:
|
||||||
nextA := it.a.Hash()
|
|
||||||
if it.aDone {
|
if it.aDone {
|
||||||
|
it.k = it.b.Hash()
|
||||||
it.bDone = !it.b.Next()
|
it.bDone = !it.b.Next()
|
||||||
it.k = nextB
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if it.bDone {
|
if it.bDone {
|
||||||
|
it.k = it.a.Hash()
|
||||||
it.aDone = !it.a.Next()
|
it.aDone = !it.a.Next()
|
||||||
it.k = nextA
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
nextA, nextB := it.a.Hash(), it.b.Hash()
|
||||||
if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 {
|
if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 {
|
||||||
it.aDone = !it.a.Next()
|
it.aDone = !it.a.Next()
|
||||||
it.k = nextA
|
it.k = nextA
|
||||||
|
|
@ -100,7 +105,8 @@ func (it *binaryAccountIterator) Hash() common.Hash {
|
||||||
// nil if the iterated snapshot stack became stale (you can check Error after
|
// nil if the iterated snapshot stack became stale (you can check Error after
|
||||||
// to see if it failed or not).
|
// to see if it failed or not).
|
||||||
func (it *binaryAccountIterator) Account() []byte {
|
func (it *binaryAccountIterator) Account() []byte {
|
||||||
blob, err := it.a.layer.AccountRLP(it.k)
|
// The topmost iterator must be `diffAccountIterator`
|
||||||
|
blob, err := it.a.(*diffAccountIterator).layer.AccountRLP(it.k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
it.fail = err
|
it.fail = err
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -177,9 +177,22 @@ func TestAccountIteratorTraversal(t *testing.T) {
|
||||||
verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator())
|
verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator())
|
||||||
|
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
||||||
defer it.Release()
|
|
||||||
|
|
||||||
verifyIterator(t, 7, it)
|
verifyIterator(t, 7, it)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x04"), 2)
|
||||||
|
verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator())
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{})
|
||||||
|
verifyIterator(t, 7, it)
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAccountIteratorTraversalValues tests some multi-layer iteration, where we
|
// TestAccountIteratorTraversalValues tests some multi-layer iteration, where we
|
||||||
|
|
@ -242,8 +255,6 @@ func TestAccountIteratorTraversalValues(t *testing.T) {
|
||||||
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, h, nil)
|
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, h, nil)
|
||||||
|
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
||||||
defer it.Release()
|
|
||||||
|
|
||||||
head := snaps.Snapshot(common.HexToHash("0x09"))
|
head := snaps.Snapshot(common.HexToHash("0x09"))
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
hash := it.Hash()
|
hash := it.Hash()
|
||||||
|
|
@ -255,6 +266,29 @@ func TestAccountIteratorTraversalValues(t *testing.T) {
|
||||||
t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want)
|
t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x09"), 2)
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
||||||
|
for it.Next() {
|
||||||
|
hash := it.Hash()
|
||||||
|
want, err := head.AccountRLP(hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve expected account: %v", err)
|
||||||
|
}
|
||||||
|
if have := it.Account(); !bytes.Equal(want, have) {
|
||||||
|
t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This testcase is notorious, all layers contain the exact same 200 accounts.
|
// This testcase is notorious, all layers contain the exact same 200 accounts.
|
||||||
|
|
@ -289,9 +323,23 @@ func TestAccountIteratorLargeTraversal(t *testing.T) {
|
||||||
verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator())
|
verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator())
|
||||||
|
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{})
|
||||||
defer it.Release()
|
|
||||||
|
|
||||||
verifyIterator(t, 200, it)
|
verifyIterator(t, 200, it)
|
||||||
|
it.Release()
|
||||||
|
|
||||||
|
// Test after persist some bottom-most layers into the disk,
|
||||||
|
// the functionalities still work.
|
||||||
|
limit := aggregatorMemoryLimit
|
||||||
|
defer func() {
|
||||||
|
aggregatorMemoryLimit = limit
|
||||||
|
}()
|
||||||
|
aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk
|
||||||
|
snaps.Cap(common.HexToHash("0x80"), 2)
|
||||||
|
|
||||||
|
verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator())
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{})
|
||||||
|
verifyIterator(t, 200, it)
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAccountIteratorFlattening tests what happens when we
|
// TestAccountIteratorFlattening tests what happens when we
|
||||||
|
|
@ -351,22 +399,30 @@ func TestAccountIteratorSeek(t *testing.T) {
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
||||||
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
||||||
|
|
||||||
// Construct various iterators and ensure their tranversal is correct
|
// Account set is now
|
||||||
|
// 02: aa, ee, f0, ff
|
||||||
|
// 03: aa, bb, dd, ee, f0 (, f0), ff
|
||||||
|
// 04: aa, bb, cc, dd, ee, f0 (, f0), ff (, ff)
|
||||||
|
// Construct various iterators and ensure their traversal is correct
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd"))
|
it, _ := snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 3, it) // expected: ee, f0, ff
|
verifyIterator(t, 3, it) // expected: ee, f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 3, it) // expected: ee, f0, ff
|
verifyIterator(t, 4, it) // expected: aa, ee, f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
verifyIterator(t, 1, it) // expected: ff
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff1"))
|
||||||
|
defer it.Release()
|
||||||
verifyIterator(t, 0, it) // expected: nothing
|
verifyIterator(t, 0, it) // expected: nothing
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 5, it) // expected: cc, dd, ee, f0, ff
|
verifyIterator(t, 6, it) // expected: bb, cc, dd, ee, f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
|
@ -374,11 +430,16 @@ func TestAccountIteratorSeek(t *testing.T) {
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
verifyIterator(t, 1, it) // expected: ff
|
verifyIterator(t, 2, it) // expected: f0, ff
|
||||||
|
|
||||||
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff"))
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff"))
|
||||||
defer it.Release()
|
defer it.Release()
|
||||||
|
verifyIterator(t, 1, it) // expected: ff
|
||||||
|
|
||||||
|
it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff1"))
|
||||||
|
defer it.Release()
|
||||||
verifyIterator(t, 0, it) // expected: nothing
|
verifyIterator(t, 0, it) // expected: nothing
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestIteratorDeletions tests that the iterator behaves correct when there are
|
// TestIteratorDeletions tests that the iterator behaves correct when there are
|
||||||
|
|
@ -402,7 +463,7 @@ func TestIteratorDeletions(t *testing.T) {
|
||||||
|
|
||||||
deleted := common.HexToHash("0x22")
|
deleted := common.HexToHash("0x22")
|
||||||
destructed := map[common.Hash]struct{}{
|
destructed := map[common.Hash]struct{}{
|
||||||
deleted: struct{}{},
|
deleted: {},
|
||||||
}
|
}
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
destructed, randomAccountSet("0x11", "0x33"), nil)
|
destructed, randomAccountSet("0x11", "0x33"), nil)
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,6 @@ func precacheTransaction(config *params.ChainConfig, bc ChainContext, author *co
|
||||||
context := NewEVMContext(msg, header, bc, author)
|
context := NewEVMContext(msg, header, bc, author)
|
||||||
vm := vm.NewEVM(context, statedb, config, cfg)
|
vm := vm.NewEVM(context, statedb, config, cfg)
|
||||||
|
|
||||||
_, _, _, err = ApplyMessage(vm, msg, gaspool)
|
_, err = ApplyMessage(vm, msg, gaspool)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
||||||
// Apply the transaction to the current state (included in the env)
|
// Apply the transaction to the current state (included in the env)
|
||||||
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
|
result, err := ApplyMessage(vmenv, msg, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -107,13 +107,13 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||||
} else {
|
} else {
|
||||||
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
||||||
}
|
}
|
||||||
*usedGas += gas
|
*usedGas += result.UsedGas
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||||
// based on the eip phase, we're passing whether the root touch-delete accounts.
|
// based on the eip phase, we're passing whether the root touch-delete accounts.
|
||||||
receipt := types.NewReceipt(root, failed, *usedGas)
|
receipt := types.NewReceipt(root, result.Failed(), *usedGas)
|
||||||
receipt.TxHash = tx.Hash()
|
receipt.TxHash = tx.Hash()
|
||||||
receipt.GasUsed = gas
|
receipt.GasUsed = result.UsedGas
|
||||||
// if the transaction created a contract, store the creation address in the receipt.
|
// if the transaction created a contract, store the creation address in the receipt.
|
||||||
if msg.To() == nil {
|
if msg.To() == nil {
|
||||||
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,14 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The State Transitioning Model
|
The State Transitioning Model
|
||||||
|
|
||||||
|
|
@ -63,7 +57,6 @@ type StateTransition struct {
|
||||||
// Message represents a message sent to a contract.
|
// Message represents a message sent to a contract.
|
||||||
type Message interface {
|
type Message interface {
|
||||||
From() common.Address
|
From() common.Address
|
||||||
//FromFrontier() (common.Address, error)
|
|
||||||
To() *common.Address
|
To() *common.Address
|
||||||
|
|
||||||
GasPrice() *big.Int
|
GasPrice() *big.Int
|
||||||
|
|
@ -75,6 +68,41 @@ type Message interface {
|
||||||
Data() []byte
|
Data() []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecutionResult includes all output after executing given evm
|
||||||
|
// message no matter the execution itself is successful or not.
|
||||||
|
type ExecutionResult struct {
|
||||||
|
UsedGas uint64 // Total used gas but include the refunded gas
|
||||||
|
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||||
|
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the internal evm error which allows us for further
|
||||||
|
// analysis outside.
|
||||||
|
func (result *ExecutionResult) Unwrap() error {
|
||||||
|
return result.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed returns the indicator whether the execution is successful or not
|
||||||
|
func (result *ExecutionResult) Failed() bool { return result.Err != nil }
|
||||||
|
|
||||||
|
// Return is a helper function to help caller distinguish between revert reason
|
||||||
|
// and function return. Return returns the data after execution if no error occurs.
|
||||||
|
func (result *ExecutionResult) Return() []byte {
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return common.CopyBytes(result.ReturnData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert returns the concrete revert reason if the execution is aborted by `REVERT`
|
||||||
|
// opcode. Note the reason can be nil if no data supplied with revert opcode.
|
||||||
|
func (result *ExecutionResult) Revert() []byte {
|
||||||
|
if result.Err != vm.ErrExecutionReverted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return common.CopyBytes(result.ReturnData)
|
||||||
|
}
|
||||||
|
|
||||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||||
func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 bool) (uint64, error) {
|
func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 bool) (uint64, error) {
|
||||||
// Set the starting gas for the raw transaction
|
// Set the starting gas for the raw transaction
|
||||||
|
|
@ -99,13 +127,13 @@ func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 boo
|
||||||
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
||||||
}
|
}
|
||||||
if (math.MaxUint64-gas)/nonZeroGas < nz {
|
if (math.MaxUint64-gas)/nonZeroGas < nz {
|
||||||
return 0, vm.ErrOutOfGas
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
gas += nz * nonZeroGas
|
gas += nz * nonZeroGas
|
||||||
|
|
||||||
z := uint64(len(data)) - nz
|
z := uint64(len(data)) - nz
|
||||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||||
return 0, vm.ErrOutOfGas
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
gas += z * params.TxDataZeroGas
|
gas += z * params.TxDataZeroGas
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +160,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
|
||||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
||||||
// indicates a core error meaning that the message would always fail for that particular
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
// state and would never be accepted within a block.
|
// state and would never be accepted within a block.
|
||||||
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
|
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) {
|
||||||
return NewStateTransition(evm, msg, gp).TransitionDb()
|
return NewStateTransition(evm, msg, gp).TransitionDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,19 +172,10 @@ func (st *StateTransition) to() common.Address {
|
||||||
return *st.msg.To()
|
return *st.msg.To()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) useGas(amount uint64) error {
|
|
||||||
if st.gas < amount {
|
|
||||||
return vm.ErrOutOfGas
|
|
||||||
}
|
|
||||||
st.gas -= amount
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *StateTransition) buyGas() error {
|
func (st *StateTransition) buyGas() error {
|
||||||
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
|
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
|
||||||
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
|
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
|
||||||
return errInsufficientBalanceForGas
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
|
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -182,11 +201,32 @@ func (st *StateTransition) preCheck() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransitionDb will transition the state by applying the current message and
|
// TransitionDb will transition the state by applying the current message and
|
||||||
// returning the result including the used gas. It returns an error if failed.
|
// returning the evm execution result with following fields.
|
||||||
// An error indicates a consensus issue.
|
//
|
||||||
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
|
// - used gas:
|
||||||
if err = st.preCheck(); err != nil {
|
// total gas used (including gas being refunded)
|
||||||
return
|
// - returndata:
|
||||||
|
// the returned data from evm
|
||||||
|
// - concrete execution error:
|
||||||
|
// various **EVM** error which aborts the execution,
|
||||||
|
// e.g. ErrOutOfGas, ErrExecutionReverted
|
||||||
|
//
|
||||||
|
// However if any consensus issue encountered, return the error directly with
|
||||||
|
// nil evm execution result.
|
||||||
|
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
|
// First check this message satisfies all consensus rules before
|
||||||
|
// applying the message. The rules include these clauses
|
||||||
|
//
|
||||||
|
// 1. the nonce of the message caller is correct
|
||||||
|
// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)
|
||||||
|
// 3. the amount of gas required is available in the block
|
||||||
|
// 4. the purchased gas is enough to cover intrinsic usage
|
||||||
|
// 5. there is no overflow when calculating intrinsic gas
|
||||||
|
// 6. caller has enough balance to cover asset transfer for **topmost** call
|
||||||
|
|
||||||
|
// Check clauses 1-3, buy gas if everything is correct
|
||||||
|
if err := st.preCheck(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
msg := st.msg
|
msg := st.msg
|
||||||
sender := vm.AccountRef(msg.From())
|
sender := vm.AccountRef(msg.From())
|
||||||
|
|
@ -194,42 +234,39 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
|
||||||
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.BlockNumber)
|
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.BlockNumber)
|
||||||
contractCreation := msg.To() == nil
|
contractCreation := msg.To() == nil
|
||||||
|
|
||||||
// Pay intrinsic gas
|
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||||
gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul)
|
gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = st.useGas(gas); err != nil {
|
if st.gas < gas {
|
||||||
return nil, 0, false, err
|
return nil, ErrIntrinsicGas
|
||||||
}
|
}
|
||||||
|
st.gas -= gas
|
||||||
|
|
||||||
|
// Check clause 6
|
||||||
|
if msg.Value().Sign() > 0 && !st.evm.CanTransfer(st.state, msg.From(), msg.Value()) {
|
||||||
|
return nil, ErrInsufficientFundsForTransfer
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
evm = st.evm
|
ret []byte
|
||||||
// vm errors do not effect consensus and are therefor
|
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||||
// not assigned to err, except for insufficient balance
|
|
||||||
// error.
|
|
||||||
vmerr error
|
|
||||||
)
|
)
|
||||||
if contractCreation {
|
if contractCreation {
|
||||||
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
|
ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction
|
// Increment the nonce for the next transaction
|
||||||
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
||||||
ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
|
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
|
||||||
}
|
|
||||||
if vmerr != nil {
|
|
||||||
log.Debug("VM returned with error", "err", vmerr)
|
|
||||||
// The only possible consensus-error would be if there wasn't
|
|
||||||
// sufficient balance to make the transfer happen. The first
|
|
||||||
// balance transfer may never fail.
|
|
||||||
if vmerr == vm.ErrInsufficientBalance {
|
|
||||||
return nil, 0, false, vmerr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
st.refundGas()
|
st.refundGas()
|
||||||
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
||||||
|
|
||||||
return ret, st.gasUsed(), vmerr != nil, err
|
return &ExecutionResult{
|
||||||
|
UsedGas: st.gasUsed(),
|
||||||
|
Err: vmerr,
|
||||||
|
ReturnData: ret,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) refundGas() {
|
func (st *StateTransition) refundGas() {
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,6 @@ var (
|
||||||
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
||||||
ErrInvalidSender = errors.New("invalid sender")
|
ErrInvalidSender = errors.New("invalid sender")
|
||||||
|
|
||||||
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
|
||||||
// one present in the local chain.
|
|
||||||
ErrNonceTooLow = errors.New("nonce too low")
|
|
||||||
|
|
||||||
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
||||||
// configured for the transaction pool.
|
// configured for the transaction pool.
|
||||||
ErrUnderpriced = errors.New("transaction underpriced")
|
ErrUnderpriced = errors.New("transaction underpriced")
|
||||||
|
|
@ -71,14 +67,6 @@ var (
|
||||||
// with a different one without the required price bump.
|
// with a different one without the required price bump.
|
||||||
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
||||||
|
|
||||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
|
||||||
// is higher than the balance of the user's account.
|
|
||||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
|
||||||
|
|
||||||
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
|
|
||||||
// than required to start the invocation.
|
|
||||||
ErrIntrinsicGas = errors.New("intrinsic gas too low")
|
|
||||||
|
|
||||||
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
||||||
// maximum allowance of the current block.
|
// maximum allowance of the current block.
|
||||||
ErrGasLimit = errors.New("exceeds block gas limit")
|
ErrGasLimit = errors.New("exceeds block gas limit")
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,51 @@
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// List execution errors
|
// List evm execution errors
|
||||||
var (
|
var (
|
||||||
ErrOutOfGas = errors.New("out of gas")
|
ErrOutOfGas = errors.New("out of gas")
|
||||||
ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
|
ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
|
||||||
ErrDepth = errors.New("max call depth exceeded")
|
ErrDepth = errors.New("max call depth exceeded")
|
||||||
ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
|
||||||
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||||
ErrContractAddressCollision = errors.New("contract address collision")
|
ErrContractAddressCollision = errors.New("contract address collision")
|
||||||
ErrNoCompatibleInterpreter = errors.New("no compatible interpreter")
|
ErrExecutionReverted = errors.New("execution reverted")
|
||||||
|
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
||||||
|
ErrInvalidJump = errors.New("invalid jump destination")
|
||||||
|
ErrWriteProtection = errors.New("write protection")
|
||||||
|
ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
|
||||||
|
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrStackUnderflow wraps an evm error when the items on the stack less
|
||||||
|
// than the minimal requirement.
|
||||||
|
type ErrStackUnderflow struct {
|
||||||
|
stackLen int
|
||||||
|
required int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrStackUnderflow) Error() string {
|
||||||
|
return fmt.Sprintf("stack underflow (%d <=> %d)", e.stackLen, e.required)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrStackOverflow wraps an evm error when the items on the stack exceeds
|
||||||
|
// the maximum allowance.
|
||||||
|
type ErrStackOverflow struct {
|
||||||
|
stackLen int
|
||||||
|
limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrStackOverflow) Error() string {
|
||||||
|
return fmt.Sprintf("stack limit reached %d (%d)", e.stackLen, e.limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidOpCode wraps an evm error when an invalid opcode is encountered.
|
||||||
|
type ErrInvalidOpCode struct {
|
||||||
|
opcode OpCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -67,7 +68,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
|
||||||
return interpreter.Run(contract, input, readOnly)
|
return interpreter.Run(contract, input, readOnly)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, ErrNoCompatibleInterpreter
|
return nil, errors.New("no compatible interpreter")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context provides the EVM with auxiliary information. Once provided
|
// Context provides the EVM with auxiliary information. Once provided
|
||||||
|
|
@ -190,7 +191,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if we're trying to execute above the call depth limit
|
// Fail if we're trying to execute above the call depth limit
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
|
|
@ -199,7 +199,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||||
return nil, gas, ErrInsufficientBalance
|
return nil, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
to = AccountRef(addr)
|
to = AccountRef(addr)
|
||||||
snapshot = evm.StateDB.Snapshot()
|
snapshot = evm.StateDB.Snapshot()
|
||||||
|
|
@ -246,7 +245,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != errExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,16 +263,17 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if we're trying to execute above the call depth limit
|
// Fail if we're trying to execute above the call depth limit
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
}
|
}
|
||||||
// Fail if we're trying to transfer more than the available balance
|
// Fail if we're trying to transfer more than the available balance
|
||||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
// Note although it's noop to transfer X ether to caller itself. But
|
||||||
|
// if caller doesn't have enough balance, it would be an error to allow
|
||||||
|
// over-charging itself. So the check here is necessary.
|
||||||
|
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||||
return nil, gas, ErrInsufficientBalance
|
return nil, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
snapshot = evm.StateDB.Snapshot()
|
snapshot = evm.StateDB.Snapshot()
|
||||||
to = AccountRef(caller.Address())
|
to = AccountRef(caller.Address())
|
||||||
|
|
@ -286,7 +286,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
ret, err = run(evm, contract, input, false)
|
ret, err = run(evm, contract, input, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != errExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -306,12 +306,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
snapshot = evm.StateDB.Snapshot()
|
snapshot = evm.StateDB.Snapshot()
|
||||||
to = AccountRef(caller.Address())
|
to = AccountRef(caller.Address())
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialise a new contract and make initialise the delegate values
|
// Initialise a new contract and make initialise the delegate values
|
||||||
contract := NewContract(caller, to, nil, gas).AsDelegate()
|
contract := NewContract(caller, to, nil, gas).AsDelegate()
|
||||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||||
|
|
@ -319,7 +317,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
ret, err = run(evm, contract, input, false)
|
ret, err = run(evm, contract, input, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != errExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -338,7 +336,6 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
to = AccountRef(addr)
|
to = AccountRef(addr)
|
||||||
snapshot = evm.StateDB.Snapshot()
|
snapshot = evm.StateDB.Snapshot()
|
||||||
|
|
@ -360,7 +357,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
ret, err = run(evm, contract, input, true)
|
ret, err = run(evm, contract, input, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != errExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -441,13 +438,13 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) {
|
if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != errExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assign err if contract code size exceeds the max while the err is still empty.
|
// Assign err if contract code size exceeds the max while the err is still empty.
|
||||||
if maxCodeSizeExceeded && err == nil {
|
if maxCodeSizeExceeded && err == nil {
|
||||||
err = errMaxCodeSizeExceeded
|
err = ErrMaxCodeSizeExceeded
|
||||||
}
|
}
|
||||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !callCost.IsUint64() {
|
if !callCost.IsUint64() {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
return callCost.Uint64(), nil
|
return callCost.Uint64(), nil
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||||
// overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used
|
// overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used
|
||||||
// without overflowing the gas calculation.
|
// without overflowing the gas calculation.
|
||||||
if newMemSize > 0x1FFFFFFFE0 {
|
if newMemSize > 0x1FFFFFFFE0 {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
newMemSizeWords := toWordSize(newMemSize)
|
newMemSizeWords := toWordSize(newMemSize)
|
||||||
newMemSize = newMemSizeWords * 32
|
newMemSize = newMemSizeWords * 32
|
||||||
|
|
@ -72,15 +72,15 @@ func memoryCopierGas(stackpos int) gasFunc {
|
||||||
// And gas for copying data, charged per word at param.CopyGas
|
// And gas for copying data, charged per word at param.CopyGas
|
||||||
words, overflow := bigUint64(stack.Back(stackpos))
|
words, overflow := bigUint64(stack.Back(stackpos))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
if gas, overflow = math.SafeAdd(gas, words); overflow {
|
if gas, overflow = math.SafeAdd(gas, words); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +221,7 @@ func makeGasLog(n uint64) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
requestedSize, overflow := bigUint64(stack.Back(1))
|
requestedSize, overflow := bigUint64(stack.Back(1))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
gas, err := memoryGasCost(mem, memorySize)
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
|
|
@ -230,18 +230,18 @@ func makeGasLog(n uint64) gasFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
var memorySizeGas uint64
|
var memorySizeGas uint64
|
||||||
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
|
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -254,13 +254,13 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||||
}
|
}
|
||||||
wordGas, overflow := bigUint64(stack.Back(1))
|
wordGas, overflow := bigUint64(stack.Back(1))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -288,13 +288,13 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
||||||
}
|
}
|
||||||
wordGas, overflow := bigUint64(stack.Back(2))
|
wordGas, overflow := bigUint64(stack.Back(2))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -307,7 +307,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
||||||
overflow bool
|
overflow bool
|
||||||
)
|
)
|
||||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -320,7 +320,7 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
||||||
overflow bool
|
overflow bool
|
||||||
)
|
)
|
||||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -347,7 +347,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||||
}
|
}
|
||||||
var overflow bool
|
var overflow bool
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
|
|
@ -355,7 +355,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -373,14 +373,14 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
||||||
gas += params.CallValueTransferGas
|
gas += params.CallValueTransferGas
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +396,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
}
|
}
|
||||||
var overflow bool
|
var overflow bool
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -412,7 +412,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
||||||
}
|
}
|
||||||
var overflow bool
|
var overflow bool
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ func TestMemoryGasCost(t *testing.T) {
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
v, err := memoryGasCost(&Memory{}, tt.size)
|
v, err := memoryGasCost(&Memory{}, tt.size)
|
||||||
if (err == errGasUintOverflow) != tt.overflow {
|
if (err == ErrGasUintOverflow) != tt.overflow {
|
||||||
t.Errorf("test %d: overflow mismatch: have %v, want %v", i, err == errGasUintOverflow, tt.overflow)
|
t.Errorf("test %d: overflow mismatch: have %v, want %v", i, err == ErrGasUintOverflow, tt.overflow)
|
||||||
}
|
}
|
||||||
if v != tt.cost {
|
if v != tt.cost {
|
||||||
t.Errorf("test %d: gas cost mismatch: have %v, want %v", i, v, tt.cost)
|
t.Errorf("test %d: gas cost mismatch: have %v, want %v", i, v, tt.cost)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -28,13 +27,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bigZero = new(big.Int)
|
bigZero = new(big.Int)
|
||||||
tt255 = math.BigPow(2, 255)
|
tt255 = math.BigPow(2, 255)
|
||||||
errWriteProtection = errors.New("evm: write protection")
|
|
||||||
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
|
|
||||||
errExecutionReverted = errors.New("evm: execution reverted")
|
|
||||||
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
|
|
||||||
errInvalidJump = errors.New("evm: invalid jump destination")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||||
|
|
@ -468,7 +462,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
|
||||||
defer interpreter.intPool.put(memOffset, dataOffset, length, end)
|
defer interpreter.intPool.put(memOffset, dataOffset, length, end)
|
||||||
|
|
||||||
if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
|
if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
|
||||||
return nil, errReturnDataOutOfBounds
|
return nil, ErrReturnDataOutOfBounds
|
||||||
}
|
}
|
||||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
|
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
|
||||||
|
|
||||||
|
|
@ -643,7 +637,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
||||||
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||||
pos := callContext.stack.pop()
|
pos := callContext.stack.pop()
|
||||||
if !callContext.contract.validJumpdest(pos) {
|
if !callContext.contract.validJumpdest(pos) {
|
||||||
return nil, errInvalidJump
|
return nil, ErrInvalidJump
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64()
|
||||||
|
|
||||||
|
|
@ -655,7 +649,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
|
||||||
pos, cond := callContext.stack.pop(), callContext.stack.pop()
|
pos, cond := callContext.stack.pop(), callContext.stack.pop()
|
||||||
if cond.Sign() != 0 {
|
if cond.Sign() != 0 {
|
||||||
if !callContext.contract.validJumpdest(pos) {
|
if !callContext.contract.validJumpdest(pos) {
|
||||||
return nil, errInvalidJump
|
return nil, ErrInvalidJump
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -712,7 +706,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
||||||
callContext.contract.Gas += returnGas
|
callContext.contract.Gas += returnGas
|
||||||
interpreter.intPool.put(value, offset, size)
|
interpreter.intPool.put(value, offset, size)
|
||||||
|
|
||||||
if suberr == errExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -740,7 +734,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
|
||||||
callContext.contract.Gas += returnGas
|
callContext.contract.Gas += returnGas
|
||||||
interpreter.intPool.put(endowment, offset, size, salt)
|
interpreter.intPool.put(endowment, offset, size, salt)
|
||||||
|
|
||||||
if suberr == errExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -766,7 +760,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
||||||
} else {
|
} else {
|
||||||
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
||||||
}
|
}
|
||||||
if err == nil || err == errExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
callContext.contract.Gas += returnGas
|
callContext.contract.Gas += returnGas
|
||||||
|
|
@ -795,7 +789,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
|
||||||
} else {
|
} else {
|
||||||
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
||||||
}
|
}
|
||||||
if err == nil || err == errExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
callContext.contract.Gas += returnGas
|
callContext.contract.Gas += returnGas
|
||||||
|
|
@ -820,7 +814,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
||||||
} else {
|
} else {
|
||||||
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
||||||
}
|
}
|
||||||
if err == nil || err == errExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
callContext.contract.Gas += returnGas
|
callContext.contract.Gas += returnGas
|
||||||
|
|
@ -845,7 +839,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
||||||
} else {
|
} else {
|
||||||
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
callContext.stack.push(interpreter.intPool.get().SetUint64(1))
|
||||||
}
|
}
|
||||||
if err == nil || err == errExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
callContext.contract.Gas += returnGas
|
callContext.contract.Gas += returnGas
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"hash"
|
"hash"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
|
@ -137,7 +136,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||||
//
|
//
|
||||||
// It's important to note that any errors returned by the interpreter should be
|
// It's important to note that any errors returned by the interpreter should be
|
||||||
// considered a revert-and-consume-all-gas operation except for
|
// considered a revert-and-consume-all-gas operation except for
|
||||||
// errExecutionReverted which means revert-and-keep-gas-left.
|
// ErrExecutionReverted which means revert-and-keep-gas-left.
|
||||||
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
|
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
|
||||||
if in.intPool == nil {
|
if in.intPool == nil {
|
||||||
in.intPool = poolOfIntPools.get()
|
in.intPool = poolOfIntPools.get()
|
||||||
|
|
@ -223,13 +222,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
op = contract.GetOp(pc)
|
op = contract.GetOp(pc)
|
||||||
operation := in.cfg.JumpTable[op]
|
operation := in.cfg.JumpTable[op]
|
||||||
if !operation.valid {
|
if !operation.valid {
|
||||||
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
return nil, &ErrInvalidOpCode{opcode: op}
|
||||||
}
|
}
|
||||||
// Validate stack
|
// Validate stack
|
||||||
if sLen := stack.len(); sLen < operation.minStack {
|
if sLen := stack.len(); sLen < operation.minStack {
|
||||||
return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
|
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
|
||||||
} else if sLen > operation.maxStack {
|
} else if sLen > operation.maxStack {
|
||||||
return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
|
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||||
}
|
}
|
||||||
// If the operation is valid, enforce and write restrictions
|
// If the operation is valid, enforce and write restrictions
|
||||||
if in.readOnly && in.evm.chainRules.IsByzantium {
|
if in.readOnly && in.evm.chainRules.IsByzantium {
|
||||||
|
|
@ -239,7 +238,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
// account to the others means the state is modified and should also
|
// account to the others means the state is modified and should also
|
||||||
// return with an error.
|
// return with an error.
|
||||||
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
|
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
|
||||||
return nil, errWriteProtection
|
return nil, ErrWriteProtection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Static portion of gas
|
// Static portion of gas
|
||||||
|
|
@ -256,12 +255,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
if operation.memorySize != nil {
|
if operation.memorySize != nil {
|
||||||
memSize, overflow := operation.memorySize(stack)
|
memSize, overflow := operation.memorySize(stack)
|
||||||
if overflow {
|
if overflow {
|
||||||
return nil, errGasUintOverflow
|
return nil, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
// memory is expanded in words of 32 bytes. Gas
|
// memory is expanded in words of 32 bytes. Gas
|
||||||
// is also calculated in words.
|
// is also calculated in words.
|
||||||
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
|
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
|
||||||
return nil, errGasUintOverflow
|
return nil, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Dynamic portion of gas
|
// Dynamic portion of gas
|
||||||
|
|
@ -301,7 +300,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return nil, err
|
return nil, err
|
||||||
case operation.reverts:
|
case operation.reverts:
|
||||||
return res, errExecutionReverted
|
return res, ErrExecutionReverted
|
||||||
case operation.halts:
|
case operation.halts:
|
||||||
return res, nil
|
return res, nil
|
||||||
case !operation.jumps:
|
case !operation.jumps:
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,8 +27,6 @@ type (
|
||||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||||
)
|
)
|
||||||
|
|
||||||
var errGasUintOverflow = errors.New("gas uint64 overflow")
|
|
||||||
|
|
||||||
type operation struct {
|
type operation struct {
|
||||||
// execute is the operation function
|
// execute is the operation function
|
||||||
execute executionFunc
|
execute executionFunc
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -29,6 +30,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
||||||
|
|
||||||
// Storage represents a contract's storage.
|
// Storage represents a contract's storage.
|
||||||
type Storage map[common.Hash]common.Hash
|
type Storage map[common.Hash]common.Hash
|
||||||
|
|
||||||
|
|
@ -140,7 +143,7 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
|
||||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||||
// check if already accumulated the specified number of logs
|
// check if already accumulated the specified number of logs
|
||||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||||
return ErrTraceLimitReached
|
return errTraceLimitReached
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialise new changed values storage container for this contract
|
// initialise new changed values storage container for this contract
|
||||||
|
|
|
||||||
|
|
@ -389,7 +389,7 @@ var opCodeToString = map[OpCode]string{
|
||||||
func (op OpCode) String() string {
|
func (op OpCode) String() string {
|
||||||
str := opCodeToString[op]
|
str := opCodeToString[op]
|
||||||
if len(str) == 0 {
|
if len(str) == 0 {
|
||||||
return fmt.Sprintf("Missing opcode 0x%x", int(op))
|
return fmt.Sprintf("opcode 0x%x not defined", int(op))
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
return str
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||||
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
|
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
|
||||||
|
|
||||||
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||||
failed = err
|
failed = err
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -596,7 +596,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
|
||||||
}
|
}
|
||||||
// Execute the transaction and flush any traces to disk
|
// Execute the transaction and flush any traces to disk
|
||||||
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf)
|
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf)
|
||||||
_, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
||||||
if writer != nil {
|
if writer != nil {
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
@ -758,7 +758,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
|
||||||
// Run the transaction with tracing enabled.
|
// Run the transaction with tracing enabled.
|
||||||
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer})
|
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer})
|
||||||
|
|
||||||
ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
|
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -766,9 +766,9 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
|
||||||
switch tracer := tracer.(type) {
|
switch tracer := tracer.(type) {
|
||||||
case *vm.StructLogger:
|
case *vm.StructLogger:
|
||||||
return ðapi.ExecutionResult{
|
return ðapi.ExecutionResult{
|
||||||
Gas: gas,
|
Gas: result.UsedGas,
|
||||||
Failed: failed,
|
Failed: result.Failed(),
|
||||||
ReturnValue: fmt.Sprintf("%x", ret),
|
ReturnValue: fmt.Sprintf("%x", result.Return()),
|
||||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
|
|
@ -812,7 +812,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
|
||||||
}
|
}
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, statedb, api.eth.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, statedb, api.eth.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideIstanbul, config.OverrideMuirGlacier)
|
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
|
||||||
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
|
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
|
||||||
return nil, genesisErr
|
return nil, genesisErr
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,10 +162,4 @@ type Config struct {
|
||||||
|
|
||||||
// CheckpointOracle is the configuration for checkpoint oracle.
|
// CheckpointOracle is the configuration for checkpoint oracle.
|
||||||
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
|
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
|
||||||
|
|
||||||
// Istanbul block override (TODO: remove after the fork)
|
|
||||||
OverrideIstanbul *big.Int `toml:",omitempty"`
|
|
||||||
|
|
||||||
// MuirGlacier block override (TODO: remove after the fork)
|
|
||||||
OverrideMuirGlacier *big.Int `toml:",omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -558,6 +558,7 @@ func (d *Downloader) cancel() {
|
||||||
// Close the current cancel channel
|
// Close the current cancel channel
|
||||||
d.cancelLock.Lock()
|
d.cancelLock.Lock()
|
||||||
defer d.cancelLock.Unlock()
|
defer d.cancelLock.Unlock()
|
||||||
|
|
||||||
if d.cancelCh != nil {
|
if d.cancelCh != nil {
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,6 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.RPCGasCap = c.RPCGasCap
|
enc.RPCGasCap = c.RPCGasCap
|
||||||
enc.Checkpoint = c.Checkpoint
|
enc.Checkpoint = c.Checkpoint
|
||||||
enc.CheckpointOracle = c.CheckpointOracle
|
enc.CheckpointOracle = c.CheckpointOracle
|
||||||
enc.OverrideIstanbul = c.OverrideIstanbul
|
|
||||||
enc.OverrideMuirGlacier = c.OverrideMuirGlacier
|
|
||||||
return &enc, nil
|
return &enc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,11 +227,5 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
if dec.CheckpointOracle != nil {
|
if dec.CheckpointOracle != nil {
|
||||||
c.CheckpointOracle = dec.CheckpointOracle
|
c.CheckpointOracle = dec.CheckpointOracle
|
||||||
}
|
}
|
||||||
if dec.OverrideIstanbul != nil {
|
|
||||||
c.OverrideIstanbul = dec.OverrideIstanbul
|
|
||||||
}
|
|
||||||
if dec.OverrideMuirGlacier != nil {
|
|
||||||
c.OverrideMuirGlacier = dec.OverrideMuirGlacier
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ func (cs *chainSyncer) loop() {
|
||||||
|
|
||||||
case <-cs.pm.quitSync:
|
case <-cs.pm.quitSync:
|
||||||
if cs.doneCh != nil {
|
if cs.doneCh != nil {
|
||||||
cs.pm.downloader.Cancel()
|
cs.pm.downloader.Terminate() // Double term is fine, Cancel would block until queue is emptied
|
||||||
<-cs.doneCh
|
<-cs.doneCh
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
"value": "0x0"
|
"value": "0x0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"error": "evm: invalid jump destination",
|
"error": "invalid jump destination",
|
||||||
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
|
"from": "0xe4a13bc304682a903e9472f469c33801dd18d9e8",
|
||||||
"gas": "0x435c8",
|
"gas": "0x435c8",
|
||||||
"gasUsed": "0x435c8",
|
"gasUsed": "0x435c8",
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
"result": {
|
"result": {
|
||||||
"calls": [
|
"calls": [
|
||||||
{
|
{
|
||||||
"error": "invalid opcode 0xfe",
|
"error": "invalid opcode: opcode 0xfe not defined",
|
||||||
"from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
|
"from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76",
|
||||||
"gas": "0x75fe3",
|
"gas": "0x75fe3",
|
||||||
"gasUsed": "0x75fe3",
|
"gasUsed": "0x75fe3",
|
||||||
|
|
|
||||||
2
eth/tracers/testdata/call_tracer_throw.json
vendored
2
eth/tracers/testdata/call_tracer_throw.json
vendored
|
|
@ -50,7 +50,7 @@
|
||||||
},
|
},
|
||||||
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
"input": "0xf88b8206668504a817c8008303d09094c212e03b9e060e36facad5fd8f4435412ca22e6b80a451a34eb8000000000000000000000000000000000000000000000027fad02094277c000029a0692a3b4e7b2842f8dd7832e712c21e09f451f416c8976d5b8d02e8c0c2b4bea9a07645e90fc421b63dd755767fd93d3c03b4ec0c4d8fafa059558d08cf11d59750",
|
||||||
"result": {
|
"result": {
|
||||||
"error": "evm: invalid jump destination",
|
"error": "invalid jump destination",
|
||||||
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
|
"from": "0x70c9217d814985faef62b124420f8dfbddd96433",
|
||||||
"gas": "0x37b38",
|
"gas": "0x37b38",
|
||||||
"gasUsed": "0x37b38",
|
"gasUsed": "0x37b38",
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if _, _, _, err = st.TransitionDb(); err != nil {
|
if _, err = st.TransitionDb(); err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Retrieve the trace result and compare against the etalon
|
// Retrieve the trace result and compare against the etalon
|
||||||
|
|
@ -256,7 +256,7 @@ func TestCallTracer(t *testing.T) {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if _, _, _, err = st.TransitionDb(); err != nil {
|
if _, err = st.TransitionDb(); err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Retrieve the trace result and compare against the etalon
|
// Retrieve the trace result and compare against the etalon
|
||||||
|
|
|
||||||
3
go.mod
3
go.mod
|
|
@ -7,12 +7,11 @@ require (
|
||||||
github.com/Azure/azure-storage-blob-go v0.7.0
|
github.com/Azure/azure-storage-blob-go v0.7.0
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect
|
github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||||
github.com/VictoriaMetrics/fastcache v1.5.3
|
github.com/VictoriaMetrics/fastcache v1.5.7
|
||||||
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847
|
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847
|
||||||
github.com/aws/aws-sdk-go v1.25.48
|
github.com/aws/aws-sdk-go v1.25.48
|
||||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6
|
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6
|
||||||
github.com/cespare/cp v0.1.0
|
github.com/cespare/cp v0.1.0
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
|
||||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9
|
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
|
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
|
||||||
|
|
|
||||||
13
go.sum
13
go.sum
|
|
@ -21,12 +21,10 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
|
||||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/VictoriaMetrics/fastcache v1.5.3 h1:2odJnXLbFZcoV9KYtQ+7TH1UOq3dn3AssMgieaezkR4=
|
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
|
||||||
github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
|
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||||
|
|
@ -42,8 +40,6 @@ github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
||||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18 h1:pl4eWIqvFe/Kg3zkn7NxevNzILnZYWDCG7qbA1CJik0=
|
|
||||||
github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk=
|
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk=
|
||||||
|
|
@ -172,8 +168,6 @@ github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubr
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9 h1:5Cp3cVwpQP4aCQ6jx6dNLP3IarbYiuStmIzYu+BjQwY=
|
|
||||||
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
|
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
|
||||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||||
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
|
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
|
||||||
|
|
@ -192,7 +186,6 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk=
|
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk=
|
||||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
|
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
|
@ -223,8 +216,6 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA=
|
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9 h1:ITeyKbRetrVzqR3U1eY+ywgp7IBspGd1U/bkwd1gWu4=
|
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9 h1:ITeyKbRetrVzqR3U1eY+ywgp7IBspGd1U/bkwd1gWu4=
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
|
|
||||||
|
|
@ -803,16 +803,19 @@ func (b *Block) Call(ctx context.Context, args struct {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, vm.Config{}, 5*time.Second, b.backend.RPCGasCap())
|
result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, vm.Config{}, 5*time.Second, b.backend.RPCGasCap())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
status := hexutil.Uint64(1)
|
status := hexutil.Uint64(1)
|
||||||
if failed {
|
if result.Failed() {
|
||||||
status = 0
|
status = 0
|
||||||
}
|
}
|
||||||
return &CallResult{
|
return &CallResult{
|
||||||
data: hexutil.Bytes(result),
|
data: result.Return(),
|
||||||
gasUsed: hexutil.Uint64(gas),
|
gasUsed: hexutil.Uint64(result.UsedGas),
|
||||||
status: status,
|
status: status,
|
||||||
}, err
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
||||||
|
|
@ -869,16 +872,19 @@ func (p *Pending) Call(ctx context.Context, args struct {
|
||||||
Data ethapi.CallArgs
|
Data ethapi.CallArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, vm.Config{}, 5*time.Second, p.backend.RPCGasCap())
|
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, vm.Config{}, 5*time.Second, p.backend.RPCGasCap())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
status := hexutil.Uint64(1)
|
status := hexutil.Uint64(1)
|
||||||
if failed {
|
if result.Failed() {
|
||||||
status = 0
|
status = 0
|
||||||
}
|
}
|
||||||
return &CallResult{
|
return &CallResult{
|
||||||
data: hexutil.Bytes(result),
|
data: result.Return(),
|
||||||
gasUsed: hexutil.Uint64(gas),
|
gasUsed: hexutil.Uint64(result.UsedGas),
|
||||||
status: status,
|
status: status,
|
||||||
}, err
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,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/accounts/abi"
|
||||||
"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/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -789,14 +790,13 @@ type account struct {
|
||||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
|
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) (*core.ExecutionResult, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, 0, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the fields of specified contracts before execution.
|
// Override the fields of specified contracts before execution.
|
||||||
for addr, account := range overrides {
|
for addr, account := range overrides {
|
||||||
// Override account nonce.
|
// Override account nonce.
|
||||||
|
|
@ -812,7 +812,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
|
||||||
state.SetBalance(addr, (*big.Int)(*account.Balance))
|
state.SetBalance(addr, (*big.Int)(*account.Balance))
|
||||||
}
|
}
|
||||||
if account.State != nil && account.StateDiff != nil {
|
if account.State != nil && account.StateDiff != nil {
|
||||||
return nil, 0, false, fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
|
return nil, fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
|
||||||
}
|
}
|
||||||
// Replace entire state if caller requires.
|
// Replace entire state if caller requires.
|
||||||
if account.State != nil {
|
if account.State != nil {
|
||||||
|
|
@ -825,7 +825,6 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup context so it may be cancelled the call has completed
|
// Setup context so it may be cancelled the call has completed
|
||||||
// or, in case of unmetered gas, setup a context with a timeout.
|
// or, in case of unmetered gas, setup a context with a timeout.
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
|
|
@ -842,7 +841,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
|
||||||
msg := args.ToMessage(globalGasCap)
|
msg := args.ToMessage(globalGasCap)
|
||||||
evm, vmError, err := b.GetEVM(ctx, msg, state, header)
|
evm, vmError, err := b.GetEVM(ctx, msg, state, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Wait for the context to be done and cancel the evm. Even if the
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
// EVM has finished, cancelling may be done (repeatedly)
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
|
|
@ -854,15 +853,15 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
|
||||||
// Setup the gas pool (also for unmetered requests)
|
// Setup the gas pool (also for unmetered requests)
|
||||||
// and apply the message.
|
// and apply the message.
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
|
result, err := core.ApplyMessage(evm, msg, gp)
|
||||||
if err := vmError(); err != nil {
|
if err := vmError(); err != nil {
|
||||||
return nil, 0, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// If the timer caused an abort, return an appropriate error message
|
// If the timer caused an abort, return an appropriate error message
|
||||||
if evm.Cancelled() {
|
if evm.Cancelled() {
|
||||||
return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout)
|
return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout)
|
||||||
}
|
}
|
||||||
return res, gas, failed, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
|
|
@ -876,8 +875,28 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOr
|
||||||
if overrides != nil {
|
if overrides != nil {
|
||||||
accounts = *overrides
|
accounts = *overrides
|
||||||
}
|
}
|
||||||
result, _, _, err := DoCall(ctx, s.b, args, blockNrOrHash, accounts, vm.Config{}, 5*time.Second, s.b.RPCGasCap())
|
result, err := DoCall(ctx, s.b, args, blockNrOrHash, accounts, vm.Config{}, 5*time.Second, s.b.RPCGasCap())
|
||||||
return (hexutil.Bytes)(result), err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result.Return(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type estimateGasError struct {
|
||||||
|
error string // Concrete error type if it's failed to estimate gas usage
|
||||||
|
vmerr error // Additional field, it's non-nil if the given transaction is invalid
|
||||||
|
revert string // Additional field, it's non-empty if the transaction is reverted and reason is provided
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e estimateGasError) Error() string {
|
||||||
|
errMsg := e.error
|
||||||
|
if e.vmerr != nil {
|
||||||
|
errMsg += fmt.Sprintf(" (%v)", e.vmerr)
|
||||||
|
}
|
||||||
|
if e.revert != "" {
|
||||||
|
errMsg += fmt.Sprintf(" (%s)", e.revert)
|
||||||
|
}
|
||||||
|
return errMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap *big.Int) (hexutil.Uint64, error) {
|
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap *big.Int) (hexutil.Uint64, error) {
|
||||||
|
|
@ -908,19 +927,30 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash
|
||||||
args.From = new(common.Address)
|
args.From = new(common.Address)
|
||||||
}
|
}
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
args.Gas = (*hexutil.Uint64)(&gas)
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
|
||||||
_, _, failed, err := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap)
|
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap)
|
||||||
if err != nil || failed {
|
if err != nil {
|
||||||
return false
|
if err == core.ErrIntrinsicGas {
|
||||||
|
return true, nil, nil // Special case, raise gas limit
|
||||||
|
}
|
||||||
|
return true, nil, err // Bail out
|
||||||
}
|
}
|
||||||
return true
|
return result.Failed(), result, nil
|
||||||
}
|
}
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
// Execute the binary search and hone in on an executable gas limit
|
||||||
for lo+1 < hi {
|
for lo+1 < hi {
|
||||||
mid := (hi + lo) / 2
|
mid := (hi + lo) / 2
|
||||||
if !executable(mid) {
|
failed, _, err := executable(mid)
|
||||||
|
|
||||||
|
// If the error is not nil(consensus error), it means the provided message
|
||||||
|
// call or transaction will never be accepted no matter how much gas it is
|
||||||
|
// assigened. Return the error directly, don't struggle any more.
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
} else {
|
} else {
|
||||||
hi = mid
|
hi = mid
|
||||||
|
|
@ -928,8 +958,29 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash
|
||||||
}
|
}
|
||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||||
if hi == cap {
|
if hi == cap {
|
||||||
if !executable(hi) {
|
failed, result, err := executable(hi)
|
||||||
return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", cap)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||||
|
var revert string
|
||||||
|
if len(result.Revert()) > 0 {
|
||||||
|
ret, err := abi.UnpackRevert(result.Revert())
|
||||||
|
if err != nil {
|
||||||
|
revert = hexutil.Encode(result.Revert())
|
||||||
|
} else {
|
||||||
|
revert = ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, estimateGasError{
|
||||||
|
error: "always failing transaction",
|
||||||
|
vmerr: result.Err,
|
||||||
|
revert: revert,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, the specified gas cap is too low
|
||||||
|
return 0, estimateGasError{error: fmt.Sprintf("gas required exceeds allowance (%d)", cap)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hexutil.Uint64(hi), nil
|
return hexutil.Uint64(hi), nil
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis,
|
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
|
||||||
config.OverrideIstanbul, config.OverrideMuirGlacier)
|
|
||||||
if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat {
|
if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat {
|
||||||
return nil, genesisErr
|
return nil, genesisErr
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
// Verify the proof and store if checks out
|
// Verify the proof and store if checks out
|
||||||
nodeSet := proofs.NodeSet()
|
nodeSet := proofs.NodeSet()
|
||||||
reads := &readTraceDB{db: nodeSet}
|
reads := &readTraceDB{db: nodeSet}
|
||||||
if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil {
|
if _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil {
|
||||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||||
}
|
}
|
||||||
// check if all nodes have been read by VerifyProof
|
// check if all nodes have been read by VerifyProof
|
||||||
|
|
@ -378,7 +378,7 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
||||||
|
|
||||||
reads := &readTraceDB{db: nodeSet}
|
reads := &readTraceDB{db: nodeSet}
|
||||||
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
|
value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -470,7 +470,7 @@ func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
|
|
||||||
for i, idx := range r.SectionIndexList {
|
for i, idx := range r.SectionIndexList {
|
||||||
binary.BigEndian.PutUint64(encNumber[2:], idx)
|
binary.BigEndian.PutUint64(encNumber[2:], idx)
|
||||||
value, _, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
|
value, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,8 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
|
|
||||||
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
|
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
|
result, _ := core.ApplyMessage(vmenv, msg, gp)
|
||||||
res = append(res, ret...)
|
res = append(res, result.Return()...)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
header := lc.GetHeaderByHash(bhash)
|
header := lc.GetHeaderByHash(bhash)
|
||||||
|
|
@ -146,9 +146,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
context := core.NewEVMContext(msg, header, lc, nil)
|
context := core.NewEVMContext(msg, header, lc, nil)
|
||||||
vmenv := vm.NewEVM(context, state, config, vm.Config{})
|
vmenv := vm.NewEVM(context, state, config, vm.Config{})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
|
result, _ := core.ApplyMessage(vmenv, msg, gp)
|
||||||
if state.Error() == nil {
|
if state.Error() == nil {
|
||||||
res = append(res, ret...)
|
res = append(res, result.Return()...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,8 +198,8 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
|
||||||
context := core.NewEVMContext(msg, header, chain, nil)
|
context := core.NewEVMContext(msg, header, chain, nil)
|
||||||
vmenv := vm.NewEVM(context, st, config, vm.Config{})
|
vmenv := vm.NewEVM(context, st, config, vm.Config{})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
|
result, _ := core.ApplyMessage(vmenv, msg, gp)
|
||||||
res = append(res, ret...)
|
res = append(res, result.Return()...)
|
||||||
if st.Error() != nil {
|
if st.Error() != nil {
|
||||||
return res, st.Error()
|
return res, st.Error()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartHTTPEndpoint starts the HTTP RPC endpoint.
|
// StartHTTPEndpoint starts the HTTP RPC endpoint.
|
||||||
func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (net.Listener, error) {
|
func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (*http.Server, net.Addr, error) {
|
||||||
// start the HTTP listener
|
// start the HTTP listener
|
||||||
var (
|
var (
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// make sure timeout values are meaningful
|
// make sure timeout values are meaningful
|
||||||
CheckTimeouts(&timeouts)
|
CheckTimeouts(&timeouts)
|
||||||
|
|
@ -45,22 +45,22 @@ func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.
|
||||||
IdleTimeout: timeouts.IdleTimeout,
|
IdleTimeout: timeouts.IdleTimeout,
|
||||||
}
|
}
|
||||||
go httpSrv.Serve(listener)
|
go httpSrv.Serve(listener)
|
||||||
return listener, err
|
return httpSrv, listener.Addr(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// startWSEndpoint starts a websocket endpoint.
|
// startWSEndpoint starts a websocket endpoint.
|
||||||
func startWSEndpoint(endpoint string, handler http.Handler) (net.Listener, error) {
|
func startWSEndpoint(endpoint string, handler http.Handler) (*http.Server, net.Addr, error) {
|
||||||
// start the HTTP listener
|
// start the HTTP listener
|
||||||
var (
|
var (
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
wsSrv := &http.Server{Handler: handler}
|
wsSrv := &http.Server{Handler: handler}
|
||||||
go wsSrv.Serve(listener)
|
go wsSrv.Serve(listener)
|
||||||
return listener, err
|
return wsSrv, listener.Addr(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkModuleAvailability checks that all names given in modules are actually
|
// checkModuleAvailability checks that all names given in modules are actually
|
||||||
|
|
|
||||||
66
node/node.go
66
node/node.go
|
|
@ -17,9 +17,11 @@
|
||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -59,14 +61,16 @@ type Node struct {
|
||||||
ipcListener net.Listener // IPC RPC listener socket to serve API requests
|
ipcListener net.Listener // IPC RPC listener socket to serve API requests
|
||||||
ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
|
ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
|
||||||
|
|
||||||
httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
|
httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
|
||||||
httpWhitelist []string // HTTP RPC modules to allow through this endpoint
|
httpWhitelist []string // HTTP RPC modules to allow through this endpoint
|
||||||
httpListener net.Listener // HTTP RPC listener socket to server API requests
|
httpListenerAddr net.Addr // Address of HTTP RPC listener socket serving API requests
|
||||||
httpHandler *rpc.Server // HTTP RPC request handler to process the API requests
|
httpServer *http.Server // HTTP RPC HTTP server
|
||||||
|
httpHandler *rpc.Server // HTTP RPC request handler to process the API requests
|
||||||
|
|
||||||
wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled)
|
wsEndpoint string // WebSocket endpoint (interface + port) to listen at (empty = WebSocket disabled)
|
||||||
wsListener net.Listener // Websocket RPC listener socket to server API requests
|
wsListenerAddr net.Addr // Address of WebSocket RPC listener socket serving API requests
|
||||||
wsHandler *rpc.Server // Websocket RPC request handler to process the API requests
|
wsHTTPServer *http.Server // WebSocket RPC HTTP server
|
||||||
|
wsHandler *rpc.Server // WebSocket RPC request handler to process the API requests
|
||||||
|
|
||||||
stop chan struct{} // Channel to wait for termination notifications
|
stop chan struct{} // Channel to wait for termination notifications
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
@ -375,23 +379,24 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
handler := NewHTTPHandlerStack(srv, cors, vhosts)
|
handler := NewHTTPHandlerStack(srv, cors, vhosts)
|
||||||
// wrap handler in websocket handler only if websocket port is the same as http rpc
|
// wrap handler in WebSocket handler only if WebSocket port is the same as http rpc
|
||||||
if n.httpEndpoint == n.wsEndpoint {
|
if n.httpEndpoint == n.wsEndpoint {
|
||||||
handler = NewWebsocketUpgradeHandler(handler, srv.WebsocketHandler(wsOrigins))
|
handler = NewWebsocketUpgradeHandler(handler, srv.WebsocketHandler(wsOrigins))
|
||||||
}
|
}
|
||||||
listener, err := StartHTTPEndpoint(endpoint, timeouts, handler)
|
httpServer, addr, err := StartHTTPEndpoint(endpoint, timeouts, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", listener.Addr()),
|
n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", addr),
|
||||||
"cors", strings.Join(cors, ","),
|
"cors", strings.Join(cors, ","),
|
||||||
"vhosts", strings.Join(vhosts, ","))
|
"vhosts", strings.Join(vhosts, ","))
|
||||||
if n.httpEndpoint == n.wsEndpoint {
|
if n.httpEndpoint == n.wsEndpoint {
|
||||||
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", listener.Addr()))
|
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr))
|
||||||
}
|
}
|
||||||
// All listeners booted successfully
|
// All listeners booted successfully
|
||||||
n.httpEndpoint = endpoint
|
n.httpEndpoint = endpoint
|
||||||
n.httpListener = listener
|
n.httpListenerAddr = addr
|
||||||
|
n.httpServer = httpServer
|
||||||
n.httpHandler = srv
|
n.httpHandler = srv
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -399,11 +404,10 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors
|
||||||
|
|
||||||
// stopHTTP terminates the HTTP RPC endpoint.
|
// stopHTTP terminates the HTTP RPC endpoint.
|
||||||
func (n *Node) stopHTTP() {
|
func (n *Node) stopHTTP() {
|
||||||
if n.httpListener != nil {
|
if n.httpServer != nil {
|
||||||
url := fmt.Sprintf("http://%v/", n.httpListener.Addr())
|
// Don't bother imposing a timeout here.
|
||||||
n.httpListener.Close()
|
n.httpServer.Shutdown(context.Background())
|
||||||
n.httpListener = nil
|
n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%v/", n.httpListenerAddr))
|
||||||
n.log.Info("HTTP endpoint closed", "url", url)
|
|
||||||
}
|
}
|
||||||
if n.httpHandler != nil {
|
if n.httpHandler != nil {
|
||||||
n.httpHandler.Stop()
|
n.httpHandler.Stop()
|
||||||
|
|
@ -411,7 +415,7 @@ func (n *Node) stopHTTP() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// startWS initializes and starts the websocket RPC endpoint.
|
// startWS initializes and starts the WebSocket RPC endpoint.
|
||||||
func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error {
|
func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error {
|
||||||
// Short circuit if the WS endpoint isn't being exposed
|
// Short circuit if the WS endpoint isn't being exposed
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
|
|
@ -424,26 +428,26 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
listener, err := startWSEndpoint(endpoint, handler)
|
httpServer, addr, err := startWSEndpoint(endpoint, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr()))
|
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr))
|
||||||
// All listeners booted successfully
|
// All listeners booted successfully
|
||||||
n.wsEndpoint = endpoint
|
n.wsEndpoint = endpoint
|
||||||
n.wsListener = listener
|
n.wsListenerAddr = addr
|
||||||
|
n.wsHTTPServer = httpServer
|
||||||
n.wsHandler = srv
|
n.wsHandler = srv
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopWS terminates the websocket RPC endpoint.
|
// stopWS terminates the WebSocket RPC endpoint.
|
||||||
func (n *Node) stopWS() {
|
func (n *Node) stopWS() {
|
||||||
if n.wsListener != nil {
|
if n.wsHTTPServer != nil {
|
||||||
n.wsListener.Close()
|
// Don't bother imposing a timeout here.
|
||||||
n.wsListener = nil
|
n.wsHTTPServer.Shutdown(context.Background())
|
||||||
|
n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%v", n.wsListenerAddr))
|
||||||
n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%s", n.wsEndpoint))
|
|
||||||
}
|
}
|
||||||
if n.wsHandler != nil {
|
if n.wsHandler != nil {
|
||||||
n.wsHandler.Stop()
|
n.wsHandler.Stop()
|
||||||
|
|
@ -607,8 +611,8 @@ func (n *Node) HTTPEndpoint() string {
|
||||||
n.lock.Lock()
|
n.lock.Lock()
|
||||||
defer n.lock.Unlock()
|
defer n.lock.Unlock()
|
||||||
|
|
||||||
if n.httpListener != nil {
|
if n.httpListenerAddr != nil {
|
||||||
return n.httpListener.Addr().String()
|
return n.httpListenerAddr.String()
|
||||||
}
|
}
|
||||||
return n.httpEndpoint
|
return n.httpEndpoint
|
||||||
}
|
}
|
||||||
|
|
@ -618,8 +622,8 @@ func (n *Node) WSEndpoint() string {
|
||||||
n.lock.Lock()
|
n.lock.Lock()
|
||||||
defer n.lock.Unlock()
|
defer n.lock.Unlock()
|
||||||
|
|
||||||
if n.wsListener != nil {
|
if n.wsListenerAddr != nil {
|
||||||
return n.wsListener.Addr().String()
|
return n.wsListenerAddr.String()
|
||||||
}
|
}
|
||||||
return n.wsEndpoint
|
return n.wsEndpoint
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func MustParseV4(rawurl string) *Node {
|
||||||
//
|
//
|
||||||
// For complete nodes, the node ID is encoded in the username portion
|
// For complete nodes, the node ID is encoded in the username portion
|
||||||
// of the URL, separated from the host by an @ sign. The hostname can
|
// of the URL, separated from the host by an @ sign. The hostname can
|
||||||
// only be given as an IP address, DNS domain names are not allowed.
|
// only be given as an IP address or using DNS domain name.
|
||||||
// The port in the host name section is the TCP listening port. If the
|
// The port in the host name section is the TCP listening port. If the
|
||||||
// TCP and UDP (discovery) ports differ, the UDP port is specified as
|
// TCP and UDP (discovery) ports differ, the UDP port is specified as
|
||||||
// query parameter "discport".
|
// query parameter "discport".
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,7 @@ func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error)
|
||||||
}
|
}
|
||||||
p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version))
|
p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version))
|
||||||
go func() {
|
go func() {
|
||||||
|
defer p.wg.Done()
|
||||||
err := proto.Run(p, rw)
|
err := proto.Run(p, rw)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version))
|
p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version))
|
||||||
|
|
@ -373,7 +374,6 @@ func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error)
|
||||||
p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err)
|
p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err)
|
||||||
}
|
}
|
||||||
p.protoErr <- err
|
p.protoErr <- err
|
||||||
p.wg.Done()
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
|
||||||
return nil, fmt.Errorf("signature %q matches, but arguments mismatch: %v", method.String(), err)
|
return nil, fmt.Errorf("signature %q matches, but arguments mismatch: %v", method.String(), err)
|
||||||
}
|
}
|
||||||
// Everything valid, assemble the call infos for the signer
|
// Everything valid, assemble the call infos for the signer
|
||||||
decoded := decodedCallData{signature: method.Sig(), name: method.RawName}
|
decoded := decodedCallData{signature: method.Sig, name: method.RawName}
|
||||||
for i := 0; i < len(method.Inputs); i++ {
|
for i := 0; i < len(method.Inputs); i++ {
|
||||||
decoded.inputs = append(decoded.inputs, decodedArgument{
|
decoded.inputs = append(decoded.inputs, decodedArgument{
|
||||||
soltype: method.Inputs[i],
|
soltype: method.Inputs[i],
|
||||||
|
|
@ -158,7 +158,7 @@ func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
|
||||||
if !bytes.Equal(encoded, argdata) {
|
if !bytes.Equal(encoded, argdata) {
|
||||||
was := common.Bytes2Hex(encoded)
|
was := common.Bytes2Hex(encoded)
|
||||||
exp := common.Bytes2Hex(argdata)
|
exp := common.Bytes2Hex(argdata)
|
||||||
return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig())
|
return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig)
|
||||||
}
|
}
|
||||||
return &decoded, nil
|
return &decoded, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ func TestEmbeddedDatabase(t *testing.T) {
|
||||||
t.Errorf("Failed to get method by id (%s): %v", id, err)
|
t.Errorf("Failed to get method by id (%s): %v", id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if m.Sig() != selector {
|
if m.Sig != selector {
|
||||||
t.Errorf("Selector mismatch: have %v, want %v", m.Sig(), selector)
|
t.Errorf("Selector mismatch: have %v, want %v", m.Sig, selector)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
||||||
gaspool := new(core.GasPool)
|
gaspool := new(core.GasPool)
|
||||||
gaspool.AddGas(block.GasLimit())
|
gaspool.AddGas(block.GasLimit())
|
||||||
snapshot := statedb.Snapshot()
|
snapshot := statedb.Snapshot()
|
||||||
if _, _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
|
if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
// Commit block
|
// Commit block
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,11 @@ var (
|
||||||
// secureKeyPrefix is the database key prefix used to store trie node preimages.
|
// secureKeyPrefix is the database key prefix used to store trie node preimages.
|
||||||
var secureKeyPrefix = []byte("secure-key-")
|
var secureKeyPrefix = []byte("secure-key-")
|
||||||
|
|
||||||
|
// secureKeyPrefixLength is the length of the above prefix
|
||||||
|
const secureKeyPrefixLength = 11
|
||||||
|
|
||||||
// secureKeyLength is the length of the above prefix + 32byte hash.
|
// secureKeyLength is the length of the above prefix + 32byte hash.
|
||||||
const secureKeyLength = 11 + 32
|
const secureKeyLength = secureKeyPrefixLength + 32
|
||||||
|
|
||||||
// Database is an intermediate write layer between the trie data structures and
|
// Database is an intermediate write layer between the trie data structures and
|
||||||
// the disk database. The aim is to accumulate trie writes in-memory and only
|
// the disk database. The aim is to accumulate trie writes in-memory and only
|
||||||
|
|
@ -79,7 +82,6 @@ type Database struct {
|
||||||
newest common.Hash // Newest tracked node, flush-list tail
|
newest common.Hash // Newest tracked node, flush-list tail
|
||||||
|
|
||||||
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
|
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
|
||||||
seckeybuf [secureKeyLength]byte // Ephemeral buffer for calculating preimage keys
|
|
||||||
|
|
||||||
gctime time.Duration // Time spent on garbage collection since last commit
|
gctime time.Duration // Time spent on garbage collection since last commit
|
||||||
gcnodes uint64 // Nodes garbage collected since last commit
|
gcnodes uint64 // Nodes garbage collected since last commit
|
||||||
|
|
@ -445,15 +447,15 @@ func (db *Database) preimage(hash common.Hash) ([]byte, error) {
|
||||||
return preimage, nil
|
return preimage, nil
|
||||||
}
|
}
|
||||||
// Content unavailable in memory, attempt to retrieve from disk
|
// Content unavailable in memory, attempt to retrieve from disk
|
||||||
return db.diskdb.Get(db.secureKey(hash[:]))
|
return db.diskdb.Get(secureKey(hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
// secureKey returns the database key for the preimage of key, as an ephemeral
|
// secureKey returns the database key for the preimage of key (as a newly
|
||||||
// buffer. The caller must not hold onto the return value because it will become
|
// allocated byte-slice)
|
||||||
// invalid on the next call.
|
func secureKey(hash common.Hash) []byte {
|
||||||
func (db *Database) secureKey(key []byte) []byte {
|
buf := make([]byte, secureKeyLength)
|
||||||
buf := append(db.seckeybuf[:0], secureKeyPrefix...)
|
copy(buf, secureKeyPrefix)
|
||||||
buf = append(buf, key...)
|
copy(buf[secureKeyPrefixLength:], hash[:])
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -596,12 +598,18 @@ func (db *Database) Cap(limit common.StorageSize) error {
|
||||||
size := db.dirtiesSize + common.StorageSize((len(db.dirties)-1)*cachedNodeSize)
|
size := db.dirtiesSize + common.StorageSize((len(db.dirties)-1)*cachedNodeSize)
|
||||||
size += db.childrenSize - common.StorageSize(len(db.dirties[common.Hash{}].children)*(common.HashLength+2))
|
size += db.childrenSize - common.StorageSize(len(db.dirties[common.Hash{}].children)*(common.HashLength+2))
|
||||||
|
|
||||||
|
// We reuse an ephemeral buffer for the keys. The batch Put operation
|
||||||
|
// copies it internally, so we can reuse it.
|
||||||
|
var keyBuf [secureKeyLength]byte
|
||||||
|
copy(keyBuf[:], secureKeyPrefix)
|
||||||
|
|
||||||
// If the preimage cache got large enough, push to disk. If it's still small
|
// If the preimage cache got large enough, push to disk. If it's still small
|
||||||
// leave for later to deduplicate writes.
|
// leave for later to deduplicate writes.
|
||||||
flushPreimages := db.preimagesSize > 4*1024*1024
|
flushPreimages := db.preimagesSize > 4*1024*1024
|
||||||
if flushPreimages {
|
if flushPreimages {
|
||||||
for hash, preimage := range db.preimages {
|
for hash, preimage := range db.preimages {
|
||||||
if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil {
|
copy(keyBuf[secureKeyPrefixLength:], hash[:])
|
||||||
|
if err := batch.Put(keyBuf[:], preimage); err != nil {
|
||||||
log.Error("Failed to commit preimage from trie database", "err", err)
|
log.Error("Failed to commit preimage from trie database", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -692,9 +700,15 @@ func (db *Database) Commit(node common.Hash, report bool) error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
batch := db.diskdb.NewBatch()
|
batch := db.diskdb.NewBatch()
|
||||||
|
|
||||||
|
// We reuse an ephemeral buffer for the keys. The batch Put operation
|
||||||
|
// copies it internally, so we can reuse it.
|
||||||
|
var keyBuf [secureKeyLength]byte
|
||||||
|
copy(keyBuf[:], secureKeyPrefix)
|
||||||
|
|
||||||
// Move all of the accumulated preimages into a write batch
|
// Move all of the accumulated preimages into a write batch
|
||||||
for hash, preimage := range db.preimages {
|
for hash, preimage := range db.preimages {
|
||||||
if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil {
|
copy(keyBuf[secureKeyPrefixLength:], hash[:])
|
||||||
|
if err := batch.Put(keyBuf[:], preimage); err != nil {
|
||||||
log.Error("Failed to commit preimage from trie database", "err", err)
|
log.Error("Failed to commit preimage from trie database", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
221
trie/proof.go
221
trie/proof.go
|
|
@ -18,10 +18,12 @@ package trie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -101,33 +103,232 @@ func (t *SecureTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWri
|
||||||
// VerifyProof checks merkle proofs. The given proof must contain the value for
|
// VerifyProof checks merkle proofs. The given proof must contain the value for
|
||||||
// key in a trie with the given root hash. VerifyProof returns an error if the
|
// key in a trie with the given root hash. VerifyProof returns an error if the
|
||||||
// proof contains invalid trie nodes or the wrong value.
|
// proof contains invalid trie nodes or the wrong value.
|
||||||
func VerifyProof(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, nodes int, err error) {
|
func VerifyProof(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, err error) {
|
||||||
key = keybytesToHex(key)
|
key = keybytesToHex(key)
|
||||||
wantHash := rootHash
|
wantHash := rootHash
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
buf, _ := proofDb.Get(wantHash[:])
|
buf, _ := proofDb.Get(wantHash[:])
|
||||||
if buf == nil {
|
if buf == nil {
|
||||||
return nil, i, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash)
|
return nil, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash)
|
||||||
}
|
}
|
||||||
n, err := decodeNode(wantHash[:], buf)
|
n, err := decodeNode(wantHash[:], buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, i, fmt.Errorf("bad proof node %d: %v", i, err)
|
return nil, fmt.Errorf("bad proof node %d: %v", i, err)
|
||||||
}
|
}
|
||||||
keyrest, cld := get(n, key)
|
keyrest, cld := get(n, key, true)
|
||||||
switch cld := cld.(type) {
|
switch cld := cld.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
// The trie doesn't contain the key.
|
// The trie doesn't contain the key.
|
||||||
return nil, i, nil
|
return nil, nil
|
||||||
case hashNode:
|
case hashNode:
|
||||||
key = keyrest
|
key = keyrest
|
||||||
copy(wantHash[:], cld)
|
copy(wantHash[:], cld)
|
||||||
case valueNode:
|
case valueNode:
|
||||||
return cld, i + 1, nil
|
return cld, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(tn node, key []byte) ([]byte, node) {
|
// proofToPath converts a merkle proof to trie node path.
|
||||||
|
// The main purpose of this function is recovering a node
|
||||||
|
// path from the merkle proof stream. All necessary nodes
|
||||||
|
// will be resolved and leave the remaining as hashnode.
|
||||||
|
func proofToPath(rootHash common.Hash, root node, key []byte, proofDb ethdb.KeyValueReader) (node, error) {
|
||||||
|
// resolveNode retrieves and resolves trie node from merkle proof stream
|
||||||
|
resolveNode := func(hash common.Hash) (node, error) {
|
||||||
|
buf, _ := proofDb.Get(hash[:])
|
||||||
|
if buf == nil {
|
||||||
|
return nil, fmt.Errorf("proof node (hash %064x) missing", hash)
|
||||||
|
}
|
||||||
|
n, err := decodeNode(hash[:], buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("bad proof node %v", err)
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
// If the root node is empty, resolve it first
|
||||||
|
if root == nil {
|
||||||
|
n, err := resolveNode(rootHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
root = n
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
child, parent node
|
||||||
|
keyrest []byte
|
||||||
|
terminate bool
|
||||||
|
)
|
||||||
|
key, parent = keybytesToHex(key), root
|
||||||
|
for {
|
||||||
|
keyrest, child = get(parent, key, false)
|
||||||
|
switch cld := child.(type) {
|
||||||
|
case nil:
|
||||||
|
// The trie doesn't contain the key.
|
||||||
|
return nil, errors.New("the node is not contained in trie")
|
||||||
|
case *shortNode:
|
||||||
|
key, parent = keyrest, child // Already resolved
|
||||||
|
continue
|
||||||
|
case *fullNode:
|
||||||
|
key, parent = keyrest, child // Already resolved
|
||||||
|
continue
|
||||||
|
case hashNode:
|
||||||
|
child, err = resolveNode(common.BytesToHash(cld))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case valueNode:
|
||||||
|
terminate = true
|
||||||
|
}
|
||||||
|
// Link the parent and child.
|
||||||
|
switch pnode := parent.(type) {
|
||||||
|
case *shortNode:
|
||||||
|
pnode.Val = child
|
||||||
|
case *fullNode:
|
||||||
|
pnode.Children[key[0]] = child
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("%T: invalid node: %v", pnode, pnode))
|
||||||
|
}
|
||||||
|
if terminate {
|
||||||
|
return root, nil // The whole path is resolved
|
||||||
|
}
|
||||||
|
key, parent = keyrest, child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsetInternal removes all internal node references(hashnode, embedded node).
|
||||||
|
// It should be called after a trie is constructed with two edge proofs. Also
|
||||||
|
// the given boundary keys must be the one used to construct the edge proofs.
|
||||||
|
//
|
||||||
|
// It's the key step for range proof. All visited nodes should be marked dirty
|
||||||
|
// since the node content might be modified. Besides it can happen that some
|
||||||
|
// fullnodes only have one child which is disallowed. But if the proof is valid,
|
||||||
|
// the missing children will be filled, otherwise it will be thrown anyway.
|
||||||
|
func unsetInternal(node node, left []byte, right []byte) error {
|
||||||
|
left, right = keybytesToHex(left), keybytesToHex(right)
|
||||||
|
|
||||||
|
// todo(rjl493456442) different length edge keys should be supported
|
||||||
|
if len(left) != len(right) {
|
||||||
|
return errors.New("inconsistent edge path")
|
||||||
|
}
|
||||||
|
// Step down to the fork point
|
||||||
|
prefix, pos := prefixLen(left, right), 0
|
||||||
|
for {
|
||||||
|
if pos >= prefix {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch n := (node).(type) {
|
||||||
|
case *shortNode:
|
||||||
|
if len(left)-pos < len(n.Key) || !bytes.Equal(n.Key, left[pos:pos+len(n.Key)]) {
|
||||||
|
return errors.New("invalid edge path")
|
||||||
|
}
|
||||||
|
n.flags = nodeFlag{dirty: true}
|
||||||
|
node, pos = n.Val, pos+len(n.Key)
|
||||||
|
case *fullNode:
|
||||||
|
n.flags = nodeFlag{dirty: true}
|
||||||
|
node, pos = n.Children[left[pos]], pos+1
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("%T: invalid node: %v", node, node))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn, ok := node.(*fullNode)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("the fork point must be a fullnode")
|
||||||
|
}
|
||||||
|
// Find the fork point! Unset all intermediate references
|
||||||
|
for i := left[prefix] + 1; i < right[prefix]; i++ {
|
||||||
|
fn.Children[i] = nil
|
||||||
|
}
|
||||||
|
fn.flags = nodeFlag{dirty: true}
|
||||||
|
unset(fn.Children[left[prefix]], left[prefix+1:], false)
|
||||||
|
unset(fn.Children[right[prefix]], right[prefix+1:], true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unset removes all internal node references either the left most or right most.
|
||||||
|
func unset(root node, rest []byte, removeLeft bool) {
|
||||||
|
switch rn := root.(type) {
|
||||||
|
case *fullNode:
|
||||||
|
if removeLeft {
|
||||||
|
for i := 0; i < int(rest[0]); i++ {
|
||||||
|
rn.Children[i] = nil
|
||||||
|
}
|
||||||
|
rn.flags = nodeFlag{dirty: true}
|
||||||
|
} else {
|
||||||
|
for i := rest[0] + 1; i < 16; i++ {
|
||||||
|
rn.Children[i] = nil
|
||||||
|
}
|
||||||
|
rn.flags = nodeFlag{dirty: true}
|
||||||
|
}
|
||||||
|
unset(rn.Children[rest[0]], rest[1:], removeLeft)
|
||||||
|
case *shortNode:
|
||||||
|
rn.flags = nodeFlag{dirty: true}
|
||||||
|
if _, ok := rn.Val.(valueNode); ok {
|
||||||
|
rn.Val = nilValueNode
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unset(rn.Val, rest[len(rn.Key):], removeLeft)
|
||||||
|
case hashNode, nil, valueNode:
|
||||||
|
panic("it shouldn't happen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyRangeProof checks whether the given leave nodes and edge proofs
|
||||||
|
// can prove the given trie leaves range is matched with given root hash
|
||||||
|
// and the range is consecutive(no gap inside).
|
||||||
|
func VerifyRangeProof(rootHash common.Hash, keys [][]byte, values [][]byte, firstProof ethdb.KeyValueReader, lastProof ethdb.KeyValueReader) error {
|
||||||
|
if len(keys) != len(values) {
|
||||||
|
return fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return fmt.Errorf("nothing to verify")
|
||||||
|
}
|
||||||
|
if len(keys) == 1 {
|
||||||
|
value, err := VerifyProof(rootHash, keys[0], firstProof)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(value, values[0]) {
|
||||||
|
return fmt.Errorf("correct proof but invalid data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Convert the edge proofs to edge trie paths. Then we can
|
||||||
|
// have the same tree architecture with the original one.
|
||||||
|
root, err := proofToPath(rootHash, nil, keys[0], firstProof)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Pass the root node here, the second path will be merged
|
||||||
|
// with the first one.
|
||||||
|
root, err = proofToPath(rootHash, root, keys[len(keys)-1], lastProof)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Remove all internal references. All the removed parts should
|
||||||
|
// be re-filled(or re-constructed) by the given leaves range.
|
||||||
|
if err := unsetInternal(root, keys[0], keys[len(keys)-1]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Rebuild the trie with the leave stream, the shape of trie
|
||||||
|
// should be same with the original one.
|
||||||
|
newtrie := &Trie{root: root, db: NewDatabase(memorydb.New())}
|
||||||
|
for index, key := range keys {
|
||||||
|
newtrie.TryUpdate(key, values[index])
|
||||||
|
}
|
||||||
|
if newtrie.Hash() != rootHash {
|
||||||
|
return fmt.Errorf("invalid proof, wanthash %x, got %x", rootHash, newtrie.Hash())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get returns the child of the given node. Return nil if the
|
||||||
|
// node with specified key doesn't exist at all.
|
||||||
|
//
|
||||||
|
// There is an additional flag `skipResolved`. If it's set then
|
||||||
|
// all resolved nodes won't be returned.
|
||||||
|
func get(tn node, key []byte, skipResolved bool) ([]byte, node) {
|
||||||
for {
|
for {
|
||||||
switch n := tn.(type) {
|
switch n := tn.(type) {
|
||||||
case *shortNode:
|
case *shortNode:
|
||||||
|
|
@ -136,9 +337,15 @@ func get(tn node, key []byte) ([]byte, node) {
|
||||||
}
|
}
|
||||||
tn = n.Val
|
tn = n.Val
|
||||||
key = key[len(n.Key):]
|
key = key[len(n.Key):]
|
||||||
|
if !skipResolved {
|
||||||
|
return key, tn
|
||||||
|
}
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
tn = n.Children[key[0]]
|
tn = n.Children[key[0]]
|
||||||
key = key[1:]
|
key = key[1:]
|
||||||
|
if !skipResolved {
|
||||||
|
return key, tn
|
||||||
|
}
|
||||||
case hashNode:
|
case hashNode:
|
||||||
return key, n
|
return key, n
|
||||||
case nil:
|
case nil:
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -65,7 +66,7 @@ func TestProof(t *testing.T) {
|
||||||
if proof == nil {
|
if proof == nil {
|
||||||
t.Fatalf("prover %d: missing key %x while constructing proof", i, kv.k)
|
t.Fatalf("prover %d: missing key %x while constructing proof", i, kv.k)
|
||||||
}
|
}
|
||||||
val, _, err := VerifyProof(root, kv.k, proof)
|
val, err := VerifyProof(root, kv.k, proof)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("prover %d: failed to verify proof for key %x: %v\nraw proof: %x", i, kv.k, err, proof)
|
t.Fatalf("prover %d: failed to verify proof for key %x: %v\nraw proof: %x", i, kv.k, err, proof)
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +88,7 @@ func TestOneElementProof(t *testing.T) {
|
||||||
if proof.Len() != 1 {
|
if proof.Len() != 1 {
|
||||||
t.Errorf("prover %d: proof should have one element", i)
|
t.Errorf("prover %d: proof should have one element", i)
|
||||||
}
|
}
|
||||||
val, _, err := VerifyProof(trie.Hash(), []byte("k"), proof)
|
val, err := VerifyProof(trie.Hash(), []byte("k"), proof)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("prover %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
t.Fatalf("prover %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +98,145 @@ func TestOneElementProof(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type entrySlice []*kv
|
||||||
|
|
||||||
|
func (p entrySlice) Len() int { return len(p) }
|
||||||
|
func (p entrySlice) Less(i, j int) bool { return bytes.Compare(p[i].k, p[j].k) < 0 }
|
||||||
|
func (p entrySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||||
|
|
||||||
|
func TestRangeProof(t *testing.T) {
|
||||||
|
trie, vals := randomTrie(4096)
|
||||||
|
var entries entrySlice
|
||||||
|
for _, kv := range vals {
|
||||||
|
entries = append(entries, kv)
|
||||||
|
}
|
||||||
|
sort.Sort(entries)
|
||||||
|
for i := 0; i < 500; i++ {
|
||||||
|
start := mrand.Intn(len(entries))
|
||||||
|
end := mrand.Intn(len(entries)-start) + start
|
||||||
|
if start == end {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
firstProof, lastProof := memorydb.New(), memorydb.New()
|
||||||
|
if err := trie.Prove(entries[start].k, 0, firstProof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the first node %v", err)
|
||||||
|
}
|
||||||
|
if err := trie.Prove(entries[end-1].k, 0, lastProof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the last node %v", err)
|
||||||
|
}
|
||||||
|
var keys [][]byte
|
||||||
|
var vals [][]byte
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
keys = append(keys, entries[i].k)
|
||||||
|
vals = append(vals, entries[i].v)
|
||||||
|
}
|
||||||
|
err := VerifyRangeProof(trie.Hash(), keys, vals, firstProof, lastProof)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Case %d(%d->%d) expect no error, got %v", i, start, end-1, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadRangeProof(t *testing.T) {
|
||||||
|
trie, vals := randomTrie(4096)
|
||||||
|
var entries entrySlice
|
||||||
|
for _, kv := range vals {
|
||||||
|
entries = append(entries, kv)
|
||||||
|
}
|
||||||
|
sort.Sort(entries)
|
||||||
|
|
||||||
|
for i := 0; i < 500; i++ {
|
||||||
|
start := mrand.Intn(len(entries))
|
||||||
|
end := mrand.Intn(len(entries)-start) + start
|
||||||
|
if start == end {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
firstProof, lastProof := memorydb.New(), memorydb.New()
|
||||||
|
if err := trie.Prove(entries[start].k, 0, firstProof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the first node %v", err)
|
||||||
|
}
|
||||||
|
if err := trie.Prove(entries[end-1].k, 0, lastProof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the last node %v", err)
|
||||||
|
}
|
||||||
|
var keys [][]byte
|
||||||
|
var vals [][]byte
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
keys = append(keys, entries[i].k)
|
||||||
|
vals = append(vals, entries[i].v)
|
||||||
|
}
|
||||||
|
testcase := mrand.Intn(6)
|
||||||
|
var index int
|
||||||
|
switch testcase {
|
||||||
|
case 0:
|
||||||
|
// Modified key
|
||||||
|
index = mrand.Intn(end - start)
|
||||||
|
keys[index] = randBytes(32) // In theory it can't be same
|
||||||
|
case 1:
|
||||||
|
// Modified val
|
||||||
|
index = mrand.Intn(end - start)
|
||||||
|
vals[index] = randBytes(20) // In theory it can't be same
|
||||||
|
case 2:
|
||||||
|
// Gapped entry slice
|
||||||
|
index = mrand.Intn(end - start)
|
||||||
|
keys = append(keys[:index], keys[index+1:]...)
|
||||||
|
vals = append(vals[:index], vals[index+1:]...)
|
||||||
|
if len(keys) <= 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
// Switched entry slice, same effect with gapped
|
||||||
|
index = mrand.Intn(end - start)
|
||||||
|
keys[index] = entries[len(entries)-1].k
|
||||||
|
vals[index] = entries[len(entries)-1].v
|
||||||
|
case 4:
|
||||||
|
// Set random key to nil
|
||||||
|
index = mrand.Intn(end - start)
|
||||||
|
keys[index] = nil
|
||||||
|
case 5:
|
||||||
|
// Set random value to nil
|
||||||
|
index = mrand.Intn(end - start)
|
||||||
|
vals[index] = nil
|
||||||
|
}
|
||||||
|
err := VerifyRangeProof(trie.Hash(), keys, vals, firstProof, lastProof)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("%d Case %d index %d range: (%d->%d) expect error, got nil", i, testcase, index, start, end-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGappedRangeProof focuses on the small trie with embedded nodes.
|
||||||
|
// If the gapped node is embedded in the trie, it should be detected too.
|
||||||
|
func TestGappedRangeProof(t *testing.T) {
|
||||||
|
trie := new(Trie)
|
||||||
|
var entries []*kv // Sorted entries
|
||||||
|
for i := byte(0); i < 10; i++ {
|
||||||
|
value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
|
||||||
|
trie.Update(value.k, value.v)
|
||||||
|
entries = append(entries, value)
|
||||||
|
}
|
||||||
|
first, last := 2, 8
|
||||||
|
firstProof, lastProof := memorydb.New(), memorydb.New()
|
||||||
|
if err := trie.Prove(entries[first].k, 0, firstProof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the first node %v", err)
|
||||||
|
}
|
||||||
|
if err := trie.Prove(entries[last-1].k, 0, lastProof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the last node %v", err)
|
||||||
|
}
|
||||||
|
var keys [][]byte
|
||||||
|
var vals [][]byte
|
||||||
|
for i := first; i < last; i++ {
|
||||||
|
if i == (first+last)/2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keys = append(keys, entries[i].k)
|
||||||
|
vals = append(vals, entries[i].v)
|
||||||
|
}
|
||||||
|
err := VerifyRangeProof(trie.Hash(), keys, vals, firstProof, lastProof)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expect error, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBadProof(t *testing.T) {
|
func TestBadProof(t *testing.T) {
|
||||||
trie, vals := randomTrie(800)
|
trie, vals := randomTrie(800)
|
||||||
root := trie.Hash()
|
root := trie.Hash()
|
||||||
|
|
@ -118,7 +258,7 @@ func TestBadProof(t *testing.T) {
|
||||||
mutateByte(val)
|
mutateByte(val)
|
||||||
proof.Put(crypto.Keccak256(val), val)
|
proof.Put(crypto.Keccak256(val), val)
|
||||||
|
|
||||||
if _, _, err := VerifyProof(root, kv.k, proof); err == nil {
|
if _, err := VerifyProof(root, kv.k, proof); err == nil {
|
||||||
t.Fatalf("prover %d: expected proof to fail for key %x", i, kv.k)
|
t.Fatalf("prover %d: expected proof to fail for key %x", i, kv.k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +278,7 @@ func TestMissingKeyProof(t *testing.T) {
|
||||||
if proof.Len() != 1 {
|
if proof.Len() != 1 {
|
||||||
t.Errorf("test %d: proof should have one element", i)
|
t.Errorf("test %d: proof should have one element", i)
|
||||||
}
|
}
|
||||||
val, _, err := VerifyProof(trie.Hash(), []byte(key), proof)
|
val, err := VerifyProof(trie.Hash(), []byte(key), proof)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
t.Fatalf("test %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
||||||
}
|
}
|
||||||
|
|
@ -191,12 +331,50 @@ func BenchmarkVerifyProof(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
im := i % len(keys)
|
im := i % len(keys)
|
||||||
if _, _, err := VerifyProof(root, []byte(keys[im]), proofs[im]); err != nil {
|
if _, err := VerifyProof(root, []byte(keys[im]), proofs[im]); err != nil {
|
||||||
b.Fatalf("key %x: %v", keys[im], err)
|
b.Fatalf("key %x: %v", keys[im], err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkVerifyRangeProof10(b *testing.B) { benchmarkVerifyRangeProof(b, 10) }
|
||||||
|
func BenchmarkVerifyRangeProof100(b *testing.B) { benchmarkVerifyRangeProof(b, 100) }
|
||||||
|
func BenchmarkVerifyRangeProof1000(b *testing.B) { benchmarkVerifyRangeProof(b, 1000) }
|
||||||
|
func BenchmarkVerifyRangeProof5000(b *testing.B) { benchmarkVerifyRangeProof(b, 5000) }
|
||||||
|
|
||||||
|
func benchmarkVerifyRangeProof(b *testing.B, size int) {
|
||||||
|
trie, vals := randomTrie(8192)
|
||||||
|
var entries entrySlice
|
||||||
|
for _, kv := range vals {
|
||||||
|
entries = append(entries, kv)
|
||||||
|
}
|
||||||
|
sort.Sort(entries)
|
||||||
|
|
||||||
|
start := 2
|
||||||
|
end := start + size
|
||||||
|
firstProof, lastProof := memorydb.New(), memorydb.New()
|
||||||
|
if err := trie.Prove(entries[start].k, 0, firstProof); err != nil {
|
||||||
|
b.Fatalf("Failed to prove the first node %v", err)
|
||||||
|
}
|
||||||
|
if err := trie.Prove(entries[end-1].k, 0, lastProof); err != nil {
|
||||||
|
b.Fatalf("Failed to prove the last node %v", err)
|
||||||
|
}
|
||||||
|
var keys [][]byte
|
||||||
|
var values [][]byte
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
keys = append(keys, entries[i].k)
|
||||||
|
values = append(values, entries[i].v)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := VerifyRangeProof(trie.Hash(), keys, values, firstProof, lastProof)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Case %d(%d->%d) expect no error, got %v", i, start, end-1, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func randomTrie(n int) (*Trie, map[string]*kv) {
|
func randomTrie(n int) (*Trie, map[string]*kv) {
|
||||||
trie := new(Trie)
|
trie := new(Trie)
|
||||||
vals := make(map[string]*kv)
|
vals := make(map[string]*kv)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue