Merge branch 'master' into localstore

This commit is contained in:
Janos Guljas 2019-01-14 10:34:28 +01:00
commit c62fee36f9
54 changed files with 1563 additions and 899 deletions

23
.github/CODEOWNERS vendored
View file

@ -10,27 +10,4 @@ les/ @zsfelfoldi
light/ @zsfelfoldi light/ @zsfelfoldi
mobile/ @karalabe mobile/ @karalabe
p2p/ @fjl @zsfelfoldi p2p/ @fjl @zsfelfoldi
p2p/simulations @lmars
p2p/protocols @zelig
swarm/api/http @justelad
swarm/bmt @zelig
swarm/dev @lmars
swarm/fuse @jmozah @holisticode
swarm/grafana_dashboards @nonsense
swarm/metrics @nonsense @holisticode
swarm/multihash @nolash
swarm/network/bitvector @zelig @janos
swarm/network/priorityqueue @zelig @janos
swarm/network/simulations @zelig @janos
swarm/network/stream @janos @zelig @holisticode @justelad
swarm/network/stream/intervals @janos
swarm/network/stream/testing @zelig
swarm/pot @zelig
swarm/pss @nolash @zelig @nonsense
swarm/services @zelig
swarm/state @justelad
swarm/storage/encryption @zelig @nagydani
swarm/storage/mock @janos
swarm/storage/feed @nolash @jpeletier
swarm/testutil @lmars
whisper/ @gballet @gluk256 whisper/ @gballet @gluk256

View file

@ -58,13 +58,11 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return nil, err return nil, err
} }
return arguments, nil return arguments, nil
} }
method, exist := abi.Methods[name] method, exist := abi.Methods[name]
if !exist { if !exist {
return nil, fmt.Errorf("method '%s' not found", name) return nil, fmt.Errorf("method '%s' not found", name)
} }
arguments, err := method.Inputs.Pack(args...) arguments, err := method.Inputs.Pack(args...)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -22,11 +22,10 @@ import (
"fmt" "fmt"
"log" "log"
"math/big" "math/big"
"reflect"
"strings" "strings"
"testing" "testing"
"reflect"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
@ -52,11 +51,14 @@ const jsondata2 = `
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, { "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }, { "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] }, { "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] } { "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
{ "type" : "function", "name" : "nestedArray", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
{ "type" : "function", "name" : "nestedArray2", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
{ "type" : "function", "name" : "nestedSlice", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] }
]` ]`
func TestReader(t *testing.T) { func TestReader(t *testing.T) {
Uint256, _ := NewType("uint256") Uint256, _ := NewType("uint256", nil)
exp := ABI{ exp := ABI{
Methods: map[string]Method{ Methods: map[string]Method{
"balance": { "balance": {
@ -177,7 +179,7 @@ func TestTestSlice(t *testing.T) {
} }
func TestMethodSignature(t *testing.T) { func TestMethodSignature(t *testing.T) {
String, _ := NewType("string") String, _ := NewType("string", nil)
m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil} m := Method{"foo", 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 {
@ -189,12 +191,31 @@ func TestMethodSignature(t *testing.T) {
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") uintt, _ := NewType("uint256", nil)
m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil} m = Method{"foo", 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
s, _ := NewType("tuple", []ArgumentMarshaling{
{Name: "a", Type: "int256"},
{Name: "b", Type: "int256[]"},
{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
{Name: "x", Type: "int256"},
{Name: "y", Type: "int256"},
}},
{Name: "d", Type: "tuple[2]", Components: []ArgumentMarshaling{
{Name: "x", Type: "int256"},
{Name: "y", Type: "int256"},
}},
})
m = Method{"foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig())
}
} }
func TestMultiPack(t *testing.T) { func TestMultiPack(t *testing.T) {
@ -564,11 +585,13 @@ func TestBareEvents(t *testing.T) {
const definition = `[ const definition = `[
{ "type" : "event", "name" : "balance" }, { "type" : "event", "name" : "balance" },
{ "type" : "event", "name" : "anon", "anonymous" : true}, { "type" : "event", "name" : "anon", "anonymous" : true},
{ "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] } { "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] },
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
]` ]`
arg0, _ := NewType("uint256") arg0, _ := NewType("uint256", nil)
arg1, _ := NewType("address") arg1, _ := NewType("address", nil)
tuple, _ := NewType("tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
expectedEvents := map[string]struct { expectedEvents := map[string]struct {
Anonymous bool Anonymous bool
@ -580,6 +603,10 @@ func TestBareEvents(t *testing.T) {
{Name: "arg0", Type: arg0, Indexed: false}, {Name: "arg0", Type: arg0, Indexed: false},
{Name: "arg1", Type: arg1, Indexed: true}, {Name: "arg1", Type: arg1, Indexed: true},
}}, }},
"tuple": {false, []Argument{
{Name: "t", Type: tuple, Indexed: false},
{Name: "arg1", Type: arg1, Indexed: true},
}},
} }
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
@ -646,7 +673,7 @@ func TestUnpackEvent(t *testing.T) {
} }
type ReceivedEvent struct { type ReceivedEvent struct {
Address common.Address Sender common.Address
Amount *big.Int Amount *big.Int
Memo []byte Memo []byte
} }
@ -655,19 +682,15 @@ func TestUnpackEvent(t *testing.T) {
err = abi.Unpack(&ev, "received", data) err = abi.Unpack(&ev, "received", data)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else {
t.Logf("len(data): %d; received event: %+v", len(data), ev)
} }
type ReceivedAddrEvent struct { type ReceivedAddrEvent struct {
Address common.Address Sender common.Address
} }
var receivedAddrEv ReceivedAddrEvent var receivedAddrEv ReceivedAddrEvent
err = abi.Unpack(&receivedAddrEv, "receivedAddr", data) err = abi.Unpack(&receivedAddrEv, "receivedAddr", data)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else {
t.Logf("len(data): %d; received event: %+v", len(data), receivedAddrEv)
} }
} }

View file

@ -33,24 +33,27 @@ type Argument struct {
type Arguments []Argument type Arguments []Argument
// UnmarshalJSON implements json.Unmarshaler interface type ArgumentMarshaling struct {
func (argument *Argument) UnmarshalJSON(data []byte) error {
var extarg struct {
Name string Name string
Type string Type string
Components []ArgumentMarshaling
Indexed bool Indexed bool
} }
err := json.Unmarshal(data, &extarg)
// UnmarshalJSON implements json.Unmarshaler interface
func (argument *Argument) UnmarshalJSON(data []byte) error {
var arg ArgumentMarshaling
err := json.Unmarshal(data, &arg)
if err != nil { if err != nil {
return fmt.Errorf("argument json err: %v", err) return fmt.Errorf("argument json err: %v", err)
} }
argument.Type, err = NewType(extarg.Type) argument.Type, err = NewType(arg.Type, arg.Components)
if err != nil { if err != nil {
return err return err
} }
argument.Name = extarg.Name argument.Name = arg.Name
argument.Indexed = extarg.Indexed argument.Indexed = arg.Indexed
return nil return nil
} }
@ -85,7 +88,6 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format // Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error { func (arguments Arguments) Unpack(v interface{}, data []byte) error {
// 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() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v) return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
@ -97,52 +99,134 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
if arguments.isTuple() { if arguments.isTuple() {
return arguments.unpackTuple(v, marshalledValues) return arguments.unpackTuple(v, marshalledValues)
} }
return arguments.unpackAtomic(v, marshalledValues) return arguments.unpackAtomic(v, marshalledValues[0])
} }
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error { // unpack sets the unmarshalled value to go format.
// Note the dst here must be settable.
func unpack(t *Type, dst interface{}, src interface{}) error {
var (
dstVal = reflect.ValueOf(dst).Elem()
srcVal = reflect.ValueOf(src)
)
if t.T != TupleTy && !((t.T == SliceTy || t.T == ArrayTy) && t.Elem.T == TupleTy) {
return set(dstVal, srcVal)
}
switch t.T {
case TupleTy:
if dstVal.Kind() != reflect.Struct {
return fmt.Errorf("abi: invalid dst value for unpack, want struct, got %s", dstVal.Kind())
}
fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, dstVal)
if err != nil {
return err
}
for i, elem := range t.TupleElems {
fname := fieldmap[t.TupleRawNames[i]]
field := dstVal.FieldByName(fname)
if !field.IsValid() {
return fmt.Errorf("abi: field %s can't found in the given value", t.TupleRawNames[i])
}
if err := unpack(elem, field.Addr().Interface(), srcVal.Field(i).Interface()); err != nil {
return err
}
}
return nil
case SliceTy:
if dstVal.Kind() != reflect.Slice {
return fmt.Errorf("abi: invalid dst value for unpack, want slice, got %s", dstVal.Kind())
}
slice := reflect.MakeSlice(dstVal.Type(), srcVal.Len(), srcVal.Len())
for i := 0; i < slice.Len(); i++ {
if err := unpack(t.Elem, slice.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
return err
}
}
dstVal.Set(slice)
case ArrayTy:
if dstVal.Kind() != reflect.Array {
return fmt.Errorf("abi: invalid dst value for unpack, want array, got %s", dstVal.Kind())
}
array := reflect.New(dstVal.Type()).Elem()
for i := 0; i < array.Len(); i++ {
if err := unpack(t.Elem, array.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
return err
}
}
dstVal.Set(array)
}
return nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
if arguments.LengthNonIndexed() == 0 {
return nil
}
argument := arguments.NonIndexed()[0]
elem := reflect.ValueOf(v).Elem()
if elem.Kind() == reflect.Struct {
fieldmap, err := mapArgNamesToStructFields([]string{argument.Name}, elem)
if err != nil {
return err
}
field := elem.FieldByName(fieldmap[argument.Name])
if !field.IsValid() {
return fmt.Errorf("abi: field %s can't be found in the given value", argument.Name)
}
return unpack(&argument.Type, field.Addr().Interface(), marshalledValues)
}
return unpack(&argument.Type, elem.Addr().Interface(), marshalledValues)
}
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
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()
) )
if err := requireUnpackKind(value, typ, kind, arguments); err != nil { if err := requireUnpackKind(value, typ, kind, 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 err error var (
abi2struct, err = mapAbiToStructFields(arguments, value) argNames []string
err error
)
for _, arg := range arguments.NonIndexed() {
argNames = append(argNames, arg.Name)
}
abi2struct, err = mapArgNamesToStructFields(argNames, value)
if err != nil { if err != nil {
return err return err
} }
} }
for i, arg := range arguments.NonIndexed() { for i, arg := range arguments.NonIndexed() {
reflectValue := reflect.ValueOf(marshalledValues[i])
switch kind { switch kind {
case reflect.Struct: case reflect.Struct:
if structField, ok := abi2struct[arg.Name]; ok { field := value.FieldByName(abi2struct[arg.Name])
if err := set(value.FieldByName(structField), reflectValue, arg); err != nil { if !field.IsValid() {
return err return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
} }
if err := unpack(&arg.Type, field.Addr().Interface(), marshalledValues[i]); err != nil {
return err
} }
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
if value.Len() < i { if value.Len() < i {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len()) return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
} }
v := value.Index(i) v := value.Index(i)
if err := requireAssignable(v, reflectValue); err != nil { if err := requireAssignable(v, reflect.ValueOf(marshalledValues[i])); err != nil {
return err return err
} }
if err := unpack(&arg.Type, v.Addr().Interface(), marshalledValues[i]); err != nil {
if err := set(v.Elem(), reflectValue, arg); err != nil {
return err return err
} }
default: default:
@ -150,48 +234,7 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
} }
} }
return nil return nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
if len(marshalledValues) != 1 {
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
}
elem := reflect.ValueOf(v).Elem()
kind := elem.Kind()
reflectValue := reflect.ValueOf(marshalledValues[0])
var abi2struct map[string]string
if kind == reflect.Struct {
var err error
if abi2struct, err = mapAbiToStructFields(arguments, elem); err != nil {
return err
}
arg := arguments.NonIndexed()[0]
if structField, ok := abi2struct[arg.Name]; ok {
return set(elem.FieldByName(structField), reflectValue, arg)
}
return nil
}
return set(elem, reflectValue, arguments.NonIndexed()[0])
}
// Computes the full size of an array;
// i.e. counting nested arrays, which count towards size for unpacking.
func getArraySize(arr *Type) int {
size := arr.Size
// Arrays can be nested, with each element being the same size
arr = arr.Elem
for arr.T == ArrayTy {
// Keep multiplying by elem.Size while the elem is an array.
size *= arr.Size
arr = arr.Elem
}
// Now we have the full array size, including its children.
return size
} }
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification, // UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
@ -202,7 +245,7 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
virtualArgs := 0 virtualArgs := 0
for index, arg := range arguments.NonIndexed() { for index, arg := range arguments.NonIndexed() {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data) marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if arg.Type.T == ArrayTy && (*arg.Type.Elem).T != StringTy { if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
// If we have a static array, like [3]uint256, these are coded as // If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256. // just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when // This means that we need to add two 'virtual' arguments when
@ -213,7 +256,11 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
// //
// Calculate the full array size to get the correct offset for the next argument. // Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied. // Decrement it by 1, as the normal index increment is still applied.
virtualArgs += getArraySize(&arg.Type) - 1 virtualArgs += getTypeSize(arg.Type)/32 - 1
} else if arg.Type.T == TupleTy && !isDynamicType(arg.Type) {
// If we have a static tuple, like (uint256, bool, uint256), these are
// coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(arg.Type)/32 - 1
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -243,7 +290,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// input offset is the bytes offset for packed output // input offset is the bytes offset for packed output
inputOffset := 0 inputOffset := 0
for _, abiArg := range abiArgs { for _, abiArg := range abiArgs {
inputOffset += getDynamicTypeOffset(abiArg.Type) inputOffset += getTypeSize(abiArg.Type)
} }
var ret []byte var ret []byte
for i, a := range args { for i, a := range args {

View file

@ -30,302 +30,355 @@ import (
func TestPack(t *testing.T) { func TestPack(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
typ string typ string
components []ArgumentMarshaling
input interface{} input interface{}
output []byte output []byte
}{ }{
{ {
"uint8", "uint8",
nil,
uint8(2), uint8(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint8[]", "uint8[]",
nil,
[]uint8{1, 2}, []uint8{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint16", "uint16",
nil,
uint16(2), uint16(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint16[]", "uint16[]",
nil,
[]uint16{1, 2}, []uint16{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint32", "uint32",
nil,
uint32(2), uint32(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint32[]", "uint32[]",
nil,
[]uint32{1, 2}, []uint32{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint64", "uint64",
nil,
uint64(2), uint64(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint64[]", "uint64[]",
nil,
[]uint64{1, 2}, []uint64{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint256", "uint256",
nil,
big.NewInt(2), big.NewInt(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint256[]", "uint256[]",
nil,
[]*big.Int{big.NewInt(1), big.NewInt(2)}, []*big.Int{big.NewInt(1), big.NewInt(2)},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int8", "int8",
nil,
int8(2), int8(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int8[]", "int8[]",
nil,
[]int8{1, 2}, []int8{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int16", "int16",
nil,
int16(2), int16(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int16[]", "int16[]",
nil,
[]int16{1, 2}, []int16{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int32", "int32",
nil,
int32(2), int32(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int32[]", "int32[]",
nil,
[]int32{1, 2}, []int32{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int64", "int64",
nil,
int64(2), int64(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int64[]", "int64[]",
nil,
[]int64{1, 2}, []int64{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int256", "int256",
nil,
big.NewInt(2), big.NewInt(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int256[]", "int256[]",
nil,
[]*big.Int{big.NewInt(1), big.NewInt(2)}, []*big.Int{big.NewInt(1), big.NewInt(2)},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"bytes1", "bytes1",
nil,
[1]byte{1}, [1]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes2", "bytes2",
nil,
[2]byte{1}, [2]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes3", "bytes3",
nil,
[3]byte{1}, [3]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes4", "bytes4",
nil,
[4]byte{1}, [4]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes5", "bytes5",
nil,
[5]byte{1}, [5]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes6", "bytes6",
nil,
[6]byte{1}, [6]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes7", "bytes7",
nil,
[7]byte{1}, [7]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes8", "bytes8",
nil,
[8]byte{1}, [8]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes9", "bytes9",
nil,
[9]byte{1}, [9]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes10", "bytes10",
nil,
[10]byte{1}, [10]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes11", "bytes11",
nil,
[11]byte{1}, [11]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes12", "bytes12",
nil,
[12]byte{1}, [12]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes13", "bytes13",
nil,
[13]byte{1}, [13]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes14", "bytes14",
nil,
[14]byte{1}, [14]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes15", "bytes15",
nil,
[15]byte{1}, [15]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes16", "bytes16",
nil,
[16]byte{1}, [16]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes17", "bytes17",
nil,
[17]byte{1}, [17]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes18", "bytes18",
nil,
[18]byte{1}, [18]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes19", "bytes19",
nil,
[19]byte{1}, [19]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes20", "bytes20",
nil,
[20]byte{1}, [20]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes21", "bytes21",
nil,
[21]byte{1}, [21]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes22", "bytes22",
nil,
[22]byte{1}, [22]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes23", "bytes23",
nil,
[23]byte{1}, [23]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes24", "bytes24",
[24]byte{1}, nil,
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes24",
[24]byte{1}, [24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes25", "bytes25",
nil,
[25]byte{1}, [25]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes26", "bytes26",
nil,
[26]byte{1}, [26]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes27", "bytes27",
nil,
[27]byte{1}, [27]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes28", "bytes28",
nil,
[28]byte{1}, [28]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes29", "bytes29",
nil,
[29]byte{1}, [29]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes30", "bytes30",
nil,
[30]byte{1}, [30]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes31", "bytes31",
nil,
[31]byte{1}, [31]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes32", "bytes32",
nil,
[32]byte{1}, [32]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"uint32[2][3][4]", "uint32[2][3][4]",
nil,
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}}, [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
}, },
{ {
"address[]", "address[]",
nil,
[]common.Address{{1}, {2}}, []common.Address{{1}, {2}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
}, },
{ {
"bytes32[]", "bytes32[]",
nil,
[]common.Hash{{1}, {2}}, []common.Hash{{1}, {2}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"function", "function",
nil,
[24]byte{1}, [24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"string", "string",
nil,
"foobar", "foobar",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
}, },
{ {
"string[]", "string[]",
nil,
[]string{"hello", "foobar"}, []string{"hello", "foobar"},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2 common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0 "0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
@ -337,6 +390,7 @@ func TestPack(t *testing.T) {
}, },
{ {
"string[2]", "string[2]",
nil,
[]string{"hello", "foobar"}, []string{"hello", "foobar"},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0 common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1 "0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
@ -347,6 +401,7 @@ func TestPack(t *testing.T) {
}, },
{ {
"bytes32[][]", "bytes32[][]",
nil,
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}}, [][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2 common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0 "0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
@ -362,6 +417,7 @@ func TestPack(t *testing.T) {
{ {
"bytes32[][2]", "bytes32[][2]",
nil,
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}}, [][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0 common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1 "00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
@ -376,6 +432,7 @@ func TestPack(t *testing.T) {
{ {
"bytes32[3][2]", "bytes32[3][2]",
nil,
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}}, [][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0] common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1] "0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
@ -384,12 +441,182 @@ func TestPack(t *testing.T) {
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1] "0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2] "0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
}, },
{
// static tuple
"tuple",
[]ArgumentMarshaling{
{Name: "a", Type: "int64"},
{Name: "b", Type: "int256"},
{Name: "c", Type: "int256"},
{Name: "d", Type: "bool"},
{Name: "e", Type: "bytes32[3][2]"},
},
struct {
A int64
B *big.Int
C *big.Int
D bool
E [][]common.Hash
}{1, big.NewInt(1), big.NewInt(-1), true, [][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
"0500000000000000000000000000000000000000000000000000000000000000"), // struct[e] array[1][2]
},
{
// dynamic tuple
"tuple",
[]ArgumentMarshaling{
{Name: "a", Type: "string"},
{Name: "b", Type: "int64"},
{Name: "c", Type: "bytes"},
{Name: "d", Type: "string[]"},
{Name: "e", Type: "int256[]"},
{Name: "f", Type: "address[]"},
},
struct {
FieldA string `abi:"a"` // Test whether abi tag works
FieldB int64 `abi:"b"`
C []byte
D []string
E []*big.Int
F []common.Address
}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
"6261720000000000000000000000000000000000000000000000000000000000" + // bar
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
"0000000000000000000000000000000000000000000000000000000000000001" + // 1
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
"0000000000000000000000000200000000000000000000000000000000000000"), // common.Address{2}
},
{
// nested tuple
"tuple",
[]ArgumentMarshaling{
{Name: "a", Type: "tuple", Components: []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256[]"}}},
{Name: "b", Type: "int256[]"},
},
struct {
A struct {
FieldA *big.Int `abi:"a"`
B []*big.Int
}
B []*big.Int
}{
A: struct {
FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple
B []*big.Int
}{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
B: []*big.Int{big.NewInt(1), big.NewInt(0)}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // a offset
"00000000000000000000000000000000000000000000000000000000000000e0" + // b offset
"0000000000000000000000000000000000000000000000000000000000000001" + // a.a value
"0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b length
"0000000000000000000000000000000000000000000000000000000000000001" + // a.b[0] value
"0000000000000000000000000000000000000000000000000000000000000000" + // a.b[1] value
"0000000000000000000000000000000000000000000000000000000000000002" + // b length
"0000000000000000000000000000000000000000000000000000000000000001" + // b[0] value
"0000000000000000000000000000000000000000000000000000000000000000"), // b[1] value
},
{
// tuple slice
"tuple[]",
[]ArgumentMarshaling{
{Name: "a", Type: "int256"},
{Name: "b", Type: "int256[]"},
},
[]struct {
A *big.Int
B []*big.Int
}{
{big.NewInt(-1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
{big.NewInt(1), []*big.Int{big.NewInt(2), big.NewInt(-1)}},
},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // tuple length
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
"00000000000000000000000000000000000000000000000000000000000000e0" + // tuple[1] offset
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0].B offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].B length
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].B[0] value
"0000000000000000000000000000000000000000000000000000000000000000" + // tuple[0].B[1] value
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[1].B offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B length
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B[0] value
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].B[1] value
},
{
// static tuple array
"tuple[2]",
[]ArgumentMarshaling{
{Name: "a", Type: "int256"},
{Name: "b", Type: "int256"},
},
[2]struct {
A *big.Int
B *big.Int
}{
{big.NewInt(-1), big.NewInt(1)},
{big.NewInt(1), big.NewInt(-1)},
},
common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].a
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].b
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].a
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].b
},
{
// dynamic tuple array
"tuple[2]",
[]ArgumentMarshaling{
{Name: "a", Type: "int256[]"},
},
[2]struct {
A []*big.Int
}{
{[]*big.Int{big.NewInt(-1), big.NewInt(1)}},
{[]*big.Int{big.NewInt(1), big.NewInt(-1)}},
},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
"00000000000000000000000000000000000000000000000000000000000000c0" + // tuple[1] offset
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[0].A offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].A length
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A[0]
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].A[1]
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[1].A offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].A length
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A[0]
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].A[1]
},
} { } {
typ, err := NewType(test.typ) typ, err := NewType(test.typ, test.components)
if err != nil { if err != nil {
t.Fatalf("%v failed. Unexpected parse error: %v", i, err) t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
} }
output, err := typ.pack(reflect.ValueOf(test.input)) output, err := typ.pack(reflect.ValueOf(test.input))
if err != nil { if err != nil {
t.Fatalf("%v failed. Unexpected pack error: %v", i, err) t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
@ -466,6 +693,59 @@ func TestMethodPack(t *testing.T) {
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
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{2}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0xa0}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
sig = abi.Methods["nestedArray2"].Id()
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 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{1}, 32)...)
packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
sig = abi.Methods["nestedSlice"].Id()
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0xa0}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 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)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
} }
func TestPackNumber(t *testing.T) { func TestPackNumber(t *testing.T) {

View file

@ -71,18 +71,17 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
// //
// set is a bit more lenient when it comes to assignment and doesn't force an as // set is a bit more lenient when it comes to assignment and doesn't force an as
// strict ruleset as bare `reflect` does. // strict ruleset as bare `reflect` does.
func set(dst, src reflect.Value, output Argument) error { func set(dst, src reflect.Value) error {
dstType := dst.Type() dstType, srcType := dst.Type(), src.Type()
srcType := src.Type()
switch { switch {
case dstType.AssignableTo(srcType): case dstType.Kind() == reflect.Interface:
return set(dst.Elem(), src)
case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
return set(dst.Elem(), src)
case srcType.AssignableTo(dstType) && dst.CanSet():
dst.Set(src) dst.Set(src)
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice: case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
return setSlice(dst, src, output) return setSlice(dst, src)
case dstType.Kind() == reflect.Interface:
dst.Set(src)
case dstType.Kind() == reflect.Ptr:
return set(dst.Elem(), src, output)
default: default:
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type()) return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
} }
@ -91,7 +90,7 @@ func set(dst, src reflect.Value, output Argument) error {
// setSlice attempts to assign src to dst when slices are not assignable by default // setSlice attempts to assign src to dst when slices are not assignable by default
// e.g. src: [][]byte -> dst: [][15]byte // e.g. src: [][]byte -> dst: [][15]byte
func setSlice(dst, src reflect.Value, output Argument) error { func setSlice(dst, src reflect.Value) error {
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len()) slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
for i := 0; i < src.Len(); i++ { for i := 0; i < src.Len(); i++ {
v := src.Index(i) v := src.Index(i)
@ -127,14 +126,14 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
return nil return nil
} }
// mapAbiToStringField maps abi to struct fields. // mapArgNamesToStructFields maps a slice of argument names to struct fields.
// first round: for each Exportable field that contains a `abi:""` tag // first round: for each Exportable field that contains a `abi:""` tag
// and this field name exists in the arguments, pair them together. // and this field name exists in the given argument name list, pair them together.
// second round: for each argument field that has not been already linked, // second round: for each argument name that has not been already linked,
// find what variable is expected to be mapped into, if it exists and has not been // find what variable is expected to be mapped into, if it exists and has not been
// used, pair them. // used, pair them.
func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]string, error) { // Note this function assumes the given value is a struct value.
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
typ := value.Type() typ := value.Type()
abi2struct := make(map[string]string) abi2struct := make(map[string]string)
@ -148,45 +147,39 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) { if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
continue continue
} }
// skip fields that have no abi:"" tag. // skip fields that have no abi:"" tag.
var ok bool var ok bool
var tagName string var tagName string
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok { if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
continue continue
} }
// check if tag is empty. // check if tag is empty.
if tagName == "" { if tagName == "" {
return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName) return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
} }
// check which argument field matches with the abi tag. // check which argument field matches with the abi tag.
found := false found := false
for _, abiField := range args.NonIndexed() { for _, arg := range argNames {
if abiField.Name == tagName { if arg == tagName {
if abi2struct[abiField.Name] != "" { if abi2struct[arg] != "" {
return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName) return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
} }
// pair them // pair them
abi2struct[abiField.Name] = structFieldName abi2struct[arg] = structFieldName
struct2abi[structFieldName] = abiField.Name struct2abi[structFieldName] = arg
found = true found = true
} }
} }
// check if this tag has been mapped. // check if this tag has been mapped.
if !found { if !found {
return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName) return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
} }
} }
// second round ~~~ // second round ~~~
for _, arg := range args { for _, argName := range argNames {
abiFieldName := arg.Name structFieldName := ToCamelCase(argName)
structFieldName := ToCamelCase(abiFieldName)
if structFieldName == "" { if structFieldName == "" {
return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct") return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
@ -196,11 +189,11 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
// struct field with the same field name. If so, raise an error: // struct field with the same field name. If so, raise an error:
// abi: [ { "name": "value" } ] // abi: [ { "name": "value" } ]
// struct { Value *big.Int , Value1 *big.Int `abi:"value"`} // struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
if abi2struct[abiFieldName] != "" { if abi2struct[argName] != "" {
if abi2struct[abiFieldName] != structFieldName && if abi2struct[argName] != structFieldName &&
struct2abi[structFieldName] == "" && struct2abi[structFieldName] == "" &&
value.FieldByName(structFieldName).IsValid() { value.FieldByName(structFieldName).IsValid() {
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", abiFieldName) return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
} }
continue continue
} }
@ -212,16 +205,14 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
if value.FieldByName(structFieldName).IsValid() { if value.FieldByName(structFieldName).IsValid() {
// pair them // pair them
abi2struct[abiFieldName] = structFieldName abi2struct[argName] = structFieldName
struct2abi[structFieldName] = abiFieldName struct2abi[structFieldName] = argName
} else { } else {
// not paired, but annotate as used, to detect cases like // not paired, but annotate as used, to detect cases like
// abi : [ { "name": "value" }, { "name": "_value" } ] // abi : [ { "name": "value" }, { "name": "_value" } ]
// struct { Value *big.Int } // struct { Value *big.Int }
struct2abi[structFieldName] = abiFieldName struct2abi[structFieldName] = argName
} }
} }
return abi2struct, nil return abi2struct, nil
} }

View file

@ -17,6 +17,7 @@
package abi package abi
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
"regexp" "regexp"
@ -32,6 +33,7 @@ const (
StringTy StringTy
SliceTy SliceTy
ArrayTy ArrayTy
TupleTy
AddressTy AddressTy
FixedBytesTy FixedBytesTy
BytesTy BytesTy
@ -43,13 +45,16 @@ const (
// Type is the reflection of the supported argument type // Type is the reflection of the supported argument type
type Type struct { type Type struct {
Elem *Type Elem *Type
Kind reflect.Kind Kind reflect.Kind
Type reflect.Type Type reflect.Type
Size int Size int
T byte // Our own type checking T byte // Our own type checking
stringKind string // holds the unparsed string for deriving signatures stringKind string // holds the unparsed string for deriving signatures
// Tuple relative fields
TupleElems []*Type // Type information of all tuple fields
TupleRawNames []string // Raw field name of all tuple fields
} }
var ( var (
@ -58,7 +63,7 @@ var (
) )
// NewType creates a new reflection type of abi type given in t. // NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) { func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
// check that array brackets are equal if they exist // check that array brackets are equal if they exist
if strings.Count(t, "[") != strings.Count(t, "]") { if strings.Count(t, "[") != strings.Count(t, "]") {
return Type{}, fmt.Errorf("invalid arg type in abi") return Type{}, fmt.Errorf("invalid arg type in abi")
@ -71,7 +76,7 @@ func NewType(t string) (typ Type, err error) {
if strings.Count(t, "[") != 0 { if strings.Count(t, "[") != 0 {
i := strings.LastIndex(t, "[") i := strings.LastIndex(t, "[")
// recursively embed the type // recursively embed the type
embeddedType, err := NewType(t[:i]) embeddedType, err := NewType(t[:i], components)
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
@ -87,6 +92,9 @@ func NewType(t string) (typ Type, err error) {
typ.Kind = reflect.Slice typ.Kind = reflect.Slice
typ.Elem = &embeddedType typ.Elem = &embeddedType
typ.Type = reflect.SliceOf(embeddedType.Type) typ.Type = reflect.SliceOf(embeddedType.Type)
if embeddedType.T == TupleTy {
typ.stringKind = embeddedType.stringKind + sliced
}
} else if len(intz) == 1 { } else if len(intz) == 1 {
// is a array // is a array
typ.T = ArrayTy typ.T = ArrayTy
@ -97,6 +105,9 @@ func NewType(t string) (typ Type, err error) {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
} }
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type) typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
if embeddedType.T == TupleTy {
typ.stringKind = embeddedType.stringKind + sliced
}
} else { } else {
return Type{}, fmt.Errorf("invalid formatting of array type") return Type{}, fmt.Errorf("invalid formatting of array type")
} }
@ -158,6 +169,40 @@ func NewType(t string) (typ Type, err error) {
typ.Size = varSize typ.Size = varSize
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0))) typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
} }
case "tuple":
var (
fields []reflect.StructField
elems []*Type
names []string
expression string // canonical parameter expression
)
expression += "("
for idx, c := range components {
cType, err := NewType(c.Type, c.Components)
if err != nil {
return Type{}, err
}
if ToCamelCase(c.Name) == "" {
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
}
fields = append(fields, reflect.StructField{
Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
Type: cType.Type,
})
elems = append(elems, &cType)
names = append(names, c.Name)
expression += cType.stringKind
if idx != len(components)-1 {
expression += ","
}
}
expression += ")"
typ.Kind = reflect.Struct
typ.Type = reflect.StructOf(fields)
typ.TupleElems = elems
typ.TupleRawNames = names
typ.T = TupleTy
typ.stringKind = expression
case "function": case "function":
typ.Kind = reflect.Array typ.Kind = reflect.Array
typ.T = FunctionTy typ.T = FunctionTy
@ -178,7 +223,6 @@ func (t Type) String() (out string) {
func (t Type) pack(v reflect.Value) ([]byte, error) { func (t Type) pack(v reflect.Value) ([]byte, error) {
// dereference pointer first if it's a pointer // dereference pointer first if it's a pointer
v = indirect(v) v = indirect(v)
if err := typeCheck(t, v); err != nil { if err := typeCheck(t, v); err != nil {
return nil, err return nil, err
} }
@ -196,7 +240,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
offset := 0 offset := 0
offsetReq := isDynamicType(*t.Elem) offsetReq := isDynamicType(*t.Elem)
if offsetReq { if offsetReq {
offset = getDynamicTypeOffset(*t.Elem) * v.Len() offset = getTypeSize(*t.Elem) * v.Len()
} }
var tail []byte var tail []byte
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
@ -213,6 +257,45 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
tail = append(tail, val...) tail = append(tail, val...)
} }
return append(ret, tail...), nil return append(ret, tail...), nil
case TupleTy:
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
// enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
// where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
// type as
// head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
// and as
// head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
// tail(X(i)) = enc(X(i))
// otherwise, i.e. if Ti is a dynamic type.
fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v)
if err != nil {
return nil, err
}
// Calculate prefix occupied size.
offset := 0
for _, elem := range t.TupleElems {
offset += getTypeSize(*elem)
}
var ret, tail []byte
for i, elem := range t.TupleElems {
field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
if !field.IsValid() {
return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
}
val, err := elem.pack(field)
if err != nil {
return nil, err
}
if isDynamicType(*elem) {
ret = append(ret, packNum(reflect.ValueOf(offset))...)
tail = append(tail, val...)
offset += len(val)
} else {
ret = append(ret, val...)
}
}
return append(ret, tail...), nil
default: default:
return packElement(t, v), nil return packElement(t, v), nil
} }
@ -225,25 +308,45 @@ func (t Type) requiresLengthPrefix() bool {
} }
// isDynamicType returns true if the type is dynamic. // isDynamicType returns true if the type is dynamic.
// StringTy, BytesTy, and SliceTy(irrespective of slice element type) are dynamic types // The following types are called “dynamic”:
// ArrayTy is considered dynamic if and only if the Array element is a dynamic type. // * bytes
// This function recursively checks the type for slice and array elements. // * string
// * T[] for any T
// * T[k] for any dynamic T and any k >= 0
// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
func isDynamicType(t Type) bool { func isDynamicType(t Type) bool {
// dynamic types if t.T == TupleTy {
// array is also a dynamic type if the array type is dynamic for _, elem := range t.TupleElems {
if isDynamicType(*elem) {
return true
}
}
return false
}
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem)) return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
} }
// getDynamicTypeOffset returns the offset for the type. // getTypeSize returns the size that this type needs to occupy.
// See `isDynamicType` to know which types are considered dynamic. // We distinguish static and dynamic types. Static types are encoded in-place
// If the type t is an array and element type is not a dynamic type, then we consider it a static type and // and dynamic types are encoded at a separately allocated location after the
// return 32 * size of array since length prefix is not required. // current block.
// If t is a dynamic type or element type(for slices and arrays) is dynamic, then we simply return 32 as offset. // So for a static variable, the size returned represents the size that the
func getDynamicTypeOffset(t Type) int { // variable actually occupies.
// if it is an array and there are no dynamic types // For a dynamic variable, the returned size is fixed 32 bytes, which is used
// then the array is static type // to store the location reference for actual value storage.
func getTypeSize(t Type) int {
if t.T == ArrayTy && !isDynamicType(*t.Elem) { if t.T == ArrayTy && !isDynamicType(*t.Elem) {
return 32 * t.Size // Recursively calculate type size if it is a nested array
if t.Elem.T == ArrayTy {
return t.Size * getTypeSize(*t.Elem)
}
return t.Size * 32
} else if t.T == TupleTy && !isDynamicType(t) {
total := 0
for _, elem := range t.TupleElems {
total += getTypeSize(*elem)
}
return total
} }
return 32 return 32
} }

View file

@ -33,71 +33,74 @@ type typeWithoutStringer Type
func TestTypeRegexp(t *testing.T) { func TestTypeRegexp(t *testing.T) {
tests := []struct { tests := []struct {
blob string blob string
components []ArgumentMarshaling
kind Type kind Type
}{ }{
{"bool", Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}}, {"bool", nil, Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
{"bool[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}}, {"bool[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
{"bool[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}}, {"bool[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
{"bool[2][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}}, {"bool[2][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
{"bool[][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}}, {"bool[][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
{"bool[][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}}, {"bool[][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
{"bool[2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}}, {"bool[2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
{"bool[2][][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}}, {"bool[2][][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
{"bool[2][2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}}, {"bool[2][2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
{"bool[][][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}}, {"bool[][][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
{"bool[][2][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}}, {"bool[][2][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
{"int8", Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}}, {"int8", nil, Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
{"int16", Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}}, {"int16", nil, Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
{"int32", Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}}, {"int32", nil, Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
{"int64", Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}}, {"int64", nil, Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
{"int256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}}, {"int256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
{"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}}, {"int8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
{"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}}, {"int8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
{"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}}, {"int16[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
{"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}}, {"int16[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
{"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}}, {"int32[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
{"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}}, {"int32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
{"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}}, {"int64[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
{"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}}, {"int64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
{"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}}, {"int256[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
{"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}}, {"int256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
{"uint8", Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}}, {"uint8", nil, Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
{"uint16", Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}}, {"uint16", nil, Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
{"uint32", Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}}, {"uint32", nil, Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
{"uint64", Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}}, {"uint64", nil, Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
{"uint256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}}, {"uint256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
{"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}}, {"uint8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
{"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}}, {"uint8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
{"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}}, {"uint16[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
{"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}}, {"uint16[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
{"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}}, {"uint32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
{"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}}, {"uint32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
{"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}}, {"uint64[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
{"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}}, {"uint64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
{"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}}, {"uint256[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
{"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}}, {"uint256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
{"bytes32", Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}}, {"bytes32", nil, Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
{"bytes[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}}, {"bytes[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
{"bytes[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}}, {"bytes[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
{"bytes32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}}, {"bytes32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
{"bytes32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}}, {"bytes32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
{"string", Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}}, {"string", nil, Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
{"string[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}}, {"string[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
{"string[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}}, {"string[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
{"address", Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}}, {"address", nil, Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
{"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}}, {"address[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
{"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}}, {"address[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
// TODO when fixed types are implemented properly // TODO when fixed types are implemented properly
// {"fixed", Type{}}, // {"fixed", nil, Type{}},
// {"fixed128x128", Type{}}, // {"fixed128x128", nil, Type{}},
// {"fixed[]", Type{}}, // {"fixed[]", nil, Type{}},
// {"fixed[2]", Type{}}, // {"fixed[2]", nil, Type{}},
// {"fixed128x128[]", Type{}}, // {"fixed128x128[]", nil, Type{}},
// {"fixed128x128[2]", Type{}}, // {"fixed128x128[2]", nil, Type{}},
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct{ A int64 }{}), stringKind: "(int64)",
TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
} }
for _, tt := range tests { for _, tt := range tests {
typ, err := NewType(tt.blob) typ, err := NewType(tt.blob, tt.components)
if err != nil { if err != nil {
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err) t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
} }
@ -110,153 +113,169 @@ func TestTypeRegexp(t *testing.T) {
func TestTypeCheck(t *testing.T) { func TestTypeCheck(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
typ string typ string
components []ArgumentMarshaling
input interface{} input interface{}
err string err string
}{ }{
{"uint", big.NewInt(1), "unsupported arg type: uint"}, {"uint", nil, big.NewInt(1), "unsupported arg type: uint"},
{"int", big.NewInt(1), "unsupported arg type: int"}, {"int", nil, big.NewInt(1), "unsupported arg type: int"},
{"uint256", big.NewInt(1), ""}, {"uint256", nil, big.NewInt(1), ""},
{"uint256[][3][]", [][3][]*big.Int{{{}}}, ""}, {"uint256[][3][]", nil, [][3][]*big.Int{{{}}}, ""},
{"uint256[][][3]", [3][][]*big.Int{{{}}}, ""}, {"uint256[][][3]", nil, [3][][]*big.Int{{{}}}, ""},
{"uint256[3][][]", [][][3]*big.Int{{{}}}, ""}, {"uint256[3][][]", nil, [][][3]*big.Int{{{}}}, ""},
{"uint256[3][3][3]", [3][3][3]*big.Int{{{}}}, ""}, {"uint256[3][3][3]", nil, [3][3][3]*big.Int{{{}}}, ""},
{"uint8[][]", [][]uint8{}, ""}, {"uint8[][]", nil, [][]uint8{}, ""},
{"int256", big.NewInt(1), ""}, {"int256", nil, big.NewInt(1), ""},
{"uint8", uint8(1), ""}, {"uint8", nil, uint8(1), ""},
{"uint16", uint16(1), ""}, {"uint16", nil, uint16(1), ""},
{"uint32", uint32(1), ""}, {"uint32", nil, uint32(1), ""},
{"uint64", uint64(1), ""}, {"uint64", nil, uint64(1), ""},
{"int8", int8(1), ""}, {"int8", nil, int8(1), ""},
{"int16", int16(1), ""}, {"int16", nil, int16(1), ""},
{"int32", int32(1), ""}, {"int32", nil, int32(1), ""},
{"int64", int64(1), ""}, {"int64", nil, int64(1), ""},
{"uint24", big.NewInt(1), ""}, {"uint24", nil, big.NewInt(1), ""},
{"uint40", big.NewInt(1), ""}, {"uint40", nil, big.NewInt(1), ""},
{"uint48", big.NewInt(1), ""}, {"uint48", nil, big.NewInt(1), ""},
{"uint56", big.NewInt(1), ""}, {"uint56", nil, big.NewInt(1), ""},
{"uint72", big.NewInt(1), ""}, {"uint72", nil, big.NewInt(1), ""},
{"uint80", big.NewInt(1), ""}, {"uint80", nil, big.NewInt(1), ""},
{"uint88", big.NewInt(1), ""}, {"uint88", nil, big.NewInt(1), ""},
{"uint96", big.NewInt(1), ""}, {"uint96", nil, big.NewInt(1), ""},
{"uint104", big.NewInt(1), ""}, {"uint104", nil, big.NewInt(1), ""},
{"uint112", big.NewInt(1), ""}, {"uint112", nil, big.NewInt(1), ""},
{"uint120", big.NewInt(1), ""}, {"uint120", nil, big.NewInt(1), ""},
{"uint128", big.NewInt(1), ""}, {"uint128", nil, big.NewInt(1), ""},
{"uint136", big.NewInt(1), ""}, {"uint136", nil, big.NewInt(1), ""},
{"uint144", big.NewInt(1), ""}, {"uint144", nil, big.NewInt(1), ""},
{"uint152", big.NewInt(1), ""}, {"uint152", nil, big.NewInt(1), ""},
{"uint160", big.NewInt(1), ""}, {"uint160", nil, big.NewInt(1), ""},
{"uint168", big.NewInt(1), ""}, {"uint168", nil, big.NewInt(1), ""},
{"uint176", big.NewInt(1), ""}, {"uint176", nil, big.NewInt(1), ""},
{"uint184", big.NewInt(1), ""}, {"uint184", nil, big.NewInt(1), ""},
{"uint192", big.NewInt(1), ""}, {"uint192", nil, big.NewInt(1), ""},
{"uint200", big.NewInt(1), ""}, {"uint200", nil, big.NewInt(1), ""},
{"uint208", big.NewInt(1), ""}, {"uint208", nil, big.NewInt(1), ""},
{"uint216", big.NewInt(1), ""}, {"uint216", nil, big.NewInt(1), ""},
{"uint224", big.NewInt(1), ""}, {"uint224", nil, big.NewInt(1), ""},
{"uint232", big.NewInt(1), ""}, {"uint232", nil, big.NewInt(1), ""},
{"uint240", big.NewInt(1), ""}, {"uint240", nil, big.NewInt(1), ""},
{"uint248", big.NewInt(1), ""}, {"uint248", nil, big.NewInt(1), ""},
{"int24", big.NewInt(1), ""}, {"int24", nil, big.NewInt(1), ""},
{"int40", big.NewInt(1), ""}, {"int40", nil, big.NewInt(1), ""},
{"int48", big.NewInt(1), ""}, {"int48", nil, big.NewInt(1), ""},
{"int56", big.NewInt(1), ""}, {"int56", nil, big.NewInt(1), ""},
{"int72", big.NewInt(1), ""}, {"int72", nil, big.NewInt(1), ""},
{"int80", big.NewInt(1), ""}, {"int80", nil, big.NewInt(1), ""},
{"int88", big.NewInt(1), ""}, {"int88", nil, big.NewInt(1), ""},
{"int96", big.NewInt(1), ""}, {"int96", nil, big.NewInt(1), ""},
{"int104", big.NewInt(1), ""}, {"int104", nil, big.NewInt(1), ""},
{"int112", big.NewInt(1), ""}, {"int112", nil, big.NewInt(1), ""},
{"int120", big.NewInt(1), ""}, {"int120", nil, big.NewInt(1), ""},
{"int128", big.NewInt(1), ""}, {"int128", nil, big.NewInt(1), ""},
{"int136", big.NewInt(1), ""}, {"int136", nil, big.NewInt(1), ""},
{"int144", big.NewInt(1), ""}, {"int144", nil, big.NewInt(1), ""},
{"int152", big.NewInt(1), ""}, {"int152", nil, big.NewInt(1), ""},
{"int160", big.NewInt(1), ""}, {"int160", nil, big.NewInt(1), ""},
{"int168", big.NewInt(1), ""}, {"int168", nil, big.NewInt(1), ""},
{"int176", big.NewInt(1), ""}, {"int176", nil, big.NewInt(1), ""},
{"int184", big.NewInt(1), ""}, {"int184", nil, big.NewInt(1), ""},
{"int192", big.NewInt(1), ""}, {"int192", nil, big.NewInt(1), ""},
{"int200", big.NewInt(1), ""}, {"int200", nil, big.NewInt(1), ""},
{"int208", big.NewInt(1), ""}, {"int208", nil, big.NewInt(1), ""},
{"int216", big.NewInt(1), ""}, {"int216", nil, big.NewInt(1), ""},
{"int224", big.NewInt(1), ""}, {"int224", nil, big.NewInt(1), ""},
{"int232", big.NewInt(1), ""}, {"int232", nil, big.NewInt(1), ""},
{"int240", big.NewInt(1), ""}, {"int240", nil, big.NewInt(1), ""},
{"int248", big.NewInt(1), ""}, {"int248", nil, big.NewInt(1), ""},
{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"}, {"uint30", nil, uint8(1), "abi: cannot use uint8 as type ptr as argument"},
{"uint8", uint16(1), "abi: cannot use uint16 as type uint8 as argument"}, {"uint8", nil, uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
{"uint8", uint32(1), "abi: cannot use uint32 as type uint8 as argument"}, {"uint8", nil, uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
{"uint8", uint64(1), "abi: cannot use uint64 as type uint8 as argument"}, {"uint8", nil, uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
{"uint8", int8(1), "abi: cannot use int8 as type uint8 as argument"}, {"uint8", nil, int8(1), "abi: cannot use int8 as type uint8 as argument"},
{"uint8", int16(1), "abi: cannot use int16 as type uint8 as argument"}, {"uint8", nil, int16(1), "abi: cannot use int16 as type uint8 as argument"},
{"uint8", int32(1), "abi: cannot use int32 as type uint8 as argument"}, {"uint8", nil, int32(1), "abi: cannot use int32 as type uint8 as argument"},
{"uint8", int64(1), "abi: cannot use int64 as type uint8 as argument"}, {"uint8", nil, int64(1), "abi: cannot use int64 as type uint8 as argument"},
{"uint16", uint16(1), ""}, {"uint16", nil, uint16(1), ""},
{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"}, {"uint16", nil, uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
{"uint16[]", []uint16{1, 2, 3}, ""}, {"uint16[]", nil, []uint16{1, 2, 3}, ""},
{"uint16[]", [3]uint16{1, 2, 3}, ""}, {"uint16[]", nil, [3]uint16{1, 2, 3}, ""},
{"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"}, {"uint16[]", nil, []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"},
{"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"}, {"uint16[3]", nil, [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
{"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"}, {"uint16[3]", nil, [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"uint16[3]", []uint16{1, 2, 3}, ""}, {"uint16[3]", nil, []uint16{1, 2, 3}, ""},
{"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"}, {"uint16[3]", nil, []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"address[]", []common.Address{{1}}, ""}, {"address[]", nil, []common.Address{{1}}, ""},
{"address[1]", []common.Address{{1}}, ""}, {"address[1]", nil, []common.Address{{1}}, ""},
{"address[1]", [1]common.Address{{1}}, ""}, {"address[1]", nil, [1]common.Address{{1}}, ""},
{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"}, {"address[2]", nil, [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
{"bytes32", [32]byte{}, ""}, {"bytes32", nil, [32]byte{}, ""},
{"bytes31", [31]byte{}, ""}, {"bytes31", nil, [31]byte{}, ""},
{"bytes30", [30]byte{}, ""}, {"bytes30", nil, [30]byte{}, ""},
{"bytes29", [29]byte{}, ""}, {"bytes29", nil, [29]byte{}, ""},
{"bytes28", [28]byte{}, ""}, {"bytes28", nil, [28]byte{}, ""},
{"bytes27", [27]byte{}, ""}, {"bytes27", nil, [27]byte{}, ""},
{"bytes26", [26]byte{}, ""}, {"bytes26", nil, [26]byte{}, ""},
{"bytes25", [25]byte{}, ""}, {"bytes25", nil, [25]byte{}, ""},
{"bytes24", [24]byte{}, ""}, {"bytes24", nil, [24]byte{}, ""},
{"bytes23", [23]byte{}, ""}, {"bytes23", nil, [23]byte{}, ""},
{"bytes22", [22]byte{}, ""}, {"bytes22", nil, [22]byte{}, ""},
{"bytes21", [21]byte{}, ""}, {"bytes21", nil, [21]byte{}, ""},
{"bytes20", [20]byte{}, ""}, {"bytes20", nil, [20]byte{}, ""},
{"bytes19", [19]byte{}, ""}, {"bytes19", nil, [19]byte{}, ""},
{"bytes18", [18]byte{}, ""}, {"bytes18", nil, [18]byte{}, ""},
{"bytes17", [17]byte{}, ""}, {"bytes17", nil, [17]byte{}, ""},
{"bytes16", [16]byte{}, ""}, {"bytes16", nil, [16]byte{}, ""},
{"bytes15", [15]byte{}, ""}, {"bytes15", nil, [15]byte{}, ""},
{"bytes14", [14]byte{}, ""}, {"bytes14", nil, [14]byte{}, ""},
{"bytes13", [13]byte{}, ""}, {"bytes13", nil, [13]byte{}, ""},
{"bytes12", [12]byte{}, ""}, {"bytes12", nil, [12]byte{}, ""},
{"bytes11", [11]byte{}, ""}, {"bytes11", nil, [11]byte{}, ""},
{"bytes10", [10]byte{}, ""}, {"bytes10", nil, [10]byte{}, ""},
{"bytes9", [9]byte{}, ""}, {"bytes9", nil, [9]byte{}, ""},
{"bytes8", [8]byte{}, ""}, {"bytes8", nil, [8]byte{}, ""},
{"bytes7", [7]byte{}, ""}, {"bytes7", nil, [7]byte{}, ""},
{"bytes6", [6]byte{}, ""}, {"bytes6", nil, [6]byte{}, ""},
{"bytes5", [5]byte{}, ""}, {"bytes5", nil, [5]byte{}, ""},
{"bytes4", [4]byte{}, ""}, {"bytes4", nil, [4]byte{}, ""},
{"bytes3", [3]byte{}, ""}, {"bytes3", nil, [3]byte{}, ""},
{"bytes2", [2]byte{}, ""}, {"bytes2", nil, [2]byte{}, ""},
{"bytes1", [1]byte{}, ""}, {"bytes1", nil, [1]byte{}, ""},
{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"}, {"bytes32", nil, [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
{"bytes32", common.Hash{1}, ""}, {"bytes32", nil, common.Hash{1}, ""},
{"bytes31", common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"}, {"bytes31", nil, common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"},
{"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"}, {"bytes31", nil, [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
{"bytes", []byte{0, 1}, ""}, {"bytes", nil, []byte{0, 1}, ""},
{"bytes", [2]byte{0, 1}, "abi: cannot use array as type slice as argument"}, {"bytes", nil, [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
{"bytes", common.Hash{1}, "abi: cannot use array as type slice as argument"}, {"bytes", nil, common.Hash{1}, "abi: cannot use array as type slice as argument"},
{"string", "hello world", ""}, {"string", nil, "hello world", ""},
{"string", string(""), ""}, {"string", nil, string(""), ""},
{"string", []byte{}, "abi: cannot use slice as type string as argument"}, {"string", nil, []byte{}, "abi: cannot use slice as type string as argument"},
{"bytes32[]", [][32]byte{{}}, ""}, {"bytes32[]", nil, [][32]byte{{}}, ""},
{"function", [24]byte{}, ""}, {"function", nil, [24]byte{}, ""},
{"bytes20", common.Address{}, ""}, {"bytes20", nil, common.Address{}, ""},
{"address", [20]byte{}, ""}, {"address", nil, [20]byte{}, ""},
{"address", common.Address{}, ""}, {"address", nil, common.Address{}, ""},
{"bytes32[]]", "", "invalid arg type in abi"}, {"bytes32[]]", nil, "", "invalid arg type in abi"},
{"invalidType", "", "unsupported arg type: invalidType"}, {"invalidType", nil, "", "unsupported arg type: invalidType"},
{"invalidSlice[]", "", "unsupported arg type: invalidSlice"}, {"invalidSlice[]", nil, "", "unsupported arg type: invalidSlice"},
// simple tuple
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, struct {
A *big.Int
B *big.Int
}{}, ""},
// tuple slice
{"tuple[]", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, []struct {
A *big.Int
B *big.Int
}{}, ""},
// tuple array
{"tuple[2]", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, []struct {
A *big.Int
B *big.Int
}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
} { } {
typ, err := NewType(test.typ) typ, err := NewType(test.typ, test.components)
if err != nil && len(test.err) == 0 { if err != nil && len(test.err) == 0 {
t.Fatal("unexpected parse error:", err) t.Fatal("unexpected parse error:", err)
} else if err != nil && len(test.err) != 0 { } else if err != nil && len(test.err) != 0 {

View file

@ -115,17 +115,6 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
} }
func getFullElemSize(elem *Type) int {
//all other should be counted as 32 (slices have pointers to respective elements)
size := 32
//arrays wrap it, each element being the same size
for elem.T == ArrayTy {
size *= elem.Size
elem = elem.Elem
}
return size
}
// iteratively unpack elements // iteratively unpack elements
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) { func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
if size < 0 { if size < 0 {
@ -150,13 +139,9 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
// Arrays have packed elements, resulting in longer unpack steps. // Arrays have packed elements, resulting in longer unpack steps.
// Slices have just 32 bytes per element (pointing to the contents). // Slices have just 32 bytes per element (pointing to the contents).
elemSize := 32 elemSize := getTypeSize(*t.Elem)
if t.T == ArrayTy || t.T == SliceTy {
elemSize = getFullElemSize(t.Elem)
}
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 { for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
inter, err := toGoType(i, *t.Elem, output) inter, err := toGoType(i, *t.Elem, output)
if err != nil { if err != nil {
return nil, err return nil, err
@ -170,6 +155,36 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return refSlice.Interface(), nil return refSlice.Interface(), nil
} }
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
retval := reflect.New(t.Type).Elem()
virtualArgs := 0
for index, elem := range t.TupleElems {
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
if elem.T == ArrayTy && !isDynamicType(*elem) {
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
// we count the index from now on.
//
// Array values nested multiple levels deep are also encoded inline:
// [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
//
// Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied.
virtualArgs += getTypeSize(*elem)/32 - 1
} else if elem.T == TupleTy && !isDynamicType(*elem) {
// If we have a static tuple, like (uint256, bool, uint256), these are
// coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(*elem)/32 - 1
}
if err != nil {
return nil, err
}
retval.Field(index).Set(reflect.ValueOf(marshalledValue))
}
return retval.Interface(), nil
}
// toGoType parses the output bytes and recursively assigns the value of these bytes // toGoType parses the output bytes and recursively assigns the value of these bytes
// into a go type with accordance with the ABI spec. // into a go type with accordance with the ABI spec.
func toGoType(index int, t Type, output []byte) (interface{}, error) { func toGoType(index int, t Type, output []byte) (interface{}, error) {
@ -179,13 +194,13 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
var ( var (
returnOutput []byte returnOutput []byte
begin, end int begin, length int
err error err error
) )
// if we require a length prefix, find the beginning word and size returned. // if we require a length prefix, find the beginning word and size returned.
if t.requiresLengthPrefix() { if t.requiresLengthPrefix() {
begin, end, err = lengthPrefixPointsTo(index, output) begin, length, err = lengthPrefixPointsTo(index, output)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -194,19 +209,26 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
} }
switch t.T { switch t.T {
case SliceTy: case TupleTy:
if (*t.Elem).T == StringTy { if isDynamicType(t) {
return forEachUnpack(t, output[begin:], 0, end) begin, err := tuplePointsTo(index, output)
if err != nil {
return nil, err
} }
return forEachUnpack(t, output, begin, end) return forTupleUnpack(t, output[begin:])
} else {
return forTupleUnpack(t, output[index:])
}
case SliceTy:
return forEachUnpack(t, output[begin:], 0, length)
case ArrayTy: case ArrayTy:
if (*t.Elem).T == StringTy { if isDynamicType(*t.Elem) {
offset := int64(binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:])) offset := int64(binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:]))
return forEachUnpack(t, output[offset:], 0, t.Size) return forEachUnpack(t, output[offset:], 0, t.Size)
} }
return forEachUnpack(t, output, index, t.Size) return forEachUnpack(t, output[index:], 0, t.Size)
case StringTy: // variable arrays are written at the end of the return bytes case StringTy: // variable arrays are written at the end of the return bytes
return string(output[begin : begin+end]), nil return string(output[begin : begin+length]), nil
case IntTy, UintTy: case IntTy, UintTy:
return readInteger(t.T, t.Kind, returnOutput), nil return readInteger(t.T, t.Kind, returnOutput), nil
case BoolTy: case BoolTy:
@ -216,7 +238,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
case HashTy: case HashTy:
return common.BytesToHash(returnOutput), nil return common.BytesToHash(returnOutput), nil
case BytesTy: case BytesTy:
return output[begin : begin+end], nil return output[begin : begin+length], nil
case FixedBytesTy: case FixedBytesTy:
return readFixedBytes(t, returnOutput) return readFixedBytes(t, returnOutput)
case FunctionTy: case FunctionTy:
@ -257,3 +279,17 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
length = int(lengthBig.Uint64()) length = int(lengthBig.Uint64())
return return
} }
// tuplePointsTo resolves the location reference for dynamic tuple.
func tuplePointsTo(index int, output []byte) (start int, err error) {
offset := big.NewInt(0).SetBytes(output[index : index+32])
outputLen := big.NewInt(int64(len(output)))
if offset.Cmp(big.NewInt(int64(len(output)))) > 0 {
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
}
if offset.BitLen() > 63 {
return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
}
return int(offset.Uint64()), nil
}

View file

@ -173,9 +173,14 @@ var unpackTests = []unpackTest{
// multi dimensional, if these pass, all types that don't require length prefix should pass // multi dimensional, if these pass, all types that don't require length prefix should pass
{ {
def: `[{"type": "uint8[][]"}]`, def: `[{"type": "uint8[][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [][]uint8{{1, 2}, {1, 2}}, want: [][]uint8{{1, 2}, {1, 2}},
}, },
{
def: `[{"type": "uint8[][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
want: [][]uint8{{1, 2}, {1, 2, 3}},
},
{ {
def: `[{"type": "uint8[2][2]"}]`, def: `[{"type": "uint8[2][2]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -183,7 +188,7 @@ var unpackTests = []unpackTest{
}, },
{ {
def: `[{"type": "uint8[][2]"}]`, def: `[{"type": "uint8[][2]"}]`,
enc: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
want: [2][]uint8{{1}, {1}}, want: [2][]uint8{{1}, {1}},
}, },
{ {
@ -251,6 +256,16 @@ var unpackTests = []unpackTest{
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000", enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000",
want: []string{"Ethereum", "go-ethereum"}, want: []string{"Ethereum", "go-ethereum"},
}, },
{
def: `[{"type": "bytes[]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003f0f0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f0f0f00000000000000000000000000000000000000000000000000000000000",
want: [][]byte{{0xf0, 0xf0, 0xf0}, {0xf0, 0xf0, 0xf0}},
},
{
def: `[{"type": "uint256[2][][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e8",
want: [][][2]*big.Int{{{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}, {{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}},
},
{ {
def: `[{"type": "int8[]"}]`, def: `[{"type": "int8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -610,7 +625,18 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008657468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000065")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[0] length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0][0] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // output[0][1] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008")) // output[0][0] length
buff.Write(common.Hex2Bytes("657468657265756d000000000000000000000000000000000000000000000000")) // output[0][0] value
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000b")) // output[0][1] length
buff.Write(common.Hex2Bytes("676f2d657468657265756d000000000000000000000000000000000000000000")) // output[0][1] value
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"} ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)} ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
@ -913,6 +939,108 @@ func TestUnmarshal(t *testing.T) {
} }
} }
func TestUnpackTuple(t *testing.T) {
const simpleTuple = `[{"name":"tuple","constant":false,"outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
abi, err := JSON(strings.NewReader(simpleTuple))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
v := struct {
Ret struct {
A *big.Int
B *big.Int
}
}{Ret: struct {
A *big.Int
B *big.Int
}{new(big.Int), new(big.Int)}}
err = abi.Unpack(&v, "tuple", buff.Bytes())
if err != nil {
t.Error(err)
} else {
if v.Ret.A.Cmp(big.NewInt(1)) != 0 {
t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.Ret.A)
}
if v.Ret.B.Cmp(big.NewInt(-1)) != 0 {
t.Errorf("unexpected value unpacked: want %x, got %x", v.Ret.B, -1)
}
}
// Test nested tuple
const nestedTuple = `[{"name":"tuple","constant":false,"outputs":[
{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
{"type":"uint256","name":"a"}
]}]`
abi, err = JSON(strings.NewReader(nestedTuple))
if err != nil {
t.Fatal(err)
}
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // t.Y = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // a = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.A = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000060")) // s.B offset
buff.Write(common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0")) // s.C offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.B length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.B[0] = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.B[0] = 2
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.C[0].X
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C[0].Y
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C[1].X
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.C[1].Y
type T struct {
X *big.Int `abi:"x"`
Z *big.Int `abi:"y"` // Test whether the abi tag works.
}
type S struct {
A *big.Int
B []*big.Int
C []T
}
type Ret struct {
FieldS S `abi:"s"`
FieldT T `abi:"t"`
A *big.Int
}
var ret Ret
var expected = Ret{
FieldS: S{
A: big.NewInt(1),
B: []*big.Int{big.NewInt(1), big.NewInt(2)},
C: []T{
{big.NewInt(1), big.NewInt(2)},
{big.NewInt(2), big.NewInt(1)},
},
},
FieldT: T{
big.NewInt(0), big.NewInt(1),
},
A: big.NewInt(1),
}
err = abi.Unpack(&ret, "tuple", buff.Bytes())
if err != nil {
t.Error(err)
}
if reflect.DeepEqual(ret, expected) {
t.Error("unexpected unpack value")
}
}
func TestOOMMaliciousInput(t *testing.T) { func TestOOMMaliciousInput(t *testing.T) {
oomTests := []unpackTest{ oomTests := []unpackTest{
{ {

View file

@ -84,10 +84,7 @@ func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum
// able to sign via our shared keystore backend). // able to sign via our shared keystore backend).
func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign
@ -100,10 +97,7 @@ func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte
// be able to sign via our shared keystore backend). // be able to sign via our shared keystore backend).
func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign
@ -114,10 +108,7 @@ func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction,
// given hash with the given account using passphrase as extra authentication. // given hash with the given account using passphrase as extra authentication.
func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign
@ -128,10 +119,7 @@ func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passph
// transaction with the given account using passphrase as extra authentication. // transaction with the given account using passphrase as extra authentication.
func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign

View file

@ -20,7 +20,6 @@ import (
"bufio" "bufio"
"errors" "errors"
"fmt" "fmt"
"io"
"math/big" "math/big"
"os" "os"
"reflect" "reflect"
@ -198,7 +197,17 @@ func dumpConfig(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
io.WriteString(os.Stdout, comment)
os.Stdout.Write(out) dump := os.Stdout
if ctx.NArg() > 0 {
dump, err = os.OpenFile(ctx.Args().Get(0), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer dump.Close()
}
dump.WriteString(comment)
dump.Write(out)
return nil return nil
} }

View file

@ -65,7 +65,7 @@ const (
triesInMemory = 128 triesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch. // BlockChainVersion ensures that an incompatible database forces a resync from scratch.
BlockChainVersion = 3 BlockChainVersion uint64 = 3
) )
// CacheConfig contains the configuration values for the trie caching/pruning // CacheConfig contains the configuration values for the trie caching/pruning

View file

@ -26,19 +26,27 @@ import (
) )
// ReadDatabaseVersion retrieves the version number of the database. // ReadDatabaseVersion retrieves the version number of the database.
func ReadDatabaseVersion(db DatabaseReader) int { func ReadDatabaseVersion(db DatabaseReader) *uint64 {
var version int var version uint64
enc, _ := db.Get(databaseVerisionKey) enc, _ := db.Get(databaseVerisionKey)
rlp.DecodeBytes(enc, &version) if len(enc) == 0 {
return nil
}
if err := rlp.DecodeBytes(enc, &version); err != nil {
return nil
}
return version return &version
} }
// WriteDatabaseVersion stores the version number of the database // WriteDatabaseVersion stores the version number of the database
func WriteDatabaseVersion(db DatabaseWriter, version int) { func WriteDatabaseVersion(db DatabaseWriter, version uint64) {
enc, _ := rlp.EncodeToBytes(version) enc, err := rlp.EncodeToBytes(version)
if err := db.Put(databaseVerisionKey, enc); err != nil { if err != nil {
log.Crit("Failed to encode database version", "err", err)
}
if err = db.Put(databaseVerisionKey, enc); err != nil {
log.Crit("Failed to store the database version", "err", err) log.Crit("Failed to store the database version", "err", err)
} }
} }

View file

@ -143,8 +143,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
if !config.SkipBcVersionCheck { if !config.SkipBcVersionCheck {
bcVersion := rawdb.ReadDatabaseVersion(chainDb) bcVersion := rawdb.ReadDatabaseVersion(chainDb)
if bcVersion != core.BlockChainVersion && bcVersion != 0 { if bcVersion != nil && *bcVersion > core.BlockChainVersion {
return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d).\n", bcVersion, core.BlockChainVersion) return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion)
} else if bcVersion != nil && *bcVersion < core.BlockChainVersion {
log.Warn("Upgrade blockchain database version", "from", *bcVersion, "to", core.BlockChainVersion)
} }
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
} }

View file

@ -1074,6 +1074,15 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx cont
// GetTransactionCount returns the number of transactions the given address has sent for the given block number // GetTransactionCount returns the number of transactions the given address has sent for the given block number
func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) { func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) {
// Ask transaction pool for the nonce which includes pending transactions
if blockNr == rpc.PendingBlockNumber {
nonce, err := s.b.GetPoolNonce(ctx, address)
if err != nil {
return nil, err
}
return (*hexutil.Uint64)(&nonce), nil
}
// Resolve block number and use its state to ask for the nonce
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil { if state == nil || err != nil {
return nil, err return nil, err

View file

@ -22,31 +22,33 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
) )
//define some metrics // define some metrics
var ( var (
//All metrics are cumulative // All metrics are cumulative
//total amount of units credited // total amount of units credited
mBalanceCredit metrics.Counter mBalanceCredit metrics.Counter
//total amount of units debited // total amount of units debited
mBalanceDebit metrics.Counter mBalanceDebit metrics.Counter
//total amount of bytes credited // total amount of bytes credited
mBytesCredit metrics.Counter mBytesCredit metrics.Counter
//total amount of bytes debited // total amount of bytes debited
mBytesDebit metrics.Counter mBytesDebit metrics.Counter
//total amount of credited messages // total amount of credited messages
mMsgCredit metrics.Counter mMsgCredit metrics.Counter
//total amount of debited messages // total amount of debited messages
mMsgDebit metrics.Counter mMsgDebit metrics.Counter
//how many times local node had to drop remote peers // how many times local node had to drop remote peers
mPeerDrops metrics.Counter mPeerDrops metrics.Counter
//how many times local node overdrafted and dropped // how many times local node overdrafted and dropped
mSelfDrops metrics.Counter mSelfDrops metrics.Counter
MetricsRegistry metrics.Registry
) )
//Prices defines how prices are being passed on to the accounting instance // Prices defines how prices are being passed on to the accounting instance
type Prices interface { type Prices interface {
//Return the Price for a message // Return the Price for a message
Price(interface{}) *Price Price(interface{}) *Price
} }
@ -57,20 +59,20 @@ const (
Receiver = Payer(false) Receiver = Payer(false)
) )
//Price represents the costs of a message // Price represents the costs of a message
type Price struct { type Price struct {
Value uint64 // Value uint64
PerByte bool //True if the price is per byte or for unit PerByte bool // True if the price is per byte or for unit
Payer Payer Payer Payer
} }
//For gives back the price for a message // For gives back the price for a message
//A protocol provides the message price in absolute value // A protocol provides the message price in absolute value
//This method then returns the correct signed amount, // This method then returns the correct signed amount,
//depending on who pays, which is identified by the `payer` argument: // depending on who pays, which is identified by the `payer` argument:
//`Send` will pass a `Sender` payer, `Receive` will pass the `Receiver` argument. // `Send` will pass a `Sender` payer, `Receive` will pass the `Receiver` argument.
//Thus: If Sending and sender pays, amount positive, otherwise negative // Thus: If Sending and sender pays, amount positive, otherwise negative
//If Receiving, and receiver pays, amount positive, otherwise negative // If Receiving, and receiver pays, amount positive, otherwise negative
func (p *Price) For(payer Payer, size uint32) int64 { func (p *Price) For(payer Payer, size uint32) int64 {
price := p.Value price := p.Value
if p.PerByte { if p.PerByte {
@ -82,22 +84,22 @@ func (p *Price) For(payer Payer, size uint32) int64 {
return int64(price) return int64(price)
} }
//Balance is the actual accounting instance // Balance is the actual accounting instance
//Balance defines the operations needed for accounting // Balance defines the operations needed for accounting
//Implementations internally maintain the balance for every peer // Implementations internally maintain the balance for every peer
type Balance interface { type Balance interface {
//Adds amount to the local balance with remote node `peer`; // Adds amount to the local balance with remote node `peer`;
//positive amount = credit local node // positive amount = credit local node
//negative amount = debit local node // negative amount = debit local node
Add(amount int64, peer *Peer) error Add(amount int64, peer *Peer) error
} }
//Accounting implements the Hook interface // Accounting implements the Hook interface
//It interfaces to the balances through the Balance interface, // It interfaces to the balances through the Balance interface,
//while interfacing with protocols and its prices through the Prices interface // while interfacing with protocols and its prices through the Prices interface
type Accounting struct { type Accounting struct {
Balance //interface to accounting logic Balance // interface to accounting logic
Prices //interface to prices logic Prices // interface to prices logic
} }
func NewAccounting(balance Balance, po Prices) *Accounting { func NewAccounting(balance Balance, po Prices) *Accounting {
@ -108,70 +110,68 @@ func NewAccounting(balance Balance, po Prices) *Accounting {
return ah return ah
} }
//SetupAccountingMetrics creates a separate registry for p2p accounting metrics; // SetupAccountingMetrics creates a separate registry for p2p accounting metrics;
//this registry should be independent of any other metrics as it persists at different endpoints. // this registry should be independent of any other metrics as it persists at different endpoints.
//It also instantiates the given metrics and starts the persisting go-routine which // It also instantiates the given metrics and starts the persisting go-routine which
//at the passed interval writes the metrics to a LevelDB // at the passed interval writes the metrics to a LevelDB
func SetupAccountingMetrics(reportInterval time.Duration, path string) *AccountingMetrics { func SetupAccountingMetrics(reportInterval time.Duration, path string) *AccountingMetrics {
//create an empty registry // create an empty registry
registry := metrics.NewRegistry() MetricsRegistry = metrics.NewRegistry()
//instantiate the metrics // instantiate the metrics
mBalanceCredit = metrics.NewRegisteredCounterForced("account.balance.credit", registry) mBalanceCredit = metrics.NewRegisteredCounterForced("account.balance.credit", MetricsRegistry)
mBalanceDebit = metrics.NewRegisteredCounterForced("account.balance.debit", registry) mBalanceDebit = metrics.NewRegisteredCounterForced("account.balance.debit", MetricsRegistry)
mBytesCredit = metrics.NewRegisteredCounterForced("account.bytes.credit", registry) mBytesCredit = metrics.NewRegisteredCounterForced("account.bytes.credit", MetricsRegistry)
mBytesDebit = metrics.NewRegisteredCounterForced("account.bytes.debit", registry) mBytesDebit = metrics.NewRegisteredCounterForced("account.bytes.debit", MetricsRegistry)
mMsgCredit = metrics.NewRegisteredCounterForced("account.msg.credit", registry) mMsgCredit = metrics.NewRegisteredCounterForced("account.msg.credit", MetricsRegistry)
mMsgDebit = metrics.NewRegisteredCounterForced("account.msg.debit", registry) mMsgDebit = metrics.NewRegisteredCounterForced("account.msg.debit", MetricsRegistry)
mPeerDrops = metrics.NewRegisteredCounterForced("account.peerdrops", registry) mPeerDrops = metrics.NewRegisteredCounterForced("account.peerdrops", MetricsRegistry)
mSelfDrops = metrics.NewRegisteredCounterForced("account.selfdrops", registry) mSelfDrops = metrics.NewRegisteredCounterForced("account.selfdrops", MetricsRegistry)
//create the DB and start persisting // create the DB and start persisting
return NewAccountingMetrics(registry, reportInterval, path) return NewAccountingMetrics(MetricsRegistry, reportInterval, path)
} }
//Implement Hook.Send
// Send takes a peer, a size and a msg and // Send takes a peer, a size and a msg and
// - calculates the cost for the local node sending a msg of size to peer using the Prices interface // - calculates the cost for the local node sending a msg of size to peer using the Prices interface
// - credits/debits local node using balance interface // - credits/debits local node using balance interface
func (ah *Accounting) Send(peer *Peer, size uint32, msg interface{}) error { func (ah *Accounting) Send(peer *Peer, size uint32, msg interface{}) error {
//get the price for a message (through the protocol spec) // get the price for a message (through the protocol spec)
price := ah.Price(msg) price := ah.Price(msg)
//this message doesn't need accounting // this message doesn't need accounting
if price == nil { if price == nil {
return nil return nil
} }
//evaluate the price for sending messages // evaluate the price for sending messages
costToLocalNode := price.For(Sender, size) costToLocalNode := price.For(Sender, size)
//do the accounting // do the accounting
err := ah.Add(costToLocalNode, peer) err := ah.Add(costToLocalNode, peer)
//record metrics: just increase counters for user-facing metrics // record metrics: just increase counters for user-facing metrics
ah.doMetrics(costToLocalNode, size, err) ah.doMetrics(costToLocalNode, size, err)
return err return err
} }
//Implement Hook.Receive
// Receive takes a peer, a size and a msg and // Receive takes a peer, a size and a msg and
// - calculates the cost for the local node receiving a msg of size from peer using the Prices interface // - calculates the cost for the local node receiving a msg of size from peer using the Prices interface
// - credits/debits local node using balance interface // - credits/debits local node using balance interface
func (ah *Accounting) Receive(peer *Peer, size uint32, msg interface{}) error { func (ah *Accounting) Receive(peer *Peer, size uint32, msg interface{}) error {
//get the price for a message (through the protocol spec) // get the price for a message (through the protocol spec)
price := ah.Price(msg) price := ah.Price(msg)
//this message doesn't need accounting // this message doesn't need accounting
if price == nil { if price == nil {
return nil return nil
} }
//evaluate the price for receiving messages // evaluate the price for receiving messages
costToLocalNode := price.For(Receiver, size) costToLocalNode := price.For(Receiver, size)
//do the accounting // do the accounting
err := ah.Add(costToLocalNode, peer) err := ah.Add(costToLocalNode, peer)
//record metrics: just increase counters for user-facing metrics // record metrics: just increase counters for user-facing metrics
ah.doMetrics(costToLocalNode, size, err) ah.doMetrics(costToLocalNode, size, err)
return err return err
} }
//record some metrics // record some metrics
//this is not an error handling. `err` is returned by both `Send` and `Receive` // this is not an error handling. `err` is returned by both `Send` and `Receive`
//`err` will only be non-nil if a limit has been violated (overdraft), in which case the peer has been dropped. // `err` will only be non-nil if a limit has been violated (overdraft), in which case the peer has been dropped.
//if the limit has been violated and `err` is thus not nil: // if the limit has been violated and `err` is thus not nil:
// * if the price is positive, local node has been credited; thus `err` implicitly signals the REMOTE has been dropped // * if the price is positive, local node has been credited; thus `err` implicitly signals the REMOTE has been dropped
// * if the price is negative, local node has been debited, thus `err` implicitly signals LOCAL node "overdraft" // * if the price is negative, local node has been debited, thus `err` implicitly signals LOCAL node "overdraft"
func (ah *Accounting) doMetrics(price int64, size uint32, err error) { func (ah *Accounting) doMetrics(price int64, size uint32, err error) {
@ -180,7 +180,7 @@ func (ah *Accounting) doMetrics(price int64, size uint32, err error) {
mBytesCredit.Inc(int64(size)) mBytesCredit.Inc(int64(size))
mMsgCredit.Inc(1) mMsgCredit.Inc(1)
if err != nil { if err != nil {
//increase the number of times a remote node has been dropped due to "overdraft" // increase the number of times a remote node has been dropped due to "overdraft"
mPeerDrops.Inc(1) mPeerDrops.Inc(1)
} }
} else { } else {
@ -188,7 +188,7 @@ func (ah *Accounting) doMetrics(price int64, size uint32, err error) {
mBytesDebit.Inc(int64(size)) mBytesDebit.Inc(int64(size))
mMsgDebit.Inc(1) mMsgDebit.Inc(1)
if err != nil { if err != nil {
//increase the number of times the local node has done an "overdraft" in respect to other nodes // increase the number of times the local node has done an "overdraft" in respect to other nodes
mSelfDrops.Inc(1) mSelfDrops.Inc(1)
} }
} }

View file

@ -351,17 +351,3 @@ func (sn *SimNode) NodeInfo() *p2p.NodeInfo {
} }
return server.NodeInfo() return server.NodeInfo()
} }
func setSocketBuffer(conn net.Conn, socketReadBuffer int, socketWriteBuffer int) error {
if v, ok := conn.(*net.UnixConn); ok {
err := v.SetReadBuffer(socketReadBuffer)
if err != nil {
return err
}
err = v.SetWriteBuffer(socketWriteBuffer)
if err != nil {
return err
}
}
return nil
}

View file

@ -25,21 +25,8 @@ import (
var ( var (
ErrNodeNotFound = errors.New("node not found") ErrNodeNotFound = errors.New("node not found")
ErrNoPivotNode = errors.New("no pivot node set")
) )
// ConnectToPivotNode connects the node with provided NodeID
// to the pivot node, already set by Network.SetPivotNode method.
// It is useful when constructing a star network topology
// when Network adds and removes nodes dynamically.
func (net *Network) ConnectToPivotNode(id enode.ID) (err error) {
pivot := net.GetPivotNode()
if pivot == nil {
return ErrNoPivotNode
}
return net.connect(pivot.ID(), id)
}
// ConnectToLastNode connects the node with provided NodeID // ConnectToLastNode connects the node with provided NodeID
// to the last node that is up, and avoiding connection to self. // to the last node that is up, and avoiding connection to self.
// It is useful when constructing a chain network topology // It is useful when constructing a chain network topology
@ -115,35 +102,23 @@ func (net *Network) ConnectNodesRing(ids []enode.ID) (err error) {
return net.connect(ids[l-1], ids[0]) return net.connect(ids[l-1], ids[0])
} }
// ConnectNodesStar connects all nodes in a star topology // ConnectNodesStar connects all nodes into a star topology
// with the center at provided NodeID.
// If ids argument is nil, all nodes that are up will be connected. // If ids argument is nil, all nodes that are up will be connected.
func (net *Network) ConnectNodesStar(pivot enode.ID, ids []enode.ID) (err error) { func (net *Network) ConnectNodesStar(ids []enode.ID, center enode.ID) (err error) {
if ids == nil { if ids == nil {
ids = net.getUpNodeIDs() ids = net.getUpNodeIDs()
} }
for _, id := range ids { for _, id := range ids {
if pivot == id { if center == id {
continue continue
} }
if err := net.connect(pivot, id); err != nil { if err := net.connect(center, id); err != nil {
return err return err
} }
} }
return nil return nil
} }
// ConnectNodesStarPivot connects all nodes in a star topology
// with the center at already set pivot node.
// If ids argument is nil, all nodes that are up will be connected.
func (net *Network) ConnectNodesStarPivot(ids []enode.ID) (err error) {
pivot := net.GetPivotNode()
if pivot == nil {
return ErrNoPivotNode
}
return net.ConnectNodesStar(pivot.ID(), ids)
}
// connect connects two nodes but ignores already connected error. // connect connects two nodes but ignores already connected error.
func (net *Network) connect(oneID, otherID enode.ID) error { func (net *Network) connect(oneID, otherID enode.ID) error {
return ignoreAlreadyConnectedErr(net.Connect(oneID, otherID)) return ignoreAlreadyConnectedErr(net.Connect(oneID, otherID))
@ -155,22 +130,3 @@ func ignoreAlreadyConnectedErr(err error) error {
} }
return err return err
} }
// SetPivotNode sets the NodeID of the network's pivot node.
// Pivot node is just a specific node that should be treated
// differently then other nodes in test. SetPivotNode and
// GetPivotNode are just a convenient functions to set and
// retrieve it.
func (net *Network) SetPivotNode(id enode.ID) {
net.lock.Lock()
defer net.lock.Unlock()
net.pivotNodeID = id
}
// GetPivotNode returns NodeID of the pivot node set by
// Network.SetPivotNode method.
func (net *Network) GetPivotNode() (node *Node) {
net.lock.RLock()
defer net.lock.RUnlock()
return net.getNode(net.pivotNodeID)
}

View file

@ -58,24 +58,6 @@ func newTestNetwork(t *testing.T, nodeCount int) (*Network, []enode.ID) {
return network, ids return network, ids
} }
func TestConnectToPivotNode(t *testing.T) {
net, ids := newTestNetwork(t, 2)
defer net.Shutdown()
pivot := ids[0]
net.SetPivotNode(pivot)
other := ids[1]
err := net.ConnectToPivotNode(other)
if err != nil {
t.Fatal(err)
}
if net.GetConn(pivot, other) == nil {
t.Error("pivot and the other node are not connected")
}
}
func TestConnectToLastNode(t *testing.T) { func TestConnectToLastNode(t *testing.T) {
net, ids := newTestNetwork(t, 10) net, ids := newTestNetwork(t, 10)
defer net.Shutdown() defer net.Shutdown()
@ -125,7 +107,20 @@ func TestConnectToRandomNode(t *testing.T) {
} }
func TestConnectNodesFull(t *testing.T) { func TestConnectNodesFull(t *testing.T) {
net, ids := newTestNetwork(t, 12) tests := []struct {
name string
nodeCount int
}{
{name: "no node", nodeCount: 0},
{name: "single node", nodeCount: 1},
{name: "2 nodes", nodeCount: 2},
{name: "3 nodes", nodeCount: 3},
{name: "even number of nodes", nodeCount: 12},
{name: "odd number of nodes", nodeCount: 13},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
net, ids := newTestNetwork(t, test.nodeCount)
defer net.Shutdown() defer net.Shutdown()
err := net.ConnectNodesFull(ids) err := net.ConnectNodesFull(ids)
@ -134,6 +129,8 @@ func TestConnectNodesFull(t *testing.T) {
} }
VerifyFull(t, net, ids) VerifyFull(t, net, ids)
})
}
} }
func TestConnectNodesChain(t *testing.T) { func TestConnectNodesChain(t *testing.T) {
@ -166,23 +163,7 @@ func TestConnectNodesStar(t *testing.T) {
pivotIndex := 2 pivotIndex := 2
err := net.ConnectNodesStar(ids[pivotIndex], ids) err := net.ConnectNodesStar(ids, ids[pivotIndex])
if err != nil {
t.Fatal(err)
}
VerifyStar(t, net, ids, pivotIndex)
}
func TestConnectNodesStarPivot(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()
pivotIndex := 4
net.SetPivotNode(ids[pivotIndex])
err := net.ConnectNodesStarPivot(ids)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -58,8 +58,6 @@ type Network struct {
Conns []*Conn `json:"conns"` Conns []*Conn `json:"conns"`
connMap map[string]int connMap map[string]int
pivotNodeID enode.ID
nodeAdapter adapters.NodeAdapter nodeAdapter adapters.NodeAdapter
events event.Feed events event.Feed
lock sync.RWMutex lock sync.RWMutex

View file

@ -65,7 +65,7 @@ func (d *Peer) HandleMsg(ctx context.Context, msg interface{}) error {
// NotifyDepth sends a message to all connections if depth of saturation is changed // NotifyDepth sends a message to all connections if depth of saturation is changed
func NotifyDepth(depth uint8, kad *Kademlia) { func NotifyDepth(depth uint8, kad *Kademlia) {
f := func(val *Peer, po int, _ bool) bool { f := func(val *Peer, po int) bool {
val.NotifyDepth(depth) val.NotifyDepth(depth)
return true return true
} }
@ -74,7 +74,7 @@ func NotifyDepth(depth uint8, kad *Kademlia) {
// NotifyPeer informs all peers about a newly added node // NotifyPeer informs all peers about a newly added node
func NotifyPeer(p *BzzAddr, k *Kademlia) { func NotifyPeer(p *BzzAddr, k *Kademlia) {
f := func(val *Peer, po int, _ bool) bool { f := func(val *Peer, po int) bool {
val.NotifyPeer(p, uint8(po)) val.NotifyPeer(p, uint8(po))
return true return true
} }
@ -160,7 +160,7 @@ func (d *Peer) handleSubPeersMsg(msg *subPeersMsg) error {
if !d.sentPeers { if !d.sentPeers {
d.setDepth(msg.Depth) d.setDepth(msg.Depth)
var peers []*BzzAddr var peers []*BzzAddr
d.kad.EachConn(d.Over(), 255, func(p *Peer, po int, isproxbin bool) bool { d.kad.EachConn(d.Over(), 255, func(p *Peer, po int) bool {
if pob, _ := Pof(d, d.kad.BaseAddr(), 0); pob > po { if pob, _ := Pof(d, d.kad.BaseAddr(), 0); pob > po {
return false return false
} }

View file

@ -114,7 +114,7 @@ func (h *Hive) Stop() error {
} }
} }
log.Info(fmt.Sprintf("%08x hive stopped, dropping peers", h.BaseAddr()[:4])) log.Info(fmt.Sprintf("%08x hive stopped, dropping peers", h.BaseAddr()[:4]))
h.EachConn(nil, 255, func(p *Peer, _ int, _ bool) bool { h.EachConn(nil, 255, func(p *Peer, _ int) bool {
log.Info(fmt.Sprintf("%08x dropping peer %08x", h.BaseAddr()[:4], p.Address()[:4])) log.Info(fmt.Sprintf("%08x dropping peer %08x", h.BaseAddr()[:4], p.Address()[:4]))
p.Drop(nil) p.Drop(nil)
return true return true
@ -228,7 +228,7 @@ func (h *Hive) loadPeers() error {
// savePeers, savePeer implement persistence callback/ // savePeers, savePeer implement persistence callback/
func (h *Hive) savePeers() error { func (h *Hive) savePeers() error {
var peers []*BzzAddr var peers []*BzzAddr
h.Kademlia.EachAddr(nil, 256, func(pa *BzzAddr, i int, _ bool) bool { h.Kademlia.EachAddr(nil, 256, func(pa *BzzAddr, i int) bool {
if pa == nil { if pa == nil {
log.Warn(fmt.Sprintf("empty addr: %v", i)) log.Warn(fmt.Sprintf("empty addr: %v", i))
return true return true

View file

@ -103,7 +103,7 @@ func TestHiveStatePersistance(t *testing.T) {
pp.Start(s1.Server) pp.Start(s1.Server)
i := 0 i := 0
pp.Kademlia.EachAddr(nil, 256, func(addr *BzzAddr, po int, nn bool) bool { pp.Kademlia.EachAddr(nil, 256, func(addr *BzzAddr, po int) bool {
delete(peers, addr.String()) delete(peers, addr.String())
i++ i++
return true return true

View file

@ -55,7 +55,7 @@ var Pof = pot.DefaultPof(256)
type KadParams struct { type KadParams struct {
// adjustable parameters // adjustable parameters
MaxProxDisplay int // number of rows the table shows MaxProxDisplay int // number of rows the table shows
MinProxBinSize int // nearest neighbour core minimum cardinality NeighbourhoodSize int // nearest neighbour core minimum cardinality
MinBinSize int // minimum number of peers in a row MinBinSize int // minimum number of peers in a row
MaxBinSize int // maximum number of peers in a row before pruning MaxBinSize int // maximum number of peers in a row before pruning
RetryInterval int64 // initial interval before a peer is first redialed RetryInterval int64 // initial interval before a peer is first redialed
@ -69,7 +69,7 @@ type KadParams struct {
func NewKadParams() *KadParams { func NewKadParams() *KadParams {
return &KadParams{ return &KadParams{
MaxProxDisplay: 16, MaxProxDisplay: 16,
MinProxBinSize: 2, NeighbourhoodSize: 2,
MinBinSize: 2, MinBinSize: 2,
MaxBinSize: 4, MaxBinSize: 4,
RetryInterval: 4200000000, // 4.2 sec RetryInterval: 4200000000, // 4.2 sec
@ -175,7 +175,7 @@ func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) {
k.lock.Lock() k.lock.Lock()
defer k.lock.Unlock() defer k.lock.Unlock()
minsize := k.MinBinSize minsize := k.MinBinSize
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
// if there is a callable neighbour within the current proxBin, connect // if there is a callable neighbour within the current proxBin, connect
// this makes sure nearest neighbour set is fully connected // this makes sure nearest neighbour set is fully connected
var ppo int var ppo int
@ -306,7 +306,7 @@ func (k *Kademlia) sendNeighbourhoodDepthChange() {
// It provides signaling of neighbourhood depth change. // It provides signaling of neighbourhood depth change.
// This part of the code is sending new neighbourhood depth to nDepthC if that condition is met. // This part of the code is sending new neighbourhood depth to nDepthC if that condition is met.
if k.nDepthC != nil { if k.nDepthC != nil {
nDepth := depthForPot(k.conns, k.MinProxBinSize, k.base) nDepth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
if nDepth != k.nDepth { if nDepth != k.nDepth {
k.nDepth = nDepth k.nDepth = nDepth
k.nDepthC <- nDepth k.nDepthC <- nDepth
@ -366,7 +366,7 @@ func (k *Kademlia) EachBin(base []byte, pof pot.Pof, o int, eachBinFunc func(con
var startPo int var startPo int
var endPo int var endPo int
kadDepth := depthForPot(k.conns, k.MinProxBinSize, k.base) kadDepth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
k.conns.EachBin(base, Pof, o, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { k.conns.EachBin(base, Pof, o, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool {
if startPo > 0 && endPo != k.MaxProxDisplay { if startPo > 0 && endPo != k.MaxProxDisplay {
@ -390,61 +390,57 @@ func (k *Kademlia) EachBin(base []byte, pof pot.Pof, o int, eachBinFunc func(con
// EachConn is an iterator with args (base, po, f) applies f to each live peer // EachConn is an iterator with args (base, po, f) applies f to each live peer
// that has proximity order po or less as measured from the base // that has proximity order po or less as measured from the base
// if base is nil, kademlia base address is used // if base is nil, kademlia base address is used
// It returns peers in order deepest to shallowest func (k *Kademlia) EachConn(base []byte, o int, f func(*Peer, int) bool) {
func (k *Kademlia) EachConn(base []byte, o int, f func(*Peer, int, bool) bool) {
k.lock.RLock() k.lock.RLock()
defer k.lock.RUnlock() defer k.lock.RUnlock()
k.eachConn(base, o, f) k.eachConn(base, o, f)
} }
func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int, bool) bool) { func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int) bool) {
if len(base) == 0 { if len(base) == 0 {
base = k.base base = k.base
} }
depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
k.conns.EachNeighbour(base, Pof, func(val pot.Val, po int) bool { k.conns.EachNeighbour(base, Pof, func(val pot.Val, po int) bool {
if po > o { if po > o {
return true return true
} }
return f(val.(*Peer), po, po >= depth) return f(val.(*Peer), po)
}) })
} }
// EachAddr called with (base, po, f) is an iterator applying f to each known peer // EachAddr called with (base, po, f) is an iterator applying f to each known peer
// that has proximity order o or less as measured from the base // that has proximity order o or less as measured from the base
// if base is nil, kademlia base address is used // if base is nil, kademlia base address is used
// It returns peers in order deepest to shallowest func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int) bool) {
func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool) {
k.lock.RLock() k.lock.RLock()
defer k.lock.RUnlock() defer k.lock.RUnlock()
k.eachAddr(base, o, f) k.eachAddr(base, o, f)
} }
func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool) { func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int) bool) {
if len(base) == 0 { if len(base) == 0 {
base = k.base base = k.base
} }
depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
k.addrs.EachNeighbour(base, Pof, func(val pot.Val, po int) bool { k.addrs.EachNeighbour(base, Pof, func(val pot.Val, po int) bool {
if po > o { if po > o {
return true return true
} }
return f(val.(*entry).BzzAddr, po, po >= depth) return f(val.(*entry).BzzAddr, po)
}) })
} }
func (k *Kademlia) NeighbourhoodDepth() (depth int) { func (k *Kademlia) NeighbourhoodDepth() (depth int) {
k.lock.RLock() k.lock.RLock()
defer k.lock.RUnlock() defer k.lock.RUnlock()
return depthForPot(k.conns, k.MinProxBinSize, k.base) return depthForPot(k.conns, k.NeighbourhoodSize, k.base)
} }
// depthForPot returns the proximity order that defines the distance of // depthForPot returns the proximity order that defines the distance of
// the nearest neighbour set with cardinality >= MinProxBinSize // the nearest neighbour set with cardinality >= NeighbourhoodSize
// if there is altogether less than MinProxBinSize peers it returns 0 // if there is altogether less than NeighbourhoodSize peers it returns 0
// caller must hold the lock // caller must hold the lock
func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) { func depthForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) {
if p.Size() <= minProxBinSize { if p.Size() <= neighbourhoodSize {
return 0 return 0
} }
@ -452,7 +448,7 @@ func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) {
var size int var size int
// determining the depth is a two-step process // determining the depth is a two-step process
// first we find the proximity bin of the shallowest of the MinProxBinSize peers // first we find the proximity bin of the shallowest of the NeighbourhoodSize peers
// the numeric value of depth cannot be higher than this // the numeric value of depth cannot be higher than this
var maxDepth int var maxDepth int
@ -465,7 +461,7 @@ func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) {
// this means we have all nn-peers. // this means we have all nn-peers.
// depth is by default set to the bin of the farthest nn-peer // depth is by default set to the bin of the farthest nn-peer
if size == minProxBinSize { if size == neighbourhoodSize {
maxDepth = i maxDepth = i
return false return false
} }
@ -542,12 +538,12 @@ func (k *Kademlia) string() string {
rows = append(rows, "=========================================================================") rows = append(rows, "=========================================================================")
rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), k.BaseAddr()[:3])) rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), k.BaseAddr()[:3]))
rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", k.conns.Size(), k.addrs.Size(), k.MinProxBinSize, k.MinBinSize, k.MaxBinSize)) rows = append(rows, fmt.Sprintf("population: %d (%d), NeighbourhoodSize: %d, MinBinSize: %d, MaxBinSize: %d", k.conns.Size(), k.addrs.Size(), k.NeighbourhoodSize, k.MinBinSize, k.MaxBinSize))
liverows := make([]string, k.MaxProxDisplay) liverows := make([]string, k.MaxProxDisplay)
peersrows := make([]string, k.MaxProxDisplay) peersrows := make([]string, k.MaxProxDisplay)
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
rest := k.conns.Size() rest := k.conns.Size()
k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool {
var rowlen int var rowlen int
@ -615,10 +611,10 @@ type PeerPot struct {
// NewPeerPotMap creates a map of pot record of *BzzAddr with keys // NewPeerPotMap creates a map of pot record of *BzzAddr with keys
// as hexadecimal representations of the address. // as hexadecimal representations of the address.
// the MinProxBinSize of the passed kademlia is used // the NeighbourhoodSize of the passed kademlia is used
// used for testing only // used for testing only
// TODO move to separate testing tools file // TODO move to separate testing tools file
func NewPeerPotMap(minProxBinSize int, addrs [][]byte) map[string]*PeerPot { func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot {
// create a table of all nodes for health check // create a table of all nodes for health check
np := pot.NewPot(nil, 0) np := pot.NewPot(nil, 0)
@ -632,7 +628,7 @@ func NewPeerPotMap(minProxBinSize int, addrs [][]byte) map[string]*PeerPot {
for i, a := range addrs { for i, a := range addrs {
// actual kademlia depth // actual kademlia depth
depth := depthForPot(np, minProxBinSize, a) depth := depthForPot(np, neighbourhoodSize, a)
// all nn-peers // all nn-peers
var nns [][]byte var nns [][]byte
@ -674,7 +670,7 @@ func (k *Kademlia) saturation() int {
return prev == po && size >= k.MinBinSize return prev == po && size >= k.MinBinSize
}) })
// TODO evaluate whether this check cannot just as well be done within the eachbin // TODO evaluate whether this check cannot just as well be done within the eachbin
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
if depth < prev { if depth < prev {
return depth return depth
} }
@ -687,12 +683,11 @@ func (k *Kademlia) saturation() int {
// TODO move to separate testing tools file // TODO move to separate testing tools file
func (k *Kademlia) knowNeighbours(addrs [][]byte) (got bool, n int, missing [][]byte) { func (k *Kademlia) knowNeighbours(addrs [][]byte) (got bool, n int, missing [][]byte) {
pm := make(map[string]bool) pm := make(map[string]bool)
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
// create a map with all peers at depth and deeper known in the kademlia // create a map with all peers at depth and deeper known in the kademlia
k.eachAddr(nil, 255, func(p *BzzAddr, po int) bool {
// in order deepest to shallowest compared to the kademlia base address // in order deepest to shallowest compared to the kademlia base address
// all bins (except self) are included (0 <= bin <= 255) // all bins (except self) are included (0 <= bin <= 255)
depth := depthForPot(k.addrs, k.MinProxBinSize, k.base)
k.eachAddr(nil, 255, func(p *BzzAddr, po int, nn bool) bool {
if po < depth { if po < depth {
return false return false
} }
@ -728,8 +723,8 @@ func (k *Kademlia) connectedNeighbours(peers [][]byte) (got bool, n int, missing
// create a map with all peers at depth and deeper that are connected in the kademlia // create a map with all peers at depth and deeper that are connected in the kademlia
// in order deepest to shallowest compared to the kademlia base address // in order deepest to shallowest compared to the kademlia base address
// all bins (except self) are included (0 <= bin <= 255) // all bins (except self) are included (0 <= bin <= 255)
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
k.eachConn(nil, 255, func(p *Peer, po int, nn bool) bool { k.eachConn(nil, 255, func(p *Peer, po int) bool {
if po < depth { if po < depth {
return false return false
} }
@ -781,7 +776,7 @@ func (k *Kademlia) Healthy(pp *PeerPot) *Health {
defer k.lock.RUnlock() defer k.lock.RUnlock()
gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet) gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet)
knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet) knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet)
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
saturated := k.saturation() < depth saturated := k.saturation() < depth
log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated)) log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated))
return &Health{ return &Health{

View file

@ -45,7 +45,7 @@ func newTestKademliaParams() *KadParams {
params := NewKadParams() params := NewKadParams()
// TODO why is this 1? // TODO why is this 1?
params.MinBinSize = 1 params.MinBinSize = 1
params.MinProxBinSize = 2 params.NeighbourhoodSize = 2
return params return params
} }
@ -87,7 +87,7 @@ func Register(k *Kademlia, regs ...string) {
// empty bins above the farthest "nearest neighbor-peer" then // empty bins above the farthest "nearest neighbor-peer" then
// the depth should be set at the farthest of those empty bins // the depth should be set at the farthest of those empty bins
// //
// TODO: Make test adapt to change in MinProxBinSize // TODO: Make test adapt to change in NeighbourhoodSize
func TestNeighbourhoodDepth(t *testing.T) { func TestNeighbourhoodDepth(t *testing.T) {
baseAddressBytes := RandomAddr().OAddr baseAddressBytes := RandomAddr().OAddr
kad := NewKademlia(baseAddressBytes, NewKadParams()) kad := NewKademlia(baseAddressBytes, NewKadParams())
@ -232,12 +232,12 @@ func assertHealth(t *testing.T, k *Kademlia, expectHealthy bool, expectSaturatio
t.Helper() t.Helper()
kid := common.Bytes2Hex(k.BaseAddr()) kid := common.Bytes2Hex(k.BaseAddr())
addrs := [][]byte{k.BaseAddr()} addrs := [][]byte{k.BaseAddr()}
k.EachAddr(nil, 255, func(addr *BzzAddr, po int, _ bool) bool { k.EachAddr(nil, 255, func(addr *BzzAddr, po int) bool {
addrs = append(addrs, addr.Address()) addrs = append(addrs, addr.Address())
return true return true
}) })
pp := NewPeerPotMap(k.MinProxBinSize, addrs) pp := NewPeerPotMap(k.NeighbourhoodSize, addrs)
healthParams := k.Healthy(pp[kid]) healthParams := k.Healthy(pp[kid])
// definition of health, all conditions but be true: // definition of health, all conditions but be true:
@ -605,7 +605,7 @@ func TestKademliaHiveString(t *testing.T) {
Register(k, "10000000", "10000001") Register(k, "10000000", "10000001")
k.MaxProxDisplay = 8 k.MaxProxDisplay = 8
h := k.String() h := k.String()
expH := "\n=========================================================================\nMon Feb 27 12:10:28 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 000000\npopulation: 2 (4), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 4\n============ DEPTH: 0 ==========================================\n000 0 | 2 8100 (0) 8000 (0)\n001 1 4000 | 1 4000 (0)\n002 1 2000 | 1 2000 (0)\n003 0 | 0\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n=========================================================================" expH := "\n=========================================================================\nMon Feb 27 12:10:28 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 000000\npopulation: 2 (4), NeighbourhoodSize: 2, MinBinSize: 1, MaxBinSize: 4\n============ DEPTH: 0 ==========================================\n000 0 | 2 8100 (0) 8000 (0)\n001 1 4000 | 1 4000 (0)\n002 1 2000 | 1 2000 (0)\n003 0 | 0\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n========================================================================="
if expH[104:] != h[104:] { if expH[104:] != h[104:] {
t.Fatalf("incorrect hive output. expected %v, got %v", expH, h) t.Fatalf("incorrect hive output. expected %v, got %v", expH, h)
} }
@ -636,7 +636,7 @@ func testKademliaCase(t *testing.T, pivotAddr string, addrs ...string) {
} }
} }
ppmap := NewPeerPotMap(k.MinProxBinSize, byteAddrs) ppmap := NewPeerPotMap(k.NeighbourhoodSize, byteAddrs)
pp := ppmap[pivotAddr] pp := ppmap[pivotAddr]
@ -662,7 +662,7 @@ in higher level tests for streaming. They were generated randomly.
========================================================================= =========================================================================
Mon Apr 9 12:18:24 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 7efef1 Mon Apr 9 12:18:24 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 7efef1
population: 9 (49), MinProxBinSize: 2, MinBinSize: 2, MaxBinSize: 4 population: 9 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
000 2 d7e5 ec56 | 18 ec56 (0) d7e5 (0) d9e0 (0) c735 (0) 000 2 d7e5 ec56 | 18 ec56 (0) d7e5 (0) d9e0 (0) c735 (0)
001 2 18f1 3176 | 14 18f1 (0) 10bb (0) 10d1 (0) 0421 (0) 001 2 18f1 3176 | 14 18f1 (0) 10bb (0) 10d1 (0) 0421 (0)
002 2 52aa 47cd | 11 52aa (0) 51d9 (0) 5161 (0) 5130 (0) 002 2 52aa 47cd | 11 52aa (0) 51d9 (0) 5161 (0) 5130 (0)
@ -745,7 +745,7 @@ in higher level tests for streaming. They were generated randomly.
========================================================================= =========================================================================
Mon Apr 9 18:43:48 UTC 2018 KΛÐΞMLIΛ hive: queen's address: bc7f3b Mon Apr 9 18:43:48 UTC 2018 KΛÐΞMLIΛ hive: queen's address: bc7f3b
population: 9 (49), MinProxBinSize: 2, MinBinSize: 2, MaxBinSize: 4 population: 9 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
000 2 0f49 67ff | 28 0f49 (0) 0211 (0) 07b2 (0) 0703 (0) 000 2 0f49 67ff | 28 0f49 (0) 0211 (0) 07b2 (0) 0703 (0)
001 2 e84b f3a4 | 13 f3a4 (0) e84b (0) e58b (0) e60b (0) 001 2 e84b f3a4 | 13 f3a4 (0) e84b (0) e58b (0) e60b (0)
002 1 8dba | 1 8dba (0) 002 1 8dba | 1 8dba (0)
@ -779,7 +779,7 @@ in higher level tests for streaming. They were generated randomly.
========================================================================= =========================================================================
Mon Apr 9 19:04:35 UTC 2018 KΛÐΞMLIΛ hive: queen's address: b4822e Mon Apr 9 19:04:35 UTC 2018 KΛÐΞMLIΛ hive: queen's address: b4822e
population: 8 (49), MinProxBinSize: 2, MinBinSize: 2, MaxBinSize: 4 population: 8 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
000 2 786c 774b | 29 774b (0) 786c (0) 7a79 (0) 7d2f (0) 000 2 786c 774b | 29 774b (0) 786c (0) 7a79 (0) 7d2f (0)
001 2 d9de cf19 | 10 cf19 (0) d9de (0) d2ff (0) d2a2 (0) 001 2 d9de cf19 | 10 cf19 (0) d9de (0) d2ff (0) d2a2 (0)
002 2 8ca1 8d74 | 5 8d74 (0) 8ca1 (0) 9793 (0) 9f51 (0) 002 2 8ca1 8d74 | 5 8d74 (0) 8ca1 (0) 9793 (0) 9f51 (0)
@ -813,7 +813,7 @@ in higher level tests for streaming. They were generated randomly.
========================================================================= =========================================================================
Mon Apr 9 19:16:25 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 9a90fe Mon Apr 9 19:16:25 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 9a90fe
population: 8 (49), MinProxBinSize: 2, MinBinSize: 2, MaxBinSize: 4 population: 8 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
000 2 72ef 4e6c | 24 0b1e (0) 0d66 (0) 17f5 (0) 17e8 (0) 000 2 72ef 4e6c | 24 0b1e (0) 0d66 (0) 17f5 (0) 17e8 (0)
001 2 fc2b fa47 | 13 fa47 (0) fc2b (0) fffd (0) ecef (0) 001 2 fc2b fa47 | 13 fa47 (0) fc2b (0) fffd (0) ecef (0)
002 2 b847 afa8 | 6 afa8 (0) ad77 (0) bb7c (0) b847 (0) 002 2 b847 afa8 | 6 afa8 (0) ad77 (0) bb7c (0) b847 (0)
@ -848,7 +848,7 @@ in higher level tests for streaming. They were generated randomly.
========================================================================= =========================================================================
Mon Apr 9 19:25:18 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 5dd5c7 Mon Apr 9 19:25:18 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 5dd5c7
population: 13 (49), MinProxBinSize: 2, MinBinSize: 2, MaxBinSize: 4 population: 13 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
000 2 e528 fad0 | 22 fad0 (0) e528 (0) e3bb (0) ed13 (0) 000 2 e528 fad0 | 22 fad0 (0) e528 (0) e3bb (0) ed13 (0)
001 3 3f30 18e0 1dd3 | 7 3f30 (0) 23db (0) 10b6 (0) 18e0 (0) 001 3 3f30 18e0 1dd3 | 7 3f30 (0) 23db (0) 10b6 (0) 18e0 (0)
002 4 7c54 7804 61e4 60f9 | 10 61e4 (0) 60f9 (0) 636c (0) 7186 (0) 002 4 7c54 7804 61e4 60f9 | 10 61e4 (0) 60f9 (0) 636c (0) 7186 (0)

View file

@ -92,7 +92,7 @@ func TestNetworkID(t *testing.T) {
if kademlias[node].addrs.Size() != len(netIDGroup)-1 { if kademlias[node].addrs.Size() != len(netIDGroup)-1 {
t.Fatalf("Kademlia size has not expected peer size. Kademlia size: %d, expected size: %d", kademlias[node].addrs.Size(), len(netIDGroup)-1) t.Fatalf("Kademlia size has not expected peer size. Kademlia size: %d, expected size: %d", kademlias[node].addrs.Size(), len(netIDGroup)-1)
} }
kademlias[node].EachAddr(nil, 0, func(addr *BzzAddr, _ int, _ bool) bool { kademlias[node].EachAddr(nil, 0, func(addr *BzzAddr, _ int) bool {
found := false found := false
for _, nd := range netIDGroup { for _, nd := range netIDGroup {
if bytes.Equal(kademlias[nd].BaseAddr(), addr.Address()) { if bytes.Equal(kademlias[nd].BaseAddr(), addr.Address()) {
@ -188,7 +188,7 @@ func newServices() adapters.Services {
return k return k
} }
params := NewKadParams() params := NewKadParams()
params.MinProxBinSize = 2 params.NeighbourhoodSize = 2
params.MaxBinSize = 3 params.MaxBinSize = 3
params.MinBinSize = 1 params.MinBinSize = 1
params.MaxRetries = 1000 params.MaxRetries = 1000

View file

@ -81,6 +81,7 @@ func TestPeerEventsTimeout(t *testing.T) {
events := sim.PeerEvents(ctx, sim.NodeIDs()) events := sim.PeerEvents(ctx, sim.NodeIDs())
done := make(chan struct{}) done := make(chan struct{})
errC := make(chan error)
go func() { go func() {
for e := range events { for e := range events {
if e.Error == context.Canceled { if e.Error == context.Canceled {
@ -90,14 +91,16 @@ func TestPeerEventsTimeout(t *testing.T) {
close(done) close(done)
return return
} else { } else {
t.Fatal(e.Error) errC <- e.Error
} }
} }
}() }()
select { select {
case <-time.After(time.Second): case <-time.After(time.Second):
t.Error("no context deadline received") t.Fatal("no context deadline received")
case err := <-errC:
t.Fatal(err)
case <-done: case <-done:
// all good, context deadline detected // all good, context deadline detected
} }

View file

@ -18,8 +18,14 @@ package simulation_test
import ( import (
"context" "context"
"fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/network/simulation" "github.com/ethereum/go-ethereum/swarm/network/simulation"
) )
@ -28,10 +34,6 @@ import (
// all nodes have the their Kademlias healthy. // all nodes have the their Kademlias healthy.
func ExampleSimulation_WaitTillHealthy() { func ExampleSimulation_WaitTillHealthy() {
log.Error("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
/* Commented out to avoid go vet errors/warnings
sim := simulation.New(map[string]simulation.ServiceFunc{ sim := simulation.New(map[string]simulation.ServiceFunc{
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) { "bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
addr := network.NewAddr(ctx.Config.Node()) addr := network.NewAddr(ctx.Config.Node())
@ -59,7 +61,7 @@ func ExampleSimulation_WaitTillHealthy() {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
ill, err := sim.WaitTillHealthy(ctx, 2) ill, err := sim.WaitTillHealthy(ctx)
if err != nil { if err != nil {
// inspect the latest detected not healthy kademlias // inspect the latest detected not healthy kademlias
for id, kad := range ill { for id, kad := range ill {
@ -71,7 +73,6 @@ func ExampleSimulation_WaitTillHealthy() {
// continue with the test // continue with the test
*/
} }
// Watch all peer events in the simulation network, buy receiving from a channel. // Watch all peer events in the simulation network, buy receiving from a channel.

View file

@ -73,7 +73,8 @@ func TestSimulationWithHTTPServer(t *testing.T) {
//this time the timeout should be long enough so that it doesn't kick in too early //this time the timeout should be long enough so that it doesn't kick in too early
ctx, cancel2 := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel2 := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel2() defer cancel2()
go sendRunSignal(t) errC := make(chan error, 1)
go triggerSimulationRun(t, errC)
result = sim.Run(ctx, func(ctx context.Context, sim *Simulation) error { result = sim.Run(ctx, func(ctx context.Context, sim *Simulation) error {
log.Debug("This run waits for the run signal from `frontend`...") log.Debug("This run waits for the run signal from `frontend`...")
//ensure with a Sleep that simulation doesn't terminate before the signal is received //ensure with a Sleep that simulation doesn't terminate before the signal is received
@ -83,10 +84,13 @@ func TestSimulationWithHTTPServer(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Fatal(result.Error) t.Fatal(result.Error)
} }
if err := <-errC; err != nil {
t.Fatal(err)
}
log.Debug("Test terminated successfully") log.Debug("Test terminated successfully")
} }
func sendRunSignal(t *testing.T) { func triggerSimulationRun(t *testing.T, errC chan error) {
//We need to first wait for the sim HTTP server to start running... //We need to first wait for the sim HTTP server to start running...
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
//then we can send the signal //then we can send the signal
@ -94,16 +98,13 @@ func sendRunSignal(t *testing.T) {
log.Debug("Sending run signal to simulation: POST /runsim...") log.Debug("Sending run signal to simulation: POST /runsim...")
resp, err := http.Post(fmt.Sprintf("http://localhost%s/runsim", DefaultHTTPSimAddr), "application/json", nil) resp, err := http.Post(fmt.Sprintf("http://localhost%s/runsim", DefaultHTTPSimAddr), "application/json", nil)
if err != nil { if err != nil {
t.Fatalf("Request failed: %v", err) errC <- fmt.Errorf("Request failed: %v", err)
return
} }
defer func() {
err := resp.Body.Close()
if err != nil {
log.Error("Error closing response body", "err", err)
}
}()
log.Debug("Signal sent") log.Debug("Signal sent")
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
t.Fatalf("err %s", resp.Status) errC <- fmt.Errorf("err %s", resp.Status)
return
} }
errC <- resp.Body.Close()
} }

View file

@ -34,7 +34,7 @@ var BucketKeyKademlia BucketKey = "kademlia"
// WaitTillHealthy is blocking until the health of all kademlias is true. // WaitTillHealthy is blocking until the health of all kademlias is true.
// If error is not nil, a map of kademlia that was found not healthy is returned. // If error is not nil, a map of kademlia that was found not healthy is returned.
// TODO: Check correctness since change in kademlia depth calculation logic // TODO: Check correctness since change in kademlia depth calculation logic
func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (ill map[enode.ID]*network.Kademlia, err error) { func (s *Simulation) WaitTillHealthy(ctx context.Context) (ill map[enode.ID]*network.Kademlia, err error) {
// Prepare PeerPot map for checking Kademlia health // Prepare PeerPot map for checking Kademlia health
var ppmap map[string]*network.PeerPot var ppmap map[string]*network.PeerPot
kademlias := s.kademlias() kademlias := s.kademlias()
@ -43,7 +43,7 @@ func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (i
for _, k := range kademlias { for _, k := range kademlias {
addrs = append(addrs, k.BaseAddr()) addrs = append(addrs, k.BaseAddr())
} }
ppmap = network.NewPeerPotMap(kadMinProxSize, addrs) ppmap = network.NewPeerPotMap(s.neighbourhoodSize, addrs)
// Wait for healthy Kademlia on every node before checking files // Wait for healthy Kademlia on every node before checking files
ticker := time.NewTicker(200 * time.Millisecond) ticker := time.NewTicker(200 * time.Millisecond)

View file

@ -28,6 +28,7 @@ import (
) )
func TestWaitTillHealthy(t *testing.T) { func TestWaitTillHealthy(t *testing.T) {
t.Skip("WaitTillHealthy depends on discovery, which relies on a reliable SuggestPeer, which is not reliable")
sim := New(map[string]ServiceFunc{ sim := New(map[string]ServiceFunc{
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) { "bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
@ -54,7 +55,7 @@ func TestWaitTillHealthy(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel() defer cancel()
ill, err := sim.WaitTillHealthy(ctx, 2) ill, err := sim.WaitTillHealthy(ctx)
if err != nil { if err != nil {
for id, kad := range ill { for id, kad := range ill {
t.Log("Node", id) t.Log("Node", id)

View file

@ -188,7 +188,7 @@ func (s *Simulation) AddNodesAndConnectStar(count int, opts ...AddNodeOption) (i
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.Net.ConnectNodesStar(ids[0], ids[1:]) err = s.Net.ConnectNodesStar(ids[1:], ids[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -241,25 +241,6 @@ func (s *Simulation) UploadSnapshot(snapshotFile string, opts ...AddNodeOption)
return nil return nil
} }
// SetPivotNode sets the NodeID of the network's pivot node.
// Pivot node is just a specific node that should be treated
// differently then other nodes in test. SetPivotNode and
// PivotNodeID are just a convenient functions to set and
// retrieve it.
func (s *Simulation) SetPivotNode(id enode.ID) {
s.mu.Lock()
defer s.mu.Unlock()
s.pivotNodeID = &id
}
// PivotNodeID returns NodeID of the pivot node set by
// Simulation.SetPivotNode method.
func (s *Simulation) PivotNodeID() (id *enode.ID) {
s.mu.Lock()
defer s.mu.Unlock()
return s.pivotNodeID
}
// StartNode starts a node by NodeID. // StartNode starts a node by NodeID.
func (s *Simulation) StartNode(id enode.ID) (err error) { func (s *Simulation) StartNode(id enode.ID) (err error) {
return s.Net.Start(id) return s.Net.Start(id)

View file

@ -314,45 +314,6 @@ func TestUploadSnapshot(t *testing.T) {
log.Debug("Done.") log.Debug("Done.")
} }
func TestPivotNode(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
id, err := sim.AddNode()
if err != nil {
t.Fatal(err)
}
id2, err := sim.AddNode()
if err != nil {
t.Fatal(err)
}
if sim.PivotNodeID() != nil {
t.Error("expected no pivot node")
}
sim.SetPivotNode(id)
pid := sim.PivotNodeID()
if pid == nil {
t.Error("pivot node not set")
} else if *pid != id {
t.Errorf("expected pivot node %s, got %s", id, *pid)
}
sim.SetPivotNode(id2)
pid = sim.PivotNodeID()
if pid == nil {
t.Error("pivot node not set")
} else if *pid != id2 {
t.Errorf("expected pivot node %s, got %s", id2, *pid)
}
}
func TestStartStopNode(t *testing.T) { func TestStartStopNode(t *testing.T) {
sim := New(noopServiceFuncMap) sim := New(noopServiceFuncMap)
defer sim.Close() defer sim.Close()

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/swarm/network"
) )
// Common errors that are returned by functions in this package. // Common errors that are returned by functions in this package.
@ -45,10 +46,10 @@ type Simulation struct {
serviceNames []string serviceNames []string
cleanupFuncs []func() cleanupFuncs []func()
buckets map[enode.ID]*sync.Map buckets map[enode.ID]*sync.Map
pivotNodeID *enode.ID
shutdownWG sync.WaitGroup shutdownWG sync.WaitGroup
done chan struct{} done chan struct{}
mu sync.RWMutex mu sync.RWMutex
neighbourhoodSize int
httpSrv *http.Server //attach a HTTP server via SimulationOptions httpSrv *http.Server //attach a HTTP server via SimulationOptions
handler *simulations.Server //HTTP handler for the server handler *simulations.Server //HTTP handler for the server
@ -74,6 +75,7 @@ func New(services map[string]ServiceFunc) (s *Simulation) {
s = &Simulation{ s = &Simulation{
buckets: make(map[enode.ID]*sync.Map), buckets: make(map[enode.ID]*sync.Map),
done: make(chan struct{}), done: make(chan struct{}),
neighbourhoodSize: network.NewKadParams().NeighbourhoodSize,
} }
adapterServices := make(map[string]adapters.ServiceFunc, len(services)) adapterServices := make(map[string]adapters.ServiceFunc, len(services))

View file

@ -46,7 +46,7 @@ import (
// serviceName is used with the exec adapter so the exec'd binary knows which // serviceName is used with the exec adapter so the exec'd binary knows which
// service to execute // service to execute
const serviceName = "discovery" const serviceName = "discovery"
const testMinProxBinSize = 2 const testNeighbourhoodSize = 2
const discoveryPersistenceDatadir = "discovery_persistence_test_store" const discoveryPersistenceDatadir = "discovery_persistence_test_store"
var discoveryPersistencePath = path.Join(os.TempDir(), discoveryPersistenceDatadir) var discoveryPersistencePath = path.Join(os.TempDir(), discoveryPersistenceDatadir)
@ -268,7 +268,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
wg.Wait() wg.Wait()
log.Debug(fmt.Sprintf("nodes: %v", len(addrs))) log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
// construct the peer pot, so that kademlia health can be checked // construct the peer pot, so that kademlia health can be checked
ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, addrs) ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
check := func(ctx context.Context, id enode.ID) (bool, error) { check := func(ctx context.Context, id enode.ID) (bool, error) {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -404,7 +404,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
} }
healthy := &network.Health{} healthy := &network.Health{}
addr := id.String() addr := id.String()
ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, addrs) ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil { if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
return fmt.Errorf("error getting node health: %s", err) return fmt.Errorf("error getting node health: %s", err)
} }
@ -492,7 +492,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
return false, fmt.Errorf("error getting node client: %s", err) return false, fmt.Errorf("error getting node client: %s", err)
} }
healthy := &network.Health{} healthy := &network.Health{}
ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, addrs) ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil { if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
return false, fmt.Errorf("error getting node health: %s", err) return false, fmt.Errorf("error getting node health: %s", err)
@ -566,7 +566,7 @@ func newService(ctx *adapters.ServiceContext) (node.Service, error) {
addr := network.NewAddr(ctx.Config.Node()) addr := network.NewAddr(ctx.Config.Node())
kp := network.NewKadParams() kp := network.NewKadParams()
kp.MinProxBinSize = testMinProxBinSize kp.NeighbourhoodSize = testNeighbourhoodSize
if ctx.Config.Reachable != nil { if ctx.Config.Reachable != nil {
kp.Reachable = func(o *network.BzzAddr) bool { kp.Reachable = func(o *network.BzzAddr) bool {

View file

@ -86,7 +86,7 @@ func (s *Simulation) NewService(ctx *adapters.ServiceContext) (node.Service, err
addr := network.NewAddr(node) addr := network.NewAddr(node)
kp := network.NewKadParams() kp := network.NewKadParams()
kp.MinProxBinSize = 2 kp.NeighbourhoodSize = 2
kp.MaxBinSize = 4 kp.MaxBinSize = 4
kp.MinBinSize = 1 kp.MinBinSize = 1
kp.MaxRetries = 1000 kp.MaxRetries = 1000

View file

@ -19,7 +19,6 @@ package stream
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
@ -245,7 +244,7 @@ func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (
return nil, nil, fmt.Errorf("source peer %v not found", spID.String()) return nil, nil, fmt.Errorf("source peer %v not found", spID.String())
} }
} else { } else {
d.kad.EachConn(req.Addr[:], 255, func(p *network.Peer, po int, nn bool) bool { d.kad.EachConn(req.Addr[:], 255, func(p *network.Peer, po int) bool {
id := p.ID() id := p.ID()
if p.LightNode { if p.LightNode {
// skip light nodes // skip light nodes

View file

@ -19,9 +19,11 @@ package stream
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -500,7 +502,7 @@ func testDeliveryFromNodes(t *testing.T, nodes, chunkCount int, skipCheck bool)
log.Info("Starting simulation") log.Info("Starting simulation")
ctx := context.Background() ctx := context.Background()
result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) (err error) {
nodeIDs := sim.UpNodeIDs() nodeIDs := sim.UpNodeIDs()
//determine the pivot node to be the first node of the simulation //determine the pivot node to be the first node of the simulation
pivot := nodeIDs[0] pivot := nodeIDs[0]
@ -542,7 +544,7 @@ func testDeliveryFromNodes(t *testing.T, nodes, chunkCount int, skipCheck bool)
log.Debug("Waiting for kademlia") log.Debug("Waiting for kademlia")
// TODO this does not seem to be correct usage of the function, as the simulation may have no kademlias // TODO this does not seem to be correct usage of the function, as the simulation may have no kademlias
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }
@ -553,14 +555,13 @@ func testDeliveryFromNodes(t *testing.T, nodes, chunkCount int, skipCheck bool)
} }
pivotFileStore := item.(*storage.FileStore) pivotFileStore := item.(*storage.FileStore)
log.Debug("Starting retrieval routine") log.Debug("Starting retrieval routine")
retErrC := make(chan error)
go func() { go func() {
// start the retrieval on the pivot node - this will spawn retrieve requests for missing chunks // start the retrieval on the pivot node - this will spawn retrieve requests for missing chunks
// we must wait for the peer connections to have started before requesting // we must wait for the peer connections to have started before requesting
n, err := readAll(pivotFileStore, fileHash) n, err := readAll(pivotFileStore, fileHash)
log.Info(fmt.Sprintf("retrieved %v", fileHash), "read", n, "err", err) log.Info(fmt.Sprintf("retrieved %v", fileHash), "read", n, "err", err)
if err != nil { retErrC <- err
t.Fatalf("requesting chunks action error: %v", err)
}
}() }()
log.Debug("Watching for disconnections") log.Debug("Watching for disconnections")
@ -570,11 +571,19 @@ func testDeliveryFromNodes(t *testing.T, nodes, chunkCount int, skipCheck bool)
simulation.NewPeerEventsFilter().Drop(), simulation.NewPeerEventsFilter().Drop(),
) )
var disconnected atomic.Value
go func() { go func() {
for d := range disconnections { for d := range disconnections {
if d.Error != nil { if d.Error != nil {
log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
t.Fatal(d.Error) disconnected.Store(true)
}
}
}()
defer func() {
if err != nil {
if yes, ok := disconnected.Load().(bool); ok && yes {
err = errors.New("disconnect events received")
} }
} }
}() }()
@ -595,6 +604,9 @@ func testDeliveryFromNodes(t *testing.T, nodes, chunkCount int, skipCheck bool)
if !success { if !success {
return fmt.Errorf("Test failed, chunks not available on all nodes") return fmt.Errorf("Test failed, chunks not available on all nodes")
} }
if err := <-retErrC; err != nil {
t.Fatalf("requesting chunks: %v", err)
}
log.Debug("Test terminated successfully") log.Debug("Test terminated successfully")
return nil return nil
}) })
@ -675,7 +687,7 @@ func benchmarkDeliveryFromNodes(b *testing.B, nodes, chunkCount int, skipCheck b
} }
ctx := context.Background() ctx := context.Background()
result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) (err error) {
nodeIDs := sim.UpNodeIDs() nodeIDs := sim.UpNodeIDs()
node := nodeIDs[len(nodeIDs)-1] node := nodeIDs[len(nodeIDs)-1]
@ -692,7 +704,7 @@ func benchmarkDeliveryFromNodes(b *testing.B, nodes, chunkCount int, skipCheck b
} }
netStore := item.(*storage.NetStore) netStore := item.(*storage.NetStore)
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }
@ -702,11 +714,19 @@ func benchmarkDeliveryFromNodes(b *testing.B, nodes, chunkCount int, skipCheck b
simulation.NewPeerEventsFilter().Drop(), simulation.NewPeerEventsFilter().Drop(),
) )
var disconnected atomic.Value
go func() { go func() {
for d := range disconnections { for d := range disconnections {
if d.Error != nil { if d.Error != nil {
log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
b.Fatal(d.Error) disconnected.Store(true)
}
}
}()
defer func() {
if err != nil {
if yes, ok := disconnected.Load().(bool); ok && yes {
err = errors.New("disconnect events received")
} }
} }
}() }()

View file

@ -19,9 +19,11 @@ package stream
import ( import (
"context" "context"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"os" "os"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -113,11 +115,11 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel() defer cancel()
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
t.Fatal(err) t.Fatal(err)
} }
result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) (err error) {
nodeIDs := sim.UpNodeIDs() nodeIDs := sim.UpNodeIDs()
storer := nodeIDs[0] storer := nodeIDs[0]
checker := nodeIDs[1] checker := nodeIDs[1]
@ -162,11 +164,19 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
return err return err
} }
var disconnected atomic.Value
go func() { go func() {
for d := range disconnections { for d := range disconnections {
if d.Error != nil { if d.Error != nil {
log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
t.Fatal(d.Error) disconnected.Store(true)
}
}
}()
defer func() {
if err != nil {
if yes, ok := disconnected.Load().(bool); ok && yes {
err = errors.New("disconnect events received")
} }
} }
}() }()

View file

@ -336,7 +336,7 @@ func (p *Peer) handleWantedHashesMsg(ctx context.Context, req *WantedHashesMsg)
// launch in go routine since GetBatch blocks until new hashes arrive // launch in go routine since GetBatch blocks until new hashes arrive
go func() { go func() {
if err := p.SendOfferedHashes(s, req.From, req.To); err != nil { if err := p.SendOfferedHashes(s, req.From, req.To); err != nil {
log.Warn("SendOfferedHashes error", "err", err) log.Warn("SendOfferedHashes error", "peer", p.ID().TerminalString(), "err", err)
} }
}() }()
// go p.SendOfferedHashes(s, req.From, req.To) // go p.SendOfferedHashes(s, req.From, req.To)

View file

@ -197,7 +197,7 @@ func runFileRetrievalTest(nodeCount int) error {
if err != nil { if err != nil {
return err return err
} }
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }
@ -287,7 +287,7 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
if err != nil { if err != nil {
return err return err
} }
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }

View file

@ -21,6 +21,7 @@ import (
"os" "os"
"runtime" "runtime"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -203,7 +204,7 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
ctx, cancelSimRun := context.WithTimeout(context.Background(), 2*time.Minute) ctx, cancelSimRun := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancelSimRun() defer cancelSimRun()
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -213,11 +214,13 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
simulation.NewPeerEventsFilter().Drop(), simulation.NewPeerEventsFilter().Drop(),
) )
var disconnected atomic.Value
go func() { go func() {
for d := range disconnections { for d := range disconnections {
if d.Error != nil {
log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
t.Fatal("unexpected disconnect") disconnected.Store(true)
cancelSimRun() }
} }
}() }()
@ -226,6 +229,9 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
if result.Error != nil { if result.Error != nil {
t.Fatal(result.Error) t.Fatal(result.Error)
} }
if yes, ok := disconnected.Load().(bool); ok && yes {
t.Fatal("disconnect events received")
}
log.Info("Simulation ended") log.Info("Simulation ended")
} }
@ -385,7 +391,7 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
return err return err
} }
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }
@ -395,11 +401,13 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
simulation.NewPeerEventsFilter().Drop(), simulation.NewPeerEventsFilter().Drop(),
) )
var disconnected atomic.Value
go func() { go func() {
for d := range disconnections { for d := range disconnections {
if d.Error != nil {
log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
t.Fatal("unexpected disconnect") disconnected.Store(true)
cancelSimRun() }
} }
}() }()
@ -463,7 +471,7 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
conf.hashes = append(conf.hashes, hashes...) conf.hashes = append(conf.hashes, hashes...)
mapKeysToNodes(conf) mapKeysToNodes(conf)
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }
@ -514,6 +522,9 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
return result.Error return result.Error
} }
if yes, ok := disconnected.Load().(bool); ok && yes {
t.Fatal("disconnect events received")
}
log.Info("Simulation ended") log.Info("Simulation ended")
return nil return nil
} }
@ -552,7 +563,7 @@ func mapKeysToNodes(conf *synctestConfig) {
np, _, _ = pot.Add(np, a, pof) np, _, _ = pot.Add(np, a, pof)
} }
ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, conf.addrs) ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, conf.addrs)
//for each address, run EachNeighbour on the chunk hashes pot to identify closest nodes //for each address, run EachNeighbour on the chunk hashes pot to identify closest nodes
log.Trace(fmt.Sprintf("Generated hash chunk(s): %v", conf.hashes)) log.Trace(fmt.Sprintf("Generated hash chunk(s): %v", conf.hashes))

View file

@ -48,28 +48,28 @@ const (
HashSize = 32 HashSize = 32
) )
//Enumerate options for syncing and retrieval // Enumerate options for syncing and retrieval
type SyncingOption int type SyncingOption int
type RetrievalOption int type RetrievalOption int
//Syncing options // Syncing options
const ( const (
//Syncing disabled // Syncing disabled
SyncingDisabled SyncingOption = iota SyncingDisabled SyncingOption = iota
//Register the client and the server but not subscribe // Register the client and the server but not subscribe
SyncingRegisterOnly SyncingRegisterOnly
//Both client and server funcs are registered, subscribe sent automatically // Both client and server funcs are registered, subscribe sent automatically
SyncingAutoSubscribe SyncingAutoSubscribe
) )
const ( const (
//Retrieval disabled. Used mostly for tests to isolate syncing features (i.e. syncing only) // Retrieval disabled. Used mostly for tests to isolate syncing features (i.e. syncing only)
RetrievalDisabled RetrievalOption = iota RetrievalDisabled RetrievalOption = iota
//Only the client side of the retrieve request is registered. // Only the client side of the retrieve request is registered.
//(light nodes do not serve retrieve requests) // (light nodes do not serve retrieve requests)
//once the client is registered, subscription to retrieve request stream is always sent // once the client is registered, subscription to retrieve request stream is always sent
RetrievalClientOnly RetrievalClientOnly
//Both client and server funcs are registered, subscribe sent automatically // Both client and server funcs are registered, subscribe sent automatically
RetrievalEnabled RetrievalEnabled
) )
@ -86,18 +86,18 @@ type Registry struct {
peers map[enode.ID]*Peer peers map[enode.ID]*Peer
delivery *Delivery delivery *Delivery
intervalsStore state.Store intervalsStore state.Store
autoRetrieval bool //automatically subscribe to retrieve request stream autoRetrieval bool // automatically subscribe to retrieve request stream
maxPeerServers int maxPeerServers int
spec *protocols.Spec //this protocol's spec balance protocols.Balance // implements protocols.Balance, for accounting
balance protocols.Balance //implements protocols.Balance, for accounting prices protocols.Prices // implements protocols.Prices, provides prices to accounting
prices protocols.Prices //implements protocols.Prices, provides prices to accounting spec *protocols.Spec // this protocol's spec
} }
// RegistryOptions holds optional values for NewRegistry constructor. // RegistryOptions holds optional values for NewRegistry constructor.
type RegistryOptions struct { type RegistryOptions struct {
SkipCheck bool SkipCheck bool
Syncing SyncingOption //Defines syncing behavior Syncing SyncingOption // Defines syncing behavior
Retrieval RetrievalOption //Defines retrieval behavior Retrieval RetrievalOption // Defines retrieval behavior
SyncUpdateDelay time.Duration SyncUpdateDelay time.Duration
MaxPeerServers int // The limit of servers for each peer in registry MaxPeerServers int // The limit of servers for each peer in registry
} }
@ -110,7 +110,7 @@ func NewRegistry(localID enode.ID, delivery *Delivery, syncChunkStore storage.Sy
if options.SyncUpdateDelay <= 0 { if options.SyncUpdateDelay <= 0 {
options.SyncUpdateDelay = 15 * time.Second options.SyncUpdateDelay = 15 * time.Second
} }
//check if retriaval has been disabled // check if retrieval has been disabled
retrieval := options.Retrieval != RetrievalDisabled retrieval := options.Retrieval != RetrievalDisabled
streamer := &Registry{ streamer := &Registry{
@ -130,7 +130,7 @@ func NewRegistry(localID enode.ID, delivery *Delivery, syncChunkStore storage.Sy
streamer.api = NewAPI(streamer) streamer.api = NewAPI(streamer)
delivery.getPeer = streamer.getPeer delivery.getPeer = streamer.getPeer
//if retrieval is enabled, register the server func, so that retrieve requests will be served (non-light nodes only) // if retrieval is enabled, register the server func, so that retrieve requests will be served (non-light nodes only)
if options.Retrieval == RetrievalEnabled { if options.Retrieval == RetrievalEnabled {
streamer.RegisterServerFunc(swarmChunkServerStreamName, func(_ *Peer, _ string, live bool) (Server, error) { streamer.RegisterServerFunc(swarmChunkServerStreamName, func(_ *Peer, _ string, live bool) (Server, error) {
if !live { if !live {
@ -140,20 +140,20 @@ func NewRegistry(localID enode.ID, delivery *Delivery, syncChunkStore storage.Sy
}) })
} }
//if retrieval is not disabled, register the client func (both light nodes and normal nodes can issue retrieve requests) // if retrieval is not disabled, register the client func (both light nodes and normal nodes can issue retrieve requests)
if options.Retrieval != RetrievalDisabled { if options.Retrieval != RetrievalDisabled {
streamer.RegisterClientFunc(swarmChunkServerStreamName, func(p *Peer, t string, live bool) (Client, error) { streamer.RegisterClientFunc(swarmChunkServerStreamName, func(p *Peer, t string, live bool) (Client, error) {
return NewSwarmSyncerClient(p, syncChunkStore, NewStream(swarmChunkServerStreamName, t, live)) return NewSwarmSyncerClient(p, syncChunkStore, NewStream(swarmChunkServerStreamName, t, live))
}) })
} }
//If syncing is not disabled, the syncing functions are registered (both client and server) // If syncing is not disabled, the syncing functions are registered (both client and server)
if options.Syncing != SyncingDisabled { if options.Syncing != SyncingDisabled {
RegisterSwarmSyncerServer(streamer, syncChunkStore) RegisterSwarmSyncerServer(streamer, syncChunkStore)
RegisterSwarmSyncerClient(streamer, syncChunkStore) RegisterSwarmSyncerClient(streamer, syncChunkStore)
} }
//if syncing is set to automatically subscribe to the syncing stream, start the subscription process // if syncing is set to automatically subscribe to the syncing stream, start the subscription process
if options.Syncing == SyncingAutoSubscribe { if options.Syncing == SyncingAutoSubscribe {
// latestIntC function ensures that // latestIntC function ensures that
// - receiving from the in chan is not blocked by processing inside the for loop // - receiving from the in chan is not blocked by processing inside the for loop
@ -235,13 +235,17 @@ func NewRegistry(localID enode.ID, delivery *Delivery, syncChunkStore storage.Sy
return streamer return streamer
} }
//we need to construct a spec instance per node instance // This is an accounted protocol, therefore we need to provide a pricing Hook to the spec
// For simulations to be able to run multiple nodes and not override the hook's balance,
// we need to construct a spec instance per node instance
func (r *Registry) setupSpec() { func (r *Registry) setupSpec() {
//first create the "bare" spec // first create the "bare" spec
r.createSpec() r.createSpec()
//if balance is nil, this node has been started without swap support (swapEnabled flag is false) // now create the pricing object
r.createPriceOracle()
// if balance is nil, this node has been started without swap support (swapEnabled flag is false)
if r.balance != nil && !reflect.ValueOf(r.balance).IsNil() { if r.balance != nil && !reflect.ValueOf(r.balance).IsNil() {
//swap is enabled, so setup the hook // swap is enabled, so setup the hook
r.spec.Hook = protocols.NewAccounting(r.balance, r.prices) r.spec.Hook = protocols.NewAccounting(r.balance, r.prices)
} }
} }
@ -533,11 +537,11 @@ func (p *Peer) HandleMsg(ctx context.Context, msg interface{}) error {
return p.handleWantedHashesMsg(ctx, msg) return p.handleWantedHashesMsg(ctx, msg)
case *ChunkDeliveryMsgRetrieval: case *ChunkDeliveryMsgRetrieval:
//handling chunk delivery is the same for retrieval and syncing, so let's cast the msg // handling chunk delivery is the same for retrieval and syncing, so let's cast the msg
return p.streamer.delivery.handleChunkDeliveryMsg(ctx, p, ((*ChunkDeliveryMsg)(msg))) return p.streamer.delivery.handleChunkDeliveryMsg(ctx, p, ((*ChunkDeliveryMsg)(msg)))
case *ChunkDeliveryMsgSyncing: case *ChunkDeliveryMsgSyncing:
//handling chunk delivery is the same for retrieval and syncing, so let's cast the msg // handling chunk delivery is the same for retrieval and syncing, so let's cast the msg
return p.streamer.delivery.handleChunkDeliveryMsg(ctx, p, ((*ChunkDeliveryMsg)(msg))) return p.streamer.delivery.handleChunkDeliveryMsg(ctx, p, ((*ChunkDeliveryMsg)(msg)))
case *RetrieveRequestMsg: case *RetrieveRequestMsg:
@ -726,9 +730,9 @@ func (c *clientParams) clientCreated() {
close(c.clientCreatedC) close(c.clientCreatedC)
} }
//GetSpec returns the streamer spec to callers // GetSpec returns the streamer spec to callers
//This used to be a global variable but for simulations with // This used to be a global variable but for simulations with
//multiple nodes its fields (notably the Hook) would be overwritten // multiple nodes its fields (notably the Hook) would be overwritten
func (r *Registry) GetSpec() *protocols.Spec { func (r *Registry) GetSpec() *protocols.Spec {
return r.spec return r.spec
} }
@ -756,6 +760,52 @@ func (r *Registry) createSpec() {
r.spec = spec r.spec = spec
} }
// An accountable message needs some meta information attached to it
// in order to evaluate the correct price
type StreamerPrices struct {
priceMatrix map[reflect.Type]*protocols.Price
registry *Registry
}
// Price implements the accounting interface and returns the price for a specific message
func (sp *StreamerPrices) Price(msg interface{}) *protocols.Price {
t := reflect.TypeOf(msg).Elem()
return sp.priceMatrix[t]
}
// Instead of hardcoding the price, get it
// through a function - it could be quite complex in the future
func (sp *StreamerPrices) getRetrieveRequestMsgPrice() uint64 {
return uint64(1)
}
// Instead of hardcoding the price, get it
// through a function - it could be quite complex in the future
func (sp *StreamerPrices) getChunkDeliveryMsgRetrievalPrice() uint64 {
return uint64(1)
}
// createPriceOracle sets up a matrix which can be queried to get
// the price for a message via the Price method
func (r *Registry) createPriceOracle() {
sp := &StreamerPrices{
registry: r,
}
sp.priceMatrix = map[reflect.Type]*protocols.Price{
reflect.TypeOf(ChunkDeliveryMsgRetrieval{}): {
Value: sp.getChunkDeliveryMsgRetrievalPrice(), // arbitrary price for now
PerByte: true,
Payer: protocols.Receiver,
},
reflect.TypeOf(RetrieveRequestMsg{}): {
Value: sp.getRetrieveRequestMsgPrice(), // arbitrary price for now
PerByte: false,
Payer: protocols.Sender,
},
}
r.prices = sp
}
func (r *Registry) Protocols() []p2p.Protocol { func (r *Registry) Protocols() []p2p.Protocol {
return []p2p.Protocol{ return []p2p.Protocol{
{ {

View file

@ -921,3 +921,34 @@ func TestMaxPeerServersWithoutUnsubscribe(t *testing.T) {
} }
} }
} }
//TestHasPriceImplementation is to check that the Registry has a
//`Price` interface implementation
func TestHasPriceImplementation(t *testing.T) {
_, r, _, teardown, err := newStreamerTester(t, &RegistryOptions{
Retrieval: RetrievalDisabled,
Syncing: SyncingDisabled,
})
defer teardown()
if err != nil {
t.Fatal(err)
}
if r.prices == nil {
t.Fatal("No prices implementation available for the stream protocol")
}
pricesInstance, ok := r.prices.(*StreamerPrices)
if !ok {
t.Fatal("`Registry` does not have the expected Prices instance")
}
price := pricesInstance.Price(&ChunkDeliveryMsgRetrieval{})
if price == nil || price.Value == 0 || price.Value != pricesInstance.getChunkDeliveryMsgRetrievalPrice() {
t.Fatal("No prices set for chunk delivery msg")
}
price = pricesInstance.Price(&RetrieveRequestMsg{})
if price == nil || price.Value == 0 || price.Value != pricesInstance.getRetrieveRequestMsgPrice() {
t.Fatal("No prices set for chunk delivery msg")
}
}

View file

@ -18,11 +18,13 @@ package stream
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math" "math"
"os" "os"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -129,7 +131,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, chunkCount int, skipCheck bool, p
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) (err error) {
nodeIDs := sim.UpNodeIDs() nodeIDs := sim.UpNodeIDs()
nodeIndex := make(map[enode.ID]int) nodeIndex := make(map[enode.ID]int)
@ -143,11 +145,19 @@ func testSyncBetweenNodes(t *testing.T, nodes, chunkCount int, skipCheck bool, p
simulation.NewPeerEventsFilter().Drop(), simulation.NewPeerEventsFilter().Drop(),
) )
var disconnected atomic.Value
go func() { go func() {
for d := range disconnections { for d := range disconnections {
if d.Error != nil { if d.Error != nil {
log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
t.Fatal(d.Error) disconnected.Store(true)
}
}
}()
defer func() {
if err != nil {
if yes, ok := disconnected.Load().(bool); ok && yes {
err = errors.New("disconnect events received")
} }
} }
}() }()
@ -179,7 +189,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, chunkCount int, skipCheck bool, p
} }
} }
// here we distribute chunks of a random file into stores 1...nodes // here we distribute chunks of a random file into stores 1...nodes
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }

View file

@ -72,7 +72,7 @@ func setupSim(serviceMap map[string]simulation.ServiceFunc) (int, int, *simulati
func watchSim(sim *simulation.Simulation) (context.Context, context.CancelFunc) { func watchSim(sim *simulation.Simulation) (context.Context, context.CancelFunc) {
ctx, cancelSimRun := context.WithTimeout(context.Background(), 1*time.Minute) ctx, cancelSimRun := context.WithTimeout(context.Background(), 1*time.Minute)
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
panic(err) panic(err)
} }

View file

@ -353,7 +353,7 @@ func testSwarmNetwork(t *testing.T, o *testSwarmNetworkOptions, steps ...testSwa
} }
if *waitKademlia { if *waitKademlia {
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx); err != nil {
return err return err
} }
} }

View file

@ -238,7 +238,7 @@ func newServices() adapters.Services {
return k return k
} }
params := network.NewKadParams() params := network.NewKadParams()
params.MinProxBinSize = 2 params.NeighbourhoodSize = 2
params.MaxBinSize = 3 params.MaxBinSize = 3
params.MinBinSize = 1 params.MinBinSize = 1
params.MaxRetries = 1000 params.MaxRetries = 1000

View file

@ -209,7 +209,7 @@ func newServices(allowRaw bool) adapters.Services {
return k return k
} }
params := network.NewKadParams() params := network.NewKadParams()
params.MinProxBinSize = 2 params.NeighbourhoodSize = 2
params.MaxBinSize = 3 params.MaxBinSize = 3
params.MinBinSize = 1 params.MinBinSize = 1
params.MaxRetries = 1000 params.MaxRetries = 1000

View file

@ -964,7 +964,7 @@ func (p *Pss) forward(msg *PssMsg) error {
onlySendOnce = true onlySendOnce = true
} }
p.Kademlia.EachConn(to, addressLength*8, func(sp *network.Peer, po int, _ bool) bool { p.Kademlia.EachConn(to, addressLength*8, func(sp *network.Peer, po int) bool {
if po < broadcastThreshold && sent > 0 { if po < broadcastThreshold && sent > 0 {
return false // stop iterating return false // stop iterating
} }

View file

@ -491,12 +491,12 @@ func TestAddressMatchProx(t *testing.T) {
// meanwhile test regression for kademlia since we are compiling the test parameters from different packages // meanwhile test regression for kademlia since we are compiling the test parameters from different packages
var proxes int var proxes int
var conns int var conns int
kad.EachConn(nil, peerCount, func(p *network.Peer, po int, prox bool) bool { depth := kad.NeighbourhoodDepth()
kad.EachConn(nil, peerCount, func(p *network.Peer, po int) bool {
conns++ conns++
if prox { if po >= depth {
proxes++ proxes++
} }
log.Trace("kadconn", "po", po, "peer", p, "prox", prox)
return true return true
}) })
if proxes != nnPeerCount { if proxes != nnPeerCount {
@ -1965,7 +1965,7 @@ func newServices(allowRaw bool) adapters.Services {
return k return k
} }
params := network.NewKadParams() params := network.NewKadParams()
params.MinProxBinSize = 2 params.NeighbourhoodSize = 2
params.MaxBinSize = 3 params.MaxBinSize = 3
params.MinBinSize = 1 params.MinBinSize = 1
params.MaxRetries = 1000 params.MaxRetries = 1000
@ -2045,7 +2045,7 @@ func newTestPss(privkey *ecdsa.PrivateKey, kad *network.Kademlia, ppextra *PssPa
// set up routing if kademlia is not passed to us // set up routing if kademlia is not passed to us
if kad == nil { if kad == nil {
kp := network.NewKadParams() kp := network.NewKadParams()
kp.MinProxBinSize = 3 kp.NeighbourhoodSize = 3
kad = network.NewKademlia(nid[:], kp) kad = network.NewKademlia(nid[:], kp)
} }

View file

@ -196,17 +196,22 @@ func ImportExport(t *testing.T, outStore, inStore mock.GlobalStorer, n int) {
r, w := io.Pipe() r, w := io.Pipe()
defer r.Close() defer r.Close()
exportErrChan := make(chan error)
go func() { go func() {
defer w.Close() defer w.Close()
if _, err := exporter.Export(w); err != nil {
t.Fatalf("export: %v", err) _, err := exporter.Export(w)
} exportErrChan <- err
}() }()
if _, err := importer.Import(r); err != nil { if _, err := importer.Import(r); err != nil {
t.Fatalf("import: %v", err) t.Fatalf("import: %v", err)
} }
if err := <-exportErrChan; err != nil {
t.Fatalf("export: %v", err)
}
for i, addr := range addrs { for i, addr := range addrs {
chunkAddr := storage.Address(append(addr[:], []byte(strconv.FormatInt(int64(i)+1, 16))...)) chunkAddr := storage.Address(append(addr[:], []byte(strconv.FormatInt(int64(i)+1, 16))...))
data := []byte(strconv.FormatInt(int64(i)+1, 16)) data := []byte(strconv.FormatInt(int64(i)+1, 16))

View file

@ -20,6 +20,8 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/rand" "crypto/rand"
"errors"
"fmt"
"io/ioutil" "io/ioutil"
"sync" "sync"
"testing" "testing"
@ -114,19 +116,24 @@ func TestNetStoreGetAndPut(t *testing.T) {
defer cancel() defer cancel()
c := make(chan struct{}) // this channel ensures that the gouroutine with the Put does not run earlier than the Get c := make(chan struct{}) // this channel ensures that the gouroutine with the Put does not run earlier than the Get
putErrC := make(chan error)
go func() { go func() {
<-c // wait for the Get to be called <-c // wait for the Get to be called
time.Sleep(200 * time.Millisecond) // and a little more so it is surely called time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
// check if netStore created a fetcher in the Get call for the unavailable chunk // check if netStore created a fetcher in the Get call for the unavailable chunk
if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
t.Fatal("Expected netStore to use a fetcher for the Get call") putErrC <- errors.New("Expected netStore to use a fetcher for the Get call")
return
} }
err := netStore.Put(ctx, chunk) err := netStore.Put(ctx, chunk)
if err != nil { if err != nil {
t.Fatalf("Expected no err got %v", err) putErrC <- fmt.Errorf("Expected no err got %v", err)
return
} }
putErrC <- nil
}() }()
close(c) close(c)
@ -134,6 +141,10 @@ func TestNetStoreGetAndPut(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Expected no err got %v", err) t.Fatalf("Expected no err got %v", err)
} }
if err := <-putErrC; err != nil {
t.Fatal(err)
}
// the retrieved chunk should be the same as what we Put // the retrieved chunk should be the same as what we Put
if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) { if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
t.Fatalf("Different chunk received than what was put") t.Fatalf("Different chunk received than what was put")
@ -200,14 +211,18 @@ func TestNetStoreGetTimeout(t *testing.T) {
defer cancel() defer cancel()
c := make(chan struct{}) // this channel ensures that the gouroutine does not run earlier than the Get c := make(chan struct{}) // this channel ensures that the gouroutine does not run earlier than the Get
fetcherErrC := make(chan error)
go func() { go func() {
<-c // wait for the Get to be called <-c // wait for the Get to be called
time.Sleep(200 * time.Millisecond) // and a little more so it is surely called time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
// check if netStore created a fetcher in the Get call for the unavailable chunk // check if netStore created a fetcher in the Get call for the unavailable chunk
if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
t.Fatal("Expected netStore to use a fetcher for the Get call") fetcherErrC <- errors.New("Expected netStore to use a fetcher for the Get call")
return
} }
fetcherErrC <- nil
}() }()
close(c) close(c)
@ -220,6 +235,10 @@ func TestNetStoreGetTimeout(t *testing.T) {
t.Fatalf("Expected context.DeadLineExceeded err got %v", err) t.Fatalf("Expected context.DeadLineExceeded err got %v", err)
} }
if err := <-fetcherErrC; err != nil {
t.Fatal(err)
}
// A fetcher was created, check if it has been removed after timeout // A fetcher was created, check if it has been removed after timeout
if netStore.fetchers.Len() != 0 { if netStore.fetchers.Len() != 0 {
t.Fatal("Expected netStore to remove the fetcher after timeout") t.Fatal("Expected netStore to remove the fetcher after timeout")
@ -243,20 +262,29 @@ func TestNetStoreGetCancel(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
c := make(chan struct{}) // this channel ensures that the gouroutine with the cancel does not run earlier than the Get c := make(chan struct{}) // this channel ensures that the gouroutine with the cancel does not run earlier than the Get
fetcherErrC := make(chan error, 1)
go func() { go func() {
<-c // wait for the Get to be called <-c // wait for the Get to be called
time.Sleep(200 * time.Millisecond) // and a little more so it is surely called time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
// check if netStore created a fetcher in the Get call for the unavailable chunk // check if netStore created a fetcher in the Get call for the unavailable chunk
if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
t.Fatal("Expected netStore to use a fetcher for the Get call") fetcherErrC <- errors.New("Expected netStore to use a fetcher for the Get call")
return
} }
fetcherErrC <- nil
cancel() cancel()
}() }()
close(c) close(c)
// We call Get with an unavailable chunk, so it will create a fetcher and wait for delivery // We call Get with an unavailable chunk, so it will create a fetcher and wait for delivery
_, err := netStore.Get(ctx, chunk.Address()) _, err := netStore.Get(ctx, chunk.Address())
if err := <-fetcherErrC; err != nil {
t.Fatal(err)
}
// After the context is cancelled above Get should return with an error // After the context is cancelled above Get should return with an error
if err != context.Canceled { if err != context.Canceled {
t.Fatalf("Expected context.Canceled err got %v", err) t.Fatalf("Expected context.Canceled err got %v", err)
@ -286,47 +314,56 @@ func TestNetStoreMultipleGetAndPut(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel() defer cancel()
putErrC := make(chan error)
go func() { go func() {
// sleep to make sure Put is called after all the Get // sleep to make sure Put is called after all the Get
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
// check if netStore created exactly one fetcher for all Get calls // check if netStore created exactly one fetcher for all Get calls
if netStore.fetchers.Len() != 1 { if netStore.fetchers.Len() != 1 {
t.Fatal("Expected netStore to use one fetcher for all Get calls") putErrC <- errors.New("Expected netStore to use one fetcher for all Get calls")
return
} }
err := netStore.Put(ctx, chunk) err := netStore.Put(ctx, chunk)
if err != nil { if err != nil {
t.Fatalf("Expected no err got %v", err) putErrC <- fmt.Errorf("Expected no err got %v", err)
return
} }
putErrC <- nil
}() }()
count := 4
// call Get 4 times for the same unavailable chunk. The calls will be blocked until the Put above. // call Get 4 times for the same unavailable chunk. The calls will be blocked until the Put above.
getWG := sync.WaitGroup{} errC := make(chan error)
for i := 0; i < 4; i++ { for i := 0; i < count; i++ {
getWG.Add(1)
go func() { go func() {
defer getWG.Done()
recChunk, err := netStore.Get(ctx, chunk.Address()) recChunk, err := netStore.Get(ctx, chunk.Address())
if err != nil { if err != nil {
t.Fatalf("Expected no err got %v", err) errC <- fmt.Errorf("Expected no err got %v", err)
} }
if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) { if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
t.Fatalf("Different chunk received than what was put") errC <- errors.New("Different chunk received than what was put")
} }
errC <- nil
}() }()
} }
finishedC := make(chan struct{}) if err := <-putErrC; err != nil {
go func() { t.Fatal(err)
getWG.Wait() }
close(finishedC)
}() timeout := time.After(1 * time.Second)
// The Get calls should return after Put, so no timeout expected // The Get calls should return after Put, so no timeout expected
for i := 0; i < count; i++ {
select { select {
case <-finishedC: case err := <-errC:
case <-time.After(1 * time.Second): if err != nil {
t.Fatal(err)
}
case <-timeout:
t.Fatalf("Timeout waiting for Get calls to return") t.Fatalf("Timeout waiting for Get calls to return")
} }
}
// A fetcher was created, check if it has been removed after cancel // A fetcher was created, check if it has been removed after cancel
if netStore.fetchers.Len() != 0 { if netStore.fetchers.Len() != 0 {
@ -448,7 +485,7 @@ func TestNetStoreGetCallsOffer(t *testing.T) {
defer cancel() defer cancel()
// We call get for a not available chunk, it will timeout because the chunk is not delivered // We call get for a not available chunk, it will timeout because the chunk is not delivered
chunk, err := netStore.Get(ctx, chunk.Address()) _, err := netStore.Get(ctx, chunk.Address())
if err != context.DeadlineExceeded { if err != context.DeadlineExceeded {
t.Fatalf("Expect error %v got %v", context.DeadlineExceeded, err) t.Fatalf("Expect error %v got %v", context.DeadlineExceeded, err)
@ -542,16 +579,12 @@ func TestNetStoreFetchFuncCalledMultipleTimes(t *testing.T) {
t.Fatalf("Expected netStore to have one fetcher for the requested chunk") t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
} }
// Call wait three times parallelly // Call wait three times in parallel
wg := sync.WaitGroup{} count := 3
for i := 0; i < 3; i++ { errC := make(chan error)
wg.Add(1) for i := 0; i < count; i++ {
go func() { go func() {
err := wait(ctx) errC <- wait(ctx)
if err != nil {
t.Fatalf("Expected no err got %v", err)
}
wg.Done()
}() }()
} }
@ -570,7 +603,12 @@ func TestNetStoreFetchFuncCalledMultipleTimes(t *testing.T) {
} }
// wait until all wait calls return (because the chunk is delivered) // wait until all wait calls return (because the chunk is delivered)
wg.Wait() for i := 0; i < count; i++ {
err := <-errC
if err != nil {
t.Fatal(err)
}
}
// There should be no more fetchers for the delivered chunk // There should be no more fetchers for the delivered chunk
if netStore.fetchers.Len() != 0 { if netStore.fetchers.Len() != 0 {
@ -606,23 +644,29 @@ func TestNetStoreFetcherLifeCycleWithTimeout(t *testing.T) {
t.Fatalf("Expected netStore to have one fetcher for the requested chunk") t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
} }
// Call wait three times parallelly // Call wait three times in parallel
wg := sync.WaitGroup{} count := 3
for i := 0; i < 3; i++ { errC := make(chan error)
wg.Add(1) for i := 0; i < count; i++ {
go func() { go func() {
defer wg.Done()
rctx, rcancel := context.WithTimeout(context.Background(), 100*time.Millisecond) rctx, rcancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer rcancel() defer rcancel()
err := wait(rctx) err := wait(rctx)
if err != context.DeadlineExceeded { if err != context.DeadlineExceeded {
t.Fatalf("Expected err %v got %v", context.DeadlineExceeded, err) errC <- fmt.Errorf("Expected err %v got %v", context.DeadlineExceeded, err)
return
} }
errC <- nil
}() }()
} }
// wait until all wait calls timeout // wait until all wait calls timeout
wg.Wait() for i := 0; i < count; i++ {
err := <-errC
if err != nil {
t.Fatal(err)
}
}
// There should be no more fetchers after timeout // There should be no more fetchers after timeout
if netStore.fetchers.Len() != 0 { if netStore.fetchers.Len() != 0 {