diff --git a/Makefile b/Makefile index 0cac496449..66401f747a 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,9 @@ android: $(GORUN) build/ci.go aar --local @echo "Done building." @echo "Import \"$(GOBIN)/geth.aar\" to use the library." - + @echo "Import \"$(GOBIN)/geth-sources.jar\" to add javadocs" + @echo "For more info see https://stackoverflow.com/questions/20994336/android-studio-how-to-attach-javadoc" + ios: $(GORUN) build/ci.go xcode --local @echo "Done building." diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 38196ed25c..5ca7c241db 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -80,36 +80,56 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { return append(method.ID, arguments...), nil } -// Unpack output in v according to the abi specification. -func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) { +func (abi ABI) getArguments(name string, data []byte) (Arguments, error) { // since there can't be naming collisions with contracts and events, // we need to decide whether we're calling a method or an event + var args Arguments if method, ok := abi.Methods[name]; ok { if len(data)%32 != 0 { - return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data) + return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data) } - return method.Outputs.Unpack(v, data) + args = method.Outputs } if event, ok := abi.Events[name]; ok { - return event.Inputs.Unpack(v, data) + args = event.Inputs } - return fmt.Errorf("abi: could not locate named method or event") + if args == nil { + return nil, errors.New("abi: could not locate named method or event") + } + return args, nil +} + +// Unpack unpacks the output according to the abi specification. +func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { + args, err := abi.getArguments(name, data) + if err != nil { + return nil, err + } + return args.Unpack(data) +} + +// UnpackIntoInterface unpacks the output in v according to the abi specification. +// It performs an additional copy. Please only use, if you want to unpack into a +// structure that does not strictly conform to the abi structure (e.g. has additional arguments) +func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error { + args, err := abi.getArguments(name, data) + if err != nil { + return err + } + unpacked, err := args.Unpack(data) + if err != nil { + return err + } + return args.Copy(v, unpacked) } // UnpackIntoMap unpacks a log into the provided map[string]interface{}. func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) { - // since there can't be naming collisions with contracts and events, - // we need to decide whether we're calling a method or an event - if method, ok := abi.Methods[name]; ok { - if len(data)%32 != 0 { - return fmt.Errorf("abi: improperly formatted output") - } - return method.Outputs.UnpackIntoMap(v, data) - } - if event, ok := abi.Events[name]; ok { - return event.Inputs.UnpackIntoMap(v, data) + args, err := abi.getArguments(name, data) + if err != nil { + return err } - return fmt.Errorf("abi: could not locate named method or event") + return args.UnpackIntoMap(v, data) } // UnmarshalJSON implements json.Unmarshaler interface. @@ -250,10 +270,10 @@ func UnpackRevert(data []byte) (string, error) { if !bytes.Equal(data[:4], revertSelector) { return "", errors.New("invalid data for unpacking") } - var reason string typ, _ := NewType("string", "", nil) - if err := (Arguments{{Type: typ}}).Unpack(&reason, data[4:]); err != nil { + unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:]) + if err != nil { return "", err } - return reason, nil + return unpacked[0].(string), nil } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index f7d7f7aa62..ad8acdf522 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -181,18 +181,15 @@ func TestConstructor(t *testing.T) { if err != nil { t.Error(err) } - v := struct { - A *big.Int - B *big.Int - }{new(big.Int), new(big.Int)} - //abi.Unpack(&v, "", packed) - if err := abi.Constructor.Inputs.Unpack(&v, packed); err != nil { + unpacked, err := abi.Constructor.Inputs.Unpack(packed) + if err != nil { t.Error(err) } - if !reflect.DeepEqual(v.A, big.NewInt(1)) { + + if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) { t.Error("Unable to pack/unpack from constructor") } - if !reflect.DeepEqual(v.B, big.NewInt(2)) { + if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) { t.Error("Unable to pack/unpack from constructor") } } @@ -743,7 +740,7 @@ func TestUnpackEvent(t *testing.T) { } var ev ReceivedEvent - err = abi.Unpack(&ev, "received", data) + err = abi.UnpackIntoInterface(&ev, "received", data) if err != nil { t.Error(err) } @@ -752,7 +749,7 @@ func TestUnpackEvent(t *testing.T) { Sender common.Address } var receivedAddrEv ReceivedAddrEvent - err = abi.Unpack(&receivedAddrEv, "receivedAddr", data) + err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data) if err != nil { t.Error(err) } diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index aaef3bd41c..e6d5245596 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -76,28 +76,20 @@ func (arguments Arguments) isTuple() bool { } // Unpack performs the operation hexdata -> Go format. -func (arguments Arguments) Unpack(v interface{}, data []byte) error { +func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { if len(data) == 0 { if len(arguments) != 0 { - return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") } - return nil // Nothing to unmarshal, return - } - // make sure the passed value is arguments pointer - if reflect.Ptr != reflect.ValueOf(v).Kind() { - return fmt.Errorf("abi: Unpack(non-pointer %T)", v) - } - marshalledValues, err := arguments.UnpackValues(data) - if err != nil { - return err - } - if len(marshalledValues) == 0 { - return fmt.Errorf("abi: Unpack(no-values unmarshalled %T)", v) - } - if arguments.isTuple() { - return arguments.unpackTuple(v, marshalledValues) + // Nothing to unmarshal, return default variables + nonIndexedArgs := arguments.NonIndexed() + defaultVars := make([]interface{}, len(nonIndexedArgs)) + for index, arg := range nonIndexedArgs { + defaultVars[index] = reflect.New(arg.Type.GetType()) + } + return defaultVars, nil } - return arguments.unpackAtomic(v, marshalledValues[0]) + return arguments.UnpackValues(data) } // UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value. @@ -122,8 +114,26 @@ func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) return nil } -// unpackAtomic unpacks ( hexdata -> go ) a single value. -func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error { +// Copy performs the operation go format -> provided struct. +func (arguments Arguments) Copy(v interface{}, values []interface{}) error { + // make sure the passed value is arguments pointer + if reflect.Ptr != reflect.ValueOf(v).Kind() { + return fmt.Errorf("abi: Unpack(non-pointer %T)", v) + } + if len(values) == 0 { + if len(arguments) != 0 { + return fmt.Errorf("abi: attempting to copy no values while %d arguments are expected", len(arguments)) + } + return nil // Nothing to copy, return + } + if arguments.isTuple() { + return arguments.copyTuple(v, values) + } + return arguments.copyAtomic(v, values[0]) +} + +// unpackAtomic unpacks ( hexdata -> go ) a single value +func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error { dst := reflect.ValueOf(v).Elem() src := reflect.ValueOf(marshalledValues) @@ -133,8 +143,8 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interfac return set(dst, src) } -// unpackTuple unpacks ( hexdata -> go ) a batch of values. -func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error { +// copyTuple copies a batch of values from marshalledValues to v. +func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error { value := reflect.ValueOf(v).Elem() nonIndexedArgs := arguments.NonIndexed() diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index c7088e7b78..e48d6df0c4 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -128,11 +128,14 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error { +func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error { // Don't crash on a lazy user if opts == nil { opts = new(CallOpts) } + if results == nil { + results = new([]interface{}) + } // Pack the input, call and unpack the results input, err := c.abi.Pack(method, params...) if err != nil { @@ -169,10 +172,14 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, } } } - if err != nil { + + if len(*results) == 0 { + res, err := c.abi.Unpack(method, output) + *results = res return err } - return c.abi.Unpack(result, method, output) + res := *results + return c.abi.UnpackIntoInterface(res[0], method, output) } // Transact invokes the (paid) contract method with params as input values. @@ -372,7 +379,7 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter // UnpackLog unpacks a retrieved log into the provided output structure. func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { if len(log.Data) > 0 { - if err := c.abi.Unpack(out, event, log.Data); err != nil { + if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { return err } } diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index 7d287850f4..c4740f68b7 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -71,11 +71,10 @@ func TestPassingBlockNumber(t *testing.T) { }, }, }, mc, nil, nil) - var ret string blockNumber := big.NewInt(42) - bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &ret, "something") + bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something") if mc.callContractBlockNumber != blockNumber { t.Fatalf("CallContract() was not passed the block number") @@ -85,7 +84,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was not passed the block number") } - bc.Call(&bind.CallOpts{}, &ret, "something") + bc.Call(&bind.CallOpts{}, nil, "something") if mc.callContractBlockNumber != nil { t.Fatalf("CallContract() was passed a block number when it should not have been") @@ -95,7 +94,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was passed a block number when it should not have been") } - bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, &ret, "something") + bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something") if !mc.pendingCallContractCalled { t.Fatalf("CallContract() was not passed the block number") diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 86d85f35af..fd301bbead 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -1696,11 +1696,11 @@ func TestGolangBindings(t *testing.T) { t.Skip("go sdk not found for testing") } // Create a temporary workspace for the test suite - ws, err := ioutil.TempDir("", "") + ws, err := ioutil.TempDir("", "binding-test") if err != nil { t.Fatalf("failed to create temporary workspace: %v", err) } - defer os.RemoveAll(ws) + //defer os.RemoveAll(ws) pkg := filepath.Join(ws, "bindtest") if err = os.MkdirAll(pkg, 0700); err != nil { diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 4fbdcdfca2..9e15a3d0a1 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -263,7 +263,7 @@ var ( // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. - func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...) } @@ -282,7 +282,7 @@ var ( // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. - func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...) } @@ -302,19 +302,23 @@ var ( // // Solidity: {{.Original.String}} func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) { - {{if .Structured}}ret := new(struct{ - {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}} - {{end}} - }){{else}}var ( - {{range $i, $_ := .Normalized.Outputs}}ret{{$i}} = new({{bindtype .Type $structs}}) - {{end}} - ){{end}} - out := {{if .Structured}}ret{{else}}{{if eq (len .Normalized.Outputs) 1}}ret0{{else}}&[]interface{}{ - {{range $i, $_ := .Normalized.Outputs}}ret{{$i}}, - {{end}} - }{{end}}{{end}} - err := _{{$contract.Type}}.contract.Call(opts, out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) - return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err + var out []interface{} + err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + {{if .Structured}} + outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} }) + {{range $i, $t := .Normalized.Outputs}} + outstruct.{{.Name}} = out[{{$i}}].({{bindtype .Type $structs}}){{end}} + + return *outstruct, err + {{else}} + if err != nil { + return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err + } + {{range $i, $t := .Normalized.Outputs}} + out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} + + return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err + {{end}} } // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. diff --git a/accounts/abi/event_test.go b/accounts/abi/event_test.go index 79504c28ce..3332f8a072 100644 --- a/accounts/abi/event_test.go +++ b/accounts/abi/event_test.go @@ -147,10 +147,6 @@ func TestEventString(t *testing.T) { // TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array. func TestEventMultiValueWithArrayUnpack(t *testing.T) { definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]` - type testStruct struct { - Value1 [2]uint8 - Value2 uint8 - } abi, err := JSON(strings.NewReader(definition)) require.NoError(t, err) var b bytes.Buffer @@ -158,10 +154,10 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) { for ; i <= 3; i++ { b.Write(packNum(reflect.ValueOf(i))) } - var rst testStruct - require.NoError(t, abi.Unpack(&rst, "test", b.Bytes())) - require.Equal(t, [2]uint8{1, 2}, rst.Value1) - require.Equal(t, uint8(3), rst.Value2) + unpacked, err := abi.Unpack("test", b.Bytes()) + require.NoError(t, err) + require.Equal(t, [2]uint8{1, 2}, unpacked[0]) + require.Equal(t, uint8(3), unpacked[1]) } func TestEventTupleUnpack(t *testing.T) { @@ -351,14 +347,14 @@ func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, ass var e Event assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI") a := ABI{Events: map[string]Event{"e": e}} - return a.Unpack(dest, "e", data) + return a.UnpackIntoInterface(dest, "e", data) } // TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder. func TestEventUnpackIndexed(t *testing.T) { definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]` type testStruct struct { - Value1 uint8 + Value1 uint8 // indexed Value2 uint8 } abi, err := JSON(strings.NewReader(definition)) @@ -366,7 +362,7 @@ func TestEventUnpackIndexed(t *testing.T) { var b bytes.Buffer b.Write(packNum(reflect.ValueOf(uint8(8)))) var rst testStruct - require.NoError(t, abi.Unpack(&rst, "test", b.Bytes())) + require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes())) require.Equal(t, uint8(0), rst.Value1) require.Equal(t, uint8(8), rst.Value2) } @@ -375,7 +371,7 @@ func TestEventUnpackIndexed(t *testing.T) { func TestEventIndexedWithArrayUnpack(t *testing.T) { definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]` type testStruct struct { - Value1 [2]uint8 + Value1 [2]uint8 // indexed Value2 string } abi, err := JSON(strings.NewReader(definition)) @@ -388,7 +384,7 @@ func TestEventIndexedWithArrayUnpack(t *testing.T) { b.Write(common.RightPadBytes([]byte(stringOut), 32)) var rst testStruct - require.NoError(t, abi.Unpack(&rst, "test", b.Bytes())) + require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes())) require.Equal(t, [2]uint8{0, 0}, rst.Value1) require.Equal(t, stringOut, rst.Value2) } diff --git a/accounts/abi/pack_test.go b/accounts/abi/pack_test.go index 284215a7d7..5c7cb1cc1a 100644 --- a/accounts/abi/pack_test.go +++ b/accounts/abi/pack_test.go @@ -44,18 +44,7 @@ func TestPack(t *testing.T) { t.Fatalf("invalid ABI definition %s, %v", inDef, err) } var packed []byte - if reflect.TypeOf(test.unpacked).Kind() != reflect.Struct { - packed, err = inAbi.Pack("method", test.unpacked) - } else { - // if want is a struct we need to use the components. - elem := reflect.ValueOf(test.unpacked) - var values []interface{} - for i := 0; i < elem.NumField(); i++ { - field := elem.Field(i) - values = append(values, field.Interface()) - } - packed, err = inAbi.Pack("method", values...) - } + packed, err = inAbi.Pack("method", test.unpacked) if err != nil { t.Fatalf("test %d (%v) failed: %v", i, test.def, err) diff --git a/accounts/abi/packing_test.go b/accounts/abi/packing_test.go index 16b4dc43d7..eae3b0df20 100644 --- a/accounts/abi/packing_test.go +++ b/accounts/abi/packing_test.go @@ -620,7 +620,7 @@ var packUnpackTests = []packUnpackTest{ { def: `[{"type": "bytes32[]"}]`, - unpacked: []common.Hash{{1}, {2}}, + unpacked: [][32]byte{{1}, {2}}, packed: "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000002" + "0100000000000000000000000000000000000000000000000000000000000000" + @@ -722,7 +722,7 @@ var packUnpackTests = []packUnpackTest{ }, // struct outputs { - def: `[{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]`, + def: `[{"components": [{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002", unpacked: struct { @@ -731,28 +731,28 @@ var packUnpackTests = []packUnpackTest{ }{big.NewInt(1), big.NewInt(2)}, }, { - def: `[{"name":"int_one","type":"int256"}]`, + def: `[{"components": [{"name":"int_one","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001", unpacked: struct { IntOne *big.Int }{big.NewInt(1)}, }, { - def: `[{"name":"int__one","type":"int256"}]`, + def: `[{"components": [{"name":"int__one","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001", unpacked: struct { IntOne *big.Int }{big.NewInt(1)}, }, { - def: `[{"name":"int_one_","type":"int256"}]`, + def: `[{"components": [{"name":"int_one_","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001", unpacked: struct { IntOne *big.Int }{big.NewInt(1)}, }, { - def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`, + def: `[{"components": [{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002", unpacked: struct { @@ -831,11 +831,11 @@ var packUnpackTests = []packUnpackTest{ }, { // static tuple - def: `[{"name":"a","type":"int64"}, + def: `[{"components": [{"name":"a","type":"int64"}, {"name":"b","type":"int256"}, {"name":"c","type":"int256"}, {"name":"d","type":"bool"}, - {"name":"e","type":"bytes32[3][2]"}]`, + {"name":"e","type":"bytes32[3][2]"}], "type":"tuple"}]`, unpacked: struct { A int64 B *big.Int @@ -855,21 +855,22 @@ var packUnpackTests = []packUnpackTest{ "0500000000000000000000000000000000000000000000000000000000000000", // struct[e] array[1][2] }, { - def: `[{"name":"a","type":"string"}, + def: `[{"components": [{"name":"a","type":"string"}, {"name":"b","type":"int64"}, {"name":"c","type":"bytes"}, {"name":"d","type":"string[]"}, {"name":"e","type":"int256[]"}, - {"name":"f","type":"address[]"}]`, + {"name":"f","type":"address[]"}], "type":"tuple"}]`, unpacked: struct { - FieldA string `abi:"a"` // Test whether abi tag works - FieldB int64 `abi:"b"` - C []byte - D []string - E []*big.Int - F []common.Address + A string + B int64 + 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}}}, - packed: "00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset + packed: "0000000000000000000000000000000000000000000000000000000000000020" + // struct a + "00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset "0000000000000000000000000000000000000000000000000000000000000001" + // struct[b] "0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset "0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset @@ -894,23 +895,24 @@ var packUnpackTests = []packUnpackTest{ "0000000000000000000000000200000000000000000000000000000000000000", // common.Address{2} }, { - def: `[{"components": [{"name": "a","type": "uint256"}, + def: `[{"components": [{ "type": "tuple","components": [{"name": "a","type": "uint256"}, {"name": "b","type": "uint256[]"}], "name": "a","type": "tuple"}, - {"name": "b","type": "uint256[]"}]`, + {"name": "b","type": "uint256[]"}], "type": "tuple"}]`, unpacked: struct { A struct { - FieldA *big.Int `abi:"a"` - B []*big.Int + A *big.Int + B []*big.Int } B []*big.Int }{ A: struct { - FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple - B []*big.Int + A *big.Int + B []*big.Int }{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(2)}}, B: []*big.Int{big.NewInt(1), big.NewInt(2)}}, - packed: "0000000000000000000000000000000000000000000000000000000000000040" + // a offset + packed: "0000000000000000000000000000000000000000000000000000000000000020" + // struct a + "0000000000000000000000000000000000000000000000000000000000000040" + // a offset "00000000000000000000000000000000000000000000000000000000000000e0" + // b offset "0000000000000000000000000000000000000000000000000000000000000001" + // a.a value "0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index f4812b06bf..11248e0730 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -24,6 +24,29 @@ import ( "strings" ) +// ConvertType converts an interface of a runtime type into a interface of the +// given type +// e.g. turn +// var fields []reflect.StructField +// fields = append(fields, reflect.StructField{ +// Name: "X", +// Type: reflect.TypeOf(new(big.Int)), +// Tag: reflect.StructTag("json:\"" + "x" + "\""), +// } +// into +// type TupleT struct { X *big.Int } +func ConvertType(in interface{}, proto interface{}) interface{} { + protoType := reflect.TypeOf(proto) + if reflect.TypeOf(in).ConvertibleTo(protoType) { + return reflect.ValueOf(in).Convert(protoType).Interface() + } + // Use set as a last ditch effort + if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil { + panic(err) + } + return proto +} + // indirect recursively dereferences the value until it either gets the value // or finds a big.Int func indirect(v reflect.Value) reflect.Value { @@ -119,6 +142,9 @@ func setSlice(dst, src reflect.Value) error { } func setArray(dst, src reflect.Value) error { + if src.Kind() == reflect.Ptr { + return set(dst, indirect(src)) + } array := reflect.New(dst.Type()).Elem() min := src.Len() if src.Len() > dst.Len() { diff --git a/accounts/abi/reflect_test.go b/accounts/abi/reflect_test.go index c425e6e54b..bac4cd9530 100644 --- a/accounts/abi/reflect_test.go +++ b/accounts/abi/reflect_test.go @@ -17,6 +17,7 @@ package abi import ( + "math/big" "reflect" "testing" ) @@ -189,3 +190,72 @@ func TestReflectNameToStruct(t *testing.T) { }) } } + +func TestConvertType(t *testing.T) { + // Test Basic Struct + type T struct { + X *big.Int + Y *big.Int + } + // Create on-the-fly structure + var fields []reflect.StructField + fields = append(fields, reflect.StructField{ + Name: "X", + Type: reflect.TypeOf(new(big.Int)), + Tag: reflect.StructTag("json:\"" + "x" + "\""), + }) + fields = append(fields, reflect.StructField{ + Name: "Y", + Type: reflect.TypeOf(new(big.Int)), + Tag: reflect.StructTag("json:\"" + "y" + "\""), + }) + val := reflect.New(reflect.StructOf(fields)) + val.Elem().Field(0).Set(reflect.ValueOf(big.NewInt(1))) + val.Elem().Field(1).Set(reflect.ValueOf(big.NewInt(2))) + // ConvertType + out := *ConvertType(val.Interface(), new(T)).(*T) + if out.X.Cmp(big.NewInt(1)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1)) + } + if out.Y.Cmp(big.NewInt(2)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2)) + } + // Slice Type + val2 := reflect.MakeSlice(reflect.SliceOf(reflect.StructOf(fields)), 2, 2) + val2.Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1))) + val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) + val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) + val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) + out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T) + if out2[0].X.Cmp(big.NewInt(1)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) + } + if out2[0].Y.Cmp(big.NewInt(2)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) + } + if out2[1].X.Cmp(big.NewInt(3)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) + } + if out2[1].Y.Cmp(big.NewInt(4)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) + } + // Array Type + val3 := reflect.New(reflect.ArrayOf(2, reflect.StructOf(fields))) + val3.Elem().Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1))) + val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) + val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) + val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) + out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T) + if out3[0].X.Cmp(big.NewInt(1)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) + } + if out3[0].Y.Cmp(big.NewInt(2)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) + } + if out3[1].X.Cmp(big.NewInt(3)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) + } + if out3[1].Y.Cmp(big.NewInt(4)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) + } +} diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index 767d1540e6..b88f77805b 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -44,15 +44,13 @@ func TestUnpack(t *testing.T) { if err != nil { t.Fatalf("invalid hex %s: %v", test.packed, err) } - outptr := reflect.New(reflect.TypeOf(test.unpacked)) - err = abi.Unpack(outptr.Interface(), "method", encb) + out, err := abi.Unpack("method", encb) if err != nil { t.Errorf("test %d (%v) failed: %v", i, test.def, err) return } - out := outptr.Elem().Interface() - if !reflect.DeepEqual(test.unpacked, out) { - t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out) + if !reflect.DeepEqual(test.unpacked, ConvertType(out[0], test.unpacked)) { + t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out[0]) } }) } @@ -221,7 +219,7 @@ func TestLocalUnpackTests(t *testing.T) { t.Fatalf("invalid hex %s: %v", test.enc, err) } outptr := reflect.New(reflect.TypeOf(test.want)) - err = abi.Unpack(outptr.Interface(), "method", encb) + err = abi.UnpackIntoInterface(outptr.Interface(), "method", encb) if err := test.checkError(err); err != nil { t.Errorf("test %d (%v) failed: %v", i, test.def, err) return @@ -234,7 +232,7 @@ func TestLocalUnpackTests(t *testing.T) { } } -func TestUnpackSetDynamicArrayOutput(t *testing.T) { +func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) { abi, err := JSON(strings.NewReader(`[{"constant":true,"inputs":[],"name":"testDynamicFixedBytes15","outputs":[{"name":"","type":"bytes15[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"testDynamicFixedBytes32","outputs":[{"name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"}]`)) if err != nil { t.Fatal(err) @@ -249,7 +247,7 @@ func TestUnpackSetDynamicArrayOutput(t *testing.T) { ) // test 32 - err = abi.Unpack(&out32, "testDynamicFixedBytes32", marshalledReturn32) + err = abi.UnpackIntoInterface(&out32, "testDynamicFixedBytes32", marshalledReturn32) if err != nil { t.Fatal(err) } @@ -266,7 +264,7 @@ func TestUnpackSetDynamicArrayOutput(t *testing.T) { } // test 15 - err = abi.Unpack(&out15, "testDynamicFixedBytes32", marshalledReturn15) + err = abi.UnpackIntoInterface(&out15, "testDynamicFixedBytes32", marshalledReturn15) if err != nil { t.Fatal(err) } @@ -367,7 +365,7 @@ func TestMethodMultiReturn(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { require := require.New(t) - err := abi.Unpack(tc.dest, "multi", data) + err := abi.UnpackIntoInterface(tc.dest, "multi", data) if tc.error == "" { require.Nil(err, "Should be able to unpack method outputs.") require.Equal(tc.expected, tc.dest) @@ -390,7 +388,7 @@ func TestMultiReturnWithArray(t *testing.T) { ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9} ret2, ret2Exp := new(uint64), uint64(8) - if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { + if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -414,7 +412,7 @@ func TestMultiReturnWithStringArray(t *testing.T) { ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f") ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"} ret4, ret4Exp := new(bool), false - if err := abi.Unpack(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil { + if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -452,7 +450,7 @@ func TestMultiReturnWithStringSlice(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"} 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.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -492,7 +490,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) { {{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}}, } ret2, ret2Exp := new(uint64), uint64(0x9876) - if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { + if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -531,7 +529,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000a")) buff.Write(common.Hex2Bytes("0102000000000000000000000000000000000000000000000000000000000000")) - err = abi.Unpack(&mixedBytes, "mixedBytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&mixedBytes, "mixedBytes", buff.Bytes()) if err != nil { t.Error(err) } else { @@ -546,7 +544,7 @@ func TestUnmarshal(t *testing.T) { // marshal int var Int *big.Int - err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } @@ -557,7 +555,7 @@ func TestUnmarshal(t *testing.T) { // marshal bool var Bool bool - err = abi.Unpack(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } @@ -574,7 +572,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(bytesOut) var Bytes []byte - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -590,7 +588,7 @@ func TestUnmarshal(t *testing.T) { bytesOut = common.RightPadBytes([]byte("hello"), 64) buff.Write(bytesOut) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -606,7 +604,7 @@ func TestUnmarshal(t *testing.T) { bytesOut = common.RightPadBytes([]byte("hello"), 64) buff.Write(bytesOut) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -616,7 +614,7 @@ func TestUnmarshal(t *testing.T) { } // marshal dynamic bytes output empty - err = abi.Unpack(&Bytes, "bytes", nil) + err = abi.UnpackIntoInterface(&Bytes, "bytes", nil) if err == nil { t.Error("expected error") } @@ -627,7 +625,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) buff.Write(common.RightPadBytes([]byte("hello"), 32)) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -641,7 +639,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.RightPadBytes([]byte("hello"), 32)) var hash common.Hash - err = abi.Unpack(&hash, "fixed", buff.Bytes()) + err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes()) if err != nil { t.Error(err) } @@ -654,12 +652,12 @@ func TestUnmarshal(t *testing.T) { // marshal error buff.Reset() buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err == nil { t.Error("expected error") } - err = abi.Unpack(&Bytes, "multi", make([]byte, 64)) + err = abi.UnpackIntoInterface(&Bytes, "multi", make([]byte, 64)) if err == nil { t.Error("expected error") } @@ -670,7 +668,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003")) // marshal int array var intArray [3]*big.Int - err = abi.Unpack(&intArray, "intArraySingle", buff.Bytes()) + err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes()) if err != nil { t.Error(err) } @@ -691,7 +689,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000")) var outAddr []common.Address - err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) + err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } @@ -718,7 +716,7 @@ func TestUnmarshal(t *testing.T) { A []common.Address B []common.Address } - err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes()) + err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } @@ -746,7 +744,7 @@ func TestUnmarshal(t *testing.T) { buff.Reset() buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100")) - err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) + err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes()) if err == nil { t.Fatal("expected error:", err) } @@ -769,7 +767,7 @@ func TestUnpackTuple(t *testing.T) { B *big.Int }{new(big.Int), new(big.Int)} - err = abi.Unpack(&v, "tuple", buff.Bytes()) + err = abi.UnpackIntoInterface(&v, "tuple", buff.Bytes()) if err != nil { t.Error(err) } else { @@ -841,7 +839,7 @@ func TestUnpackTuple(t *testing.T) { A: big.NewInt(1), } - err = abi.Unpack(&ret, "tuple", buff.Bytes()) + err = abi.UnpackIntoInterface(&ret, "tuple", buff.Bytes()) if err != nil { t.Error(err) } diff --git a/accounts/accounts.go b/accounts/accounts.go index 7a14e4e3e5..dc85cba174 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -88,7 +88,7 @@ type Wallet interface { // to discover non zero accounts and automatically add them to list of tracked // accounts. // - // Note, self derivaton will increment the last component of the specified path + // Note, self derivation will increment the last component of the specified path // opposed to decending into a child path to allow discovering accounts starting // from non zero components. // diff --git a/accounts/keystore/file_cache.go b/accounts/keystore/file_cache.go index 73ff6ae9ee..8b309321d3 100644 --- a/accounts/keystore/file_cache.go +++ b/accounts/keystore/file_cache.go @@ -32,7 +32,7 @@ import ( type fileCache struct { all mapset.Set // Set of all files from the keystore folder lastMod time.Time // Last time instance when a file was modified - mu sync.RWMutex + mu sync.Mutex } // scan performs a new scan on the given directory, compares against the already diff --git a/accounts/keystore/keystore_test.go b/accounts/keystore/keystore_test.go index 29c251d7c1..cb5de11c0d 100644 --- a/accounts/keystore/keystore_test.go +++ b/accounts/keystore/keystore_test.go @@ -336,7 +336,9 @@ func TestWalletNotifications(t *testing.T) { // Shut down the event collector and check events. sub.Unsubscribe() - <-updates + for ev := range updates { + events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) + } checkAccounts(t, live, ks.Wallets()) checkEvents(t, wantEvents, events) } diff --git a/accounts/scwallet/wallet.go b/accounts/scwallet/wallet.go index 80009fc5eb..85fae8c114 100644 --- a/accounts/scwallet/wallet.go +++ b/accounts/scwallet/wallet.go @@ -637,7 +637,7 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun // to discover non zero accounts and automatically add them to list of tracked // accounts. // -// Note, self derivaton will increment the last component of the specified path +// Note, self derivation will increment the last component of the specified path // opposed to decending into a child path to allow discovering accounts starting // from non zero components. // diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go index e31bc143f0..107314b483 100644 --- a/accounts/usbwallet/wallet.go +++ b/accounts/usbwallet/wallet.go @@ -494,7 +494,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun // to discover non zero accounts and automatically add them to list of tracked // accounts. // -// Note, self derivaton will increment the last component of the specified path +// Note, self derivation will increment the last component of the specified path // opposed to decending into a child path to allow discovering accounts starting // from non zero components. // diff --git a/build/ci.go b/build/ci.go index 1105ed46b6..51a3953712 100644 --- a/build/ci.go +++ b/build/ci.go @@ -840,6 +840,7 @@ func doAndroidArchive(cmdline []string) { if *local { // If we're building locally, copy bundle to build dir and skip Maven os.Rename("geth.aar", filepath.Join(GOBIN, "geth.aar")) + os.Rename("geth-sources.jar", filepath.Join(GOBIN, "geth-sources.jar")) return } meta := newMavenMetadata(env) diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go index f6e2a14c3b..6c9ff615a1 100644 --- a/cmd/bootnode/main.go +++ b/cmd/bootnode/main.go @@ -44,7 +44,7 @@ func main() { natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:)") netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)") runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") - verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-9)") + verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-5)") vmodule = flag.String("vmodule", "", "log verbosity pattern") nodeKey *ecdsa.PrivateKey diff --git a/cmd/clef/extapi_changelog.md b/cmd/clef/extapi_changelog.md index dbc302631b..31554f0790 100644 --- a/cmd/clef/extapi_changelog.md +++ b/cmd/clef/extapi_changelog.md @@ -10,6 +10,64 @@ TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the: Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. +### 6.1.0 + +The API-method `account_signGnosisSafeTx` was added. This method takes two parameters, +`[address, safeTx]`. The latter, `safeTx`, can be copy-pasted from the gnosis relay. For example: + +``` +{ + "jsonrpc": "2.0", + "method": "account_signGnosisSafeTx", + "params": ["0xfd1c4226bfD1c436672092F4eCbfC270145b7256", + { + "safe": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3", + "to": "0xB372a646f7F05Cc1785018dBDA7EBc734a2A20E2", + "value": "20000000000000000", + "data": null, + "operation": 0, + "gasToken": "0x0000000000000000000000000000000000000000", + "safeTxGas": 27845, + "baseGas": 0, + "gasPrice": "0", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": 2, + "executionDate": null, + "submissionDate": "2020-09-15T21:54:49.617634Z", + "modified": "2020-09-15T21:54:49.617634Z", + "blockNumber": null, + "transactionHash": null, + "safeTxHash": "0x2edfbd5bc113ff18c0631595db32eb17182872d88d9bf8ee4d8c2dd5db6d95e2", + "executor": null, + "isExecuted": false, + "isSuccessful": null, + "ethGasPrice": null, + "gasUsed": null, + "fee": null, + "origin": null, + "dataDecoded": null, + "confirmationsRequired": null, + "confirmations": [ + { + "owner": "0xAd2e180019FCa9e55CADe76E4487F126Fd08DA34", + "submissionDate": "2020-09-15T21:54:49.663299Z", + "transactionHash": null, + "confirmationType": "CONFIRMATION", + "signature": "0x95a7250bb645f831c86defc847350e7faff815b2fb586282568e96cc859e39315876db20a2eed5f7a0412906ec5ab57652a6f645ad4833f345bda059b9da2b821c", + "signatureType": "EOA" + } + ], + "signatures": null + } + ], + "id": 67 +} +``` + +Not all fields are required, though. This method is really just a UX helper, which massages the +input to conform to the `EIP-712` [specification](https://docs.gnosis.io/safe/docs/contracts_tx_execution/#transaction-hash) +for the Gnosis Safe, and making the output be directly importable to by a relay service. + ### 6.0.0 diff --git a/cmd/devp2p/README.md b/cmd/devp2p/README.md new file mode 100644 index 0000000000..2763c75085 --- /dev/null +++ b/cmd/devp2p/README.md @@ -0,0 +1,86 @@ +# The devp2p command + +The devp2p command line tool is a utility for low-level peer-to-peer debugging and +protocol development purposes. It can do many things. + +### ENR Decoding + +Use `devp2p enrdump ` to verify and display an Ethereum Node Record. + +### Node Key Management + +The `devp2p key ...` command family deals with node key files. + +Run `devp2p key generate mynode.key` to create a new node key in the `mynode.key` file. + +Run `devp2p key to-enode mynode.key -ip 127.0.0.1 -tcp 30303` to create an enode:// URL +corresponding to the given node key and address information. + +### Maintaining DNS Discovery Node Lists + +The devp2p command can create and publish DNS discovery node lists. + +Run `devp2p dns sign ` to update the signature of a DNS discovery tree. + +Run `devp2p dns sync ` to download a complete DNS discovery tree. + +Run `devp2p dns to-cloudflare ` to publish a tree to CloudFlare DNS. + +Run `devp2p dns to-route53 ` to publish a tree to Amazon Route53. + +You can find more information about these commands in the [DNS Discovery Setup Guide][dns-tutorial]. + +### Discovery v4 Utilities + +The `devp2p discv4 ...` command family deals with the [Node Discovery v4][discv4] +protocol. + +Run `devp2p discv4 ping ` to ping a node. + +Run `devp2p discv4 resolve ` to find the most recent node record of a node in +the DHT. + +Run `devp2p discv4 crawl ` to create or update a JSON node set. + +### Discovery v5 Utilities + +The `devp2p discv5 ...` command family deals with the [Node Discovery v5][discv5] +protocol. This protocol is currently under active development. + +Run `devp2p discv5 ping ` to ping a node. + +Run `devp2p discv5 resolve ` to find the most recent node record of a node in +the discv5 DHT. + +Run `devp2p discv5 listen` to run a Discovery v5 node. + +Run `devp2p discv5 crawl ` to create or update a JSON node set containing +discv5 nodes. + +### Discovery Test Suites + +The devp2p command also contains interactive test suites for Discovery v4 and Discovery +v5. + +To run these tests against your implementation, you need to set up a networking +environment where two separate UDP listening addresses are available on the same machine. +The two listening addresses must also be routed such that they are able to reach the node +you want to test. + +For example, if you want to run the test on your local host, and the node under test is +also on the local host, you need to assign two IP addresses (or a larger range) to your +loopback interface. On macOS, this can be done by executing the following command: + + sudo ifconfig lo0 add 127.0.0.2 + +You can now run either test suite as follows: Start the node under test first, ensuring +that it won't talk to the Internet (i.e. disable bootstrapping). An easy way to prevent +unintended connections to the global DHT is listening on `127.0.0.1`. + +Now get the ENR of your node and store it in the `NODE` environment variable. + +Start the test by running `devp2p discv5 test -listen1 127.0.0.1 -listen2 127.0.0.2 $NODE`. + +[dns-tutorial]: https://geth.ethereum.org/docs/developers/dns-discovery-setup +[discv4]: https://github.com/ethereum/devp2p/tree/master/discv4.md +[discv5]: https://github.com/ethereum/devp2p/tree/master/discv5/discv5.md diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 99b0957ab3..467c20deb5 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -286,7 +286,11 @@ func listen(ln *enode.LocalNode, addr string) *net.UDPConn { } usocket := socket.(*net.UDPConn) uaddr := socket.LocalAddr().(*net.UDPAddr) - ln.SetFallbackIP(net.IP{127, 0, 0, 1}) + if uaddr.IP.IsUnspecified() { + ln.SetFallbackIP(net.IP{127, 0, 0, 1}) + } else { + ln.SetFallbackIP(uaddr.IP) + } ln.SetFallbackUDP(uaddr.Port) return usocket } @@ -294,7 +298,11 @@ func listen(ln *enode.LocalNode, addr string) *net.UDPConn { func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { s := params.RinkebyBootnodes if ctx.IsSet(bootnodesFlag.Name) { - s = strings.Split(ctx.String(bootnodesFlag.Name), ",") + input := ctx.String(bootnodesFlag.Name) + if input == "" { + return nil, nil + } + s = strings.Split(input, ",") } nodes := make([]*enode.Node, len(s)) var err error diff --git a/cmd/devp2p/discv5cmd.go b/cmd/devp2p/discv5cmd.go index f871821ea2..1d7442144f 100644 --- a/cmd/devp2p/discv5cmd.go +++ b/cmd/devp2p/discv5cmd.go @@ -18,9 +18,13 @@ package main import ( "fmt" + "os" "time" + "github.com/ethereum/go-ethereum/cmd/devp2p/internal/v5test" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/internal/utesting" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/discover" "gopkg.in/urfave/cli.v1" ) @@ -33,6 +37,7 @@ var ( discv5PingCommand, discv5ResolveCommand, discv5CrawlCommand, + discv5TestCommand, discv5ListenCommand, }, } @@ -53,6 +58,12 @@ var ( Action: discv5Crawl, Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag}, } + discv5TestCommand = cli.Command{ + Name: "test", + Usage: "Runs protocol tests against a node", + Action: discv5Test, + Flags: []cli.Flag{testPatternFlag, testListen1Flag, testListen2Flag}, + } discv5ListenCommand = cli.Command{ Name: "listen", Usage: "Runs a node", @@ -103,6 +114,30 @@ func discv5Crawl(ctx *cli.Context) error { return nil } +func discv5Test(ctx *cli.Context) error { + // Disable logging unless explicitly enabled. + if !ctx.GlobalIsSet("verbosity") && !ctx.GlobalIsSet("vmodule") { + log.Root().SetHandler(log.DiscardHandler()) + } + + // Filter and run test cases. + suite := &v5test.Suite{ + Dest: getNodeArg(ctx), + Listen1: ctx.String(testListen1Flag.Name), + Listen2: ctx.String(testListen2Flag.Name), + } + tests := suite.AllTests() + if ctx.IsSet(testPatternFlag.Name) { + tests = utesting.MatchTests(tests, ctx.String(testPatternFlag.Name)) + } + results := utesting.RunTests(tests, os.Stdout) + if fails := utesting.CountFailures(results); fails > 0 { + return fmt.Errorf("%v/%v tests passed.", len(tests)-fails, len(tests)) + } + fmt.Printf("%v/%v passed\n", len(tests), len(tests)) + return nil +} + func discv5Listen(ctx *cli.Context) error { disc := startV5(ctx) defer disc.Close() diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go index 250be64fe6..654888a4ca 100644 --- a/cmd/devp2p/internal/ethtest/chain.go +++ b/cmd/devp2p/internal/ethtest/chain.go @@ -1,3 +1,19 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package ethtest import ( @@ -68,6 +84,43 @@ func (c *Chain) Head() *types.Block { return c.blocks[c.Len()-1] } +func (c *Chain) GetHeaders(req GetBlockHeaders) (BlockHeaders, error) { + if req.Amount < 1 { + return nil, fmt.Errorf("no block headers requested") + } + + headers := make(BlockHeaders, req.Amount) + var blockNumber uint64 + + // range over blocks to check if our chain has the requested header + for _, block := range c.blocks { + if block.Hash() == req.Origin.Hash || block.Number().Uint64() == req.Origin.Number { + headers[0] = block.Header() + blockNumber = block.Number().Uint64() + } + } + if headers[0] == nil { + return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash) + } + + if req.Reverse { + for i := 1; i < int(req.Amount); i++ { + blockNumber -= (1 - req.Skip) + headers[i] = c.blocks[blockNumber].Header() + + } + + return headers, nil + } + + for i := 1; i < int(req.Amount); i++ { + blockNumber += (1 + req.Skip) + headers[i] = c.blocks[blockNumber].Header() + } + + return headers, nil +} + // loadChain takes the given chain.rlp file, and decodes and returns // the blocks from the file. func loadChain(chainfile string, genesis string) (*Chain, error) { diff --git a/cmd/devp2p/internal/ethtest/chain_test.go b/cmd/devp2p/internal/ethtest/chain_test.go new file mode 100644 index 0000000000..c8b977d237 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/chain_test.go @@ -0,0 +1,150 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethtest + +import ( + "path/filepath" + "strconv" + "testing" + + "github.com/ethereum/go-ethereum/p2p" + "github.com/stretchr/testify/assert" +) + +// TestEthProtocolNegotiation tests whether the test suite +// can negotiate the highest eth protocol in a status message exchange +func TestEthProtocolNegotiation(t *testing.T) { + var tests = []struct { + conn *Conn + caps []p2p.Cap + expected uint32 + }{ + { + conn: &Conn{}, + caps: []p2p.Cap{ + {Name: "eth", Version: 63}, + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + expected: uint32(65), + }, + { + conn: &Conn{}, + caps: []p2p.Cap{ + {Name: "eth", Version: 0}, + {Name: "eth", Version: 89}, + {Name: "eth", Version: 65}, + }, + expected: uint32(65), + }, + { + conn: &Conn{}, + caps: []p2p.Cap{ + {Name: "eth", Version: 63}, + {Name: "eth", Version: 64}, + {Name: "wrongProto", Version: 65}, + }, + expected: uint32(64), + }, + } + + for i, tt := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + tt.conn.negotiateEthProtocol(tt.caps) + assert.Equal(t, tt.expected, uint32(tt.conn.ethProtocolVersion)) + }) + } +} + +// TestChain_GetHeaders tests whether the test suite can correctly +// respond to a GetBlockHeaders request from a node. +func TestChain_GetHeaders(t *testing.T) { + chainFile, err := filepath.Abs("./testdata/chain.rlp.gz") + if err != nil { + t.Fatal(err) + } + genesisFile, err := filepath.Abs("./testdata/genesis.json") + if err != nil { + t.Fatal(err) + } + + chain, err := loadChain(chainFile, genesisFile) + if err != nil { + t.Fatal(err) + } + + var tests = []struct { + req GetBlockHeaders + expected BlockHeaders + }{ + { + req: GetBlockHeaders{ + Origin: hashOrNumber{ + Number: uint64(2), + }, + Amount: uint64(5), + Skip: 1, + Reverse: false, + }, + expected: BlockHeaders{ + chain.blocks[2].Header(), + chain.blocks[4].Header(), + chain.blocks[6].Header(), + chain.blocks[8].Header(), + chain.blocks[10].Header(), + }, + }, + { + req: GetBlockHeaders{ + Origin: hashOrNumber{ + Number: uint64(chain.Len() - 1), + }, + Amount: uint64(3), + Skip: 0, + Reverse: true, + }, + expected: BlockHeaders{ + chain.blocks[chain.Len()-1].Header(), + chain.blocks[chain.Len()-2].Header(), + chain.blocks[chain.Len()-3].Header(), + }, + }, + { + req: GetBlockHeaders{ + Origin: hashOrNumber{ + Hash: chain.Head().Hash(), + }, + Amount: uint64(1), + Skip: 0, + Reverse: false, + }, + expected: BlockHeaders{ + chain.Head().Header(), + }, + }, + } + + for i, tt := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + headers, err := chain.GetHeaders(tt.req) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, headers, tt.expected) + }) + } +} diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 6951f13bfb..f70bc43efa 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -1,19 +1,29 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package ethtest import ( - "crypto/ecdsa" "fmt" "net" - "reflect" - "time" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/utesting" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/rlpx" - "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/assert" ) @@ -26,137 +36,6 @@ type Suite struct { fullChain *Chain } -type Conn struct { - *rlpx.Conn - ourKey *ecdsa.PrivateKey -} - -func (c *Conn) Read() Message { - code, rawData, _, err := c.Conn.Read() - if err != nil { - return &Error{fmt.Errorf("could not read from connection: %v", err)} - } - - var msg Message - switch int(code) { - case (Hello{}).Code(): - msg = new(Hello) - case (Disconnect{}).Code(): - msg = new(Disconnect) - case (Status{}).Code(): - msg = new(Status) - case (GetBlockHeaders{}).Code(): - msg = new(GetBlockHeaders) - case (BlockHeaders{}).Code(): - msg = new(BlockHeaders) - case (GetBlockBodies{}).Code(): - msg = new(GetBlockBodies) - case (BlockBodies{}).Code(): - msg = new(BlockBodies) - case (NewBlock{}).Code(): - msg = new(NewBlock) - case (NewBlockHashes{}).Code(): - msg = new(NewBlockHashes) - default: - return &Error{fmt.Errorf("invalid message code: %d", code)} - } - - if err := rlp.DecodeBytes(rawData, msg); err != nil { - return &Error{fmt.Errorf("could not rlp decode message: %v", err)} - } - - return msg -} - -func (c *Conn) Write(msg Message) error { - payload, err := rlp.EncodeToBytes(msg) - if err != nil { - return err - } - _, err = c.Conn.Write(uint64(msg.Code()), payload) - return err - -} - -// handshake checks to make sure a `HELLO` is received. -func (c *Conn) handshake(t *utesting.T) Message { - // write protoHandshake to client - pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] - ourHandshake := &Hello{ - Version: 5, - Caps: []p2p.Cap{{Name: "eth", Version: 64}, {Name: "eth", Version: 65}}, - ID: pub0, - } - if err := c.Write(ourHandshake); err != nil { - t.Fatalf("could not write to connection: %v", err) - } - // read protoHandshake from client - switch msg := c.Read().(type) { - case *Hello: - return msg - default: - t.Fatalf("bad handshake: %v", msg) - return nil - } -} - -// statusExchange performs a `Status` message exchange with the given -// node. -func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { - // read status message from client - var message Message - switch msg := c.Read().(type) { - case *Status: - if msg.Head != chain.blocks[chain.Len()-1].Hash() { - t.Fatalf("wrong head in status: %v", msg.Head) - } - if msg.TD.Cmp(chain.TD(chain.Len())) != 0 { - t.Fatalf("wrong TD in status: %v", msg.TD) - } - if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) { - t.Fatalf("wrong fork ID in status: %v", msg.ForkID) - } - message = msg - default: - t.Fatalf("bad status message: %v", msg) - } - // write status message to client - status := Status{ - ProtocolVersion: 64, - NetworkID: 1, - TD: chain.TD(chain.Len()), - Head: chain.blocks[chain.Len()-1].Hash(), - Genesis: chain.blocks[0].Hash(), - ForkID: chain.ForkID(), - } - if err := c.Write(status); err != nil { - t.Fatalf("could not write to connection: %v", err) - } - - return message -} - -// waitForBlock waits for confirmation from the client that it has -// imported the given block. -func (c *Conn) waitForBlock(block *types.Block) error { - for { - req := &GetBlockHeaders{Origin: hashOrNumber{Hash: block.Hash()}, Amount: 1} - if err := c.Write(req); err != nil { - return err - } - - switch msg := c.Read().(type) { - case *BlockHeaders: - if len(*msg) > 0 { - return nil - } - time.Sleep(100 * time.Millisecond) - default: - return fmt.Errorf("invalid message: %v", msg) - } - } -} - // NewSuite creates and returns a new eth-test suite that can // be used to test the given node against the given blockchain // data. @@ -196,7 +75,7 @@ func (s *Suite) TestStatus(t *utesting.T) { case *Status: t.Logf("%+v\n", msg) default: - t.Fatalf("error: %v", msg) + t.Fatalf("unexpected: %#v", msg) } } @@ -225,7 +104,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } - switch msg := conn.Read().(type) { + switch msg := conn.ReadAndServe(s.chain).(type) { case *BlockHeaders: headers := msg for _, header := range *headers { @@ -234,7 +113,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { t.Logf("\nHEADER FOR BLOCK NUMBER %d: %+v\n", header.Number, header) } default: - t.Fatalf("error: %v", msg) + t.Fatalf("unexpected: %#v", msg) } } @@ -254,14 +133,14 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } - switch msg := conn.Read().(type) { + switch msg := conn.ReadAndServe(s.chain).(type) { case *BlockBodies: bodies := msg for _, body := range *bodies { t.Logf("\nBODY: %+v\n", body) } default: - t.Fatalf("error: %v", msg) + t.Fatalf("unexpected: %#v", msg) } } @@ -294,7 +173,7 @@ func (s *Suite) TestBroadcast(t *utesting.T) { t.Fatalf("could not write to connection: %v", err) } - switch msg := receiveConn.Read().(type) { + switch msg := receiveConn.ReadAndServe(s.chain).(type) { case *NewBlock: assert.Equal(t, blockAnnouncement.Block.Header(), msg.Block.Header(), "wrong block header in announcement") @@ -305,7 +184,7 @@ func (s *Suite) TestBroadcast(t *utesting.T) { assert.Equal(t, blockAnnouncement.Block.Hash(), hashes[0].Hash, "wrong block hash in announcement") default: - t.Fatal(msg) + t.Fatalf("unexpected: %#v", msg) } // update test suite chain s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[1000]) diff --git a/cmd/devp2p/internal/ethtest/testdata/chain.rlp.gz b/cmd/devp2p/internal/ethtest/testdata/chain.rlp.gz new file mode 100755 index 0000000000..bdd6290ce6 Binary files /dev/null and b/cmd/devp2p/internal/ethtest/testdata/chain.rlp.gz differ diff --git a/cmd/devp2p/internal/ethtest/testdata/genesis.json b/cmd/devp2p/internal/ethtest/testdata/genesis.json new file mode 100644 index 0000000000..ea5e2725b5 --- /dev/null +++ b/cmd/devp2p/internal/ethtest/testdata/genesis.json @@ -0,0 +1,26 @@ +{ + "config": { + "chainId": 1, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "ethash": {} + }, + "nonce": "0xdeadbeefdeadbeef", + "timestamp": "0x0", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x8000000", + "difficulty": "0x10", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "71562b71999873db5b286df957af199ec94617f7": { + "balance": "0xf4240" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index ef2c52ddfd..b6298e8083 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -1,14 +1,36 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package ethtest import ( + "crypto/ecdsa" "fmt" "io" "math/big" + "reflect" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/utesting" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/rlpx" "github.com/ethereum/go-ethereum/rlp" ) @@ -20,9 +42,10 @@ type Error struct { err error } -func (e *Error) Unwrap() error { return e.err } -func (e *Error) Error() string { return e.err.Error() } -func (e *Error) Code() int { return -1 } +func (e *Error) Unwrap() error { return e.err } +func (e *Error) Error() string { return e.err.Error() } +func (e *Error) Code() int { return -1 } +func (e *Error) GoString() string { return e.Error() } // Hello is the RLP structure of the protocol handshake. type Hello struct { @@ -45,6 +68,14 @@ type Disconnect struct { func (d Disconnect) Code() int { return 0x01 } +type Ping struct{} + +func (p Ping) Code() int { return 0x02 } + +type Pong struct{} + +func (p Pong) Code() int { return 0x03 } + // Status is the network packet for the status message for eth/64 and later. type Status struct { ProtocolVersion uint32 @@ -132,3 +163,204 @@ func (gbb GetBlockBodies) Code() int { return 21 } type BlockBodies []*types.Body func (bb BlockBodies) Code() int { return 22 } + +// Conn represents an individual connection with a peer +type Conn struct { + *rlpx.Conn + ourKey *ecdsa.PrivateKey + ethProtocolVersion uint +} + +func (c *Conn) Read() Message { + code, rawData, _, err := c.Conn.Read() + if err != nil { + return &Error{fmt.Errorf("could not read from connection: %v", err)} + } + + var msg Message + switch int(code) { + case (Hello{}).Code(): + msg = new(Hello) + case (Ping{}).Code(): + msg = new(Ping) + case (Pong{}).Code(): + msg = new(Pong) + case (Disconnect{}).Code(): + msg = new(Disconnect) + case (Status{}).Code(): + msg = new(Status) + case (GetBlockHeaders{}).Code(): + msg = new(GetBlockHeaders) + case (BlockHeaders{}).Code(): + msg = new(BlockHeaders) + case (GetBlockBodies{}).Code(): + msg = new(GetBlockBodies) + case (BlockBodies{}).Code(): + msg = new(BlockBodies) + case (NewBlock{}).Code(): + msg = new(NewBlock) + case (NewBlockHashes{}).Code(): + msg = new(NewBlockHashes) + default: + return &Error{fmt.Errorf("invalid message code: %d", code)} + } + + if err := rlp.DecodeBytes(rawData, msg); err != nil { + return &Error{fmt.Errorf("could not rlp decode message: %v", err)} + } + + return msg +} + +// ReadAndServe serves GetBlockHeaders requests while waiting +// on another message from the node. +func (c *Conn) ReadAndServe(chain *Chain) Message { + for { + switch msg := c.Read().(type) { + case *Ping: + c.Write(&Pong{}) + case *GetBlockHeaders: + req := *msg + headers, err := chain.GetHeaders(req) + if err != nil { + return &Error{fmt.Errorf("could not get headers for inbound header request: %v", err)} + } + + if err := c.Write(headers); err != nil { + return &Error{fmt.Errorf("could not write to connection: %v", err)} + } + default: + return msg + } + } +} + +func (c *Conn) Write(msg Message) error { + payload, err := rlp.EncodeToBytes(msg) + if err != nil { + return err + } + _, err = c.Conn.Write(uint64(msg.Code()), payload) + return err + +} + +// handshake checks to make sure a `HELLO` is received. +func (c *Conn) handshake(t *utesting.T) Message { + // write protoHandshake to client + pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] + ourHandshake := &Hello{ + Version: 5, + Caps: []p2p.Cap{ + {Name: "eth", Version: 64}, + {Name: "eth", Version: 65}, + }, + ID: pub0, + } + if err := c.Write(ourHandshake); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // read protoHandshake from client + switch msg := c.Read().(type) { + case *Hello: + // set snappy if version is at least 5 + if msg.Version >= 5 { + c.SetSnappy(true) + } + + c.negotiateEthProtocol(msg.Caps) + if c.ethProtocolVersion == 0 { + t.Fatalf("unexpected eth protocol version") + } + return msg + default: + t.Fatalf("bad handshake: %#v", msg) + return nil + } +} + +// negotiateEthProtocol sets the Conn's eth protocol version +// to highest advertised capability from peer +func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { + var highestEthVersion uint + for _, capability := range caps { + if capability.Name != "eth" { + continue + } + if capability.Version > highestEthVersion && capability.Version <= 65 { + highestEthVersion = capability.Version + } + } + c.ethProtocolVersion = highestEthVersion +} + +// statusExchange performs a `Status` message exchange with the given +// node. +func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message { + // read status message from client + var message Message + +loop: + for { + switch msg := c.Read().(type) { + case *Status: + if msg.Head != chain.blocks[chain.Len()-1].Hash() { + t.Fatalf("wrong head in status: %v", msg.Head) + } + if msg.TD.Cmp(chain.TD(chain.Len())) != 0 { + t.Fatalf("wrong TD in status: %v", msg.TD) + } + if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) { + t.Fatalf("wrong fork ID in status: %v", msg.ForkID) + } + message = msg + break loop + case *Disconnect: + t.Fatalf("disconnect received: %v", msg.Reason) + case *Ping: + c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error + // (PINGs should not be a response upon fresh connection) + default: + t.Fatalf("bad status message: %#v", msg) + } + } + // make sure eth protocol version is set for negotiation + if c.ethProtocolVersion == 0 { + t.Fatalf("eth protocol version must be set in Conn") + } + // write status message to client + status := Status{ + ProtocolVersion: uint32(c.ethProtocolVersion), + NetworkID: 1, + TD: chain.TD(chain.Len()), + Head: chain.blocks[chain.Len()-1].Hash(), + Genesis: chain.blocks[0].Hash(), + ForkID: chain.ForkID(), + } + if err := c.Write(status); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + + return message +} + +// waitForBlock waits for confirmation from the client that it has +// imported the given block. +func (c *Conn) waitForBlock(block *types.Block) error { + for { + req := &GetBlockHeaders{Origin: hashOrNumber{Hash: block.Hash()}, Amount: 1} + if err := c.Write(req); err != nil { + return err + } + + switch msg := c.Read().(type) { + case *BlockHeaders: + if len(*msg) > 0 { + return nil + } + time.Sleep(100 * time.Millisecond) + default: + return fmt.Errorf("invalid message: %v", msg) + } + } +} diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go new file mode 100644 index 0000000000..7866498f73 --- /dev/null +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -0,0 +1,377 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package v5test + +import ( + "bytes" + "net" + "sync" + "time" + + "github.com/ethereum/go-ethereum/internal/utesting" + "github.com/ethereum/go-ethereum/p2p/discover/v5wire" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/netutil" +) + +// Suite is the discv5 test suite. +type Suite struct { + Dest *enode.Node + Listen1, Listen2 string // listening addresses +} + +func (s *Suite) listen1(log logger) (*conn, net.PacketConn) { + c := newConn(s.Dest, log) + l := c.listen(s.Listen1) + return c, l +} + +func (s *Suite) listen2(log logger) (*conn, net.PacketConn, net.PacketConn) { + c := newConn(s.Dest, log) + l1, l2 := c.listen(s.Listen1), c.listen(s.Listen2) + return c, l1, l2 +} + +func (s *Suite) AllTests() []utesting.Test { + return []utesting.Test{ + {Name: "Ping", Fn: s.TestPing}, + {Name: "PingLargeRequestID", Fn: s.TestPingLargeRequestID}, + {Name: "PingMultiIP", Fn: s.TestPingMultiIP}, + {Name: "PingHandshakeInterrupted", Fn: s.TestPingHandshakeInterrupted}, + {Name: "TalkRequest", Fn: s.TestTalkRequest}, + {Name: "FindnodeZeroDistance", Fn: s.TestFindnodeZeroDistance}, + {Name: "FindnodeResults", Fn: s.TestFindnodeResults}, + } +} + +// This test sends PING and expects a PONG response. +func (s *Suite) TestPing(t *utesting.T) { + conn, l1 := s.listen1(t) + defer conn.close() + + ping := &v5wire.Ping{ReqID: conn.nextReqID()} + switch resp := conn.reqresp(l1, ping).(type) { + case *v5wire.Pong: + checkPong(t, resp, ping, l1) + default: + t.Fatal("expected PONG, got", resp.Name()) + } +} + +func checkPong(t *utesting.T, pong *v5wire.Pong, ping *v5wire.Ping, c net.PacketConn) { + if !bytes.Equal(pong.ReqID, ping.ReqID) { + t.Fatalf("wrong request ID %x in PONG, want %x", pong.ReqID, ping.ReqID) + } + if !pong.ToIP.Equal(laddr(c).IP) { + t.Fatalf("wrong destination IP %v in PONG, want %v", pong.ToIP, laddr(c).IP) + } + if int(pong.ToPort) != laddr(c).Port { + t.Fatalf("wrong destination port %v in PONG, want %v", pong.ToPort, laddr(c).Port) + } +} + +// This test sends PING with a 9-byte request ID, which isn't allowed by the spec. +// The remote node should not respond. +func (s *Suite) TestPingLargeRequestID(t *utesting.T) { + conn, l1 := s.listen1(t) + defer conn.close() + + ping := &v5wire.Ping{ReqID: make([]byte, 9)} + switch resp := conn.reqresp(l1, ping).(type) { + case *v5wire.Pong: + t.Errorf("PONG response with unknown request ID %x", resp.ReqID) + case *readError: + if resp.err == v5wire.ErrInvalidReqID { + t.Error("response with oversized request ID") + } else if !netutil.IsTimeout(resp.err) { + t.Error(resp) + } + } +} + +// In this test, a session is established from one IP as usual. The session is then reused +// on another IP, which shouldn't work. The remote node should respond with WHOAREYOU for +// the attempt from a different IP. +func (s *Suite) TestPingMultiIP(t *utesting.T) { + conn, l1, l2 := s.listen2(t) + defer conn.close() + + // Create the session on l1. + ping := &v5wire.Ping{ReqID: conn.nextReqID()} + resp := conn.reqresp(l1, ping) + if resp.Kind() != v5wire.PongMsg { + t.Fatal("expected PONG, got", resp) + } + checkPong(t, resp.(*v5wire.Pong), ping, l1) + + // Send on l2. This reuses the session because there is only one codec. + ping2 := &v5wire.Ping{ReqID: conn.nextReqID()} + conn.write(l2, ping2, nil) + switch resp := conn.read(l2).(type) { + case *v5wire.Pong: + t.Fatalf("remote responded to PING from %v for session on IP %v", laddr(l2).IP, laddr(l1).IP) + case *v5wire.Whoareyou: + t.Logf("got WHOAREYOU for new session as expected") + resp.Node = s.Dest + conn.write(l2, ping2, resp) + default: + t.Fatal("expected WHOAREYOU, got", resp) + } + + // Catch the PONG on l2. + switch resp := conn.read(l2).(type) { + case *v5wire.Pong: + checkPong(t, resp, ping2, l2) + default: + t.Fatal("expected PONG, got", resp) + } + + // Try on l1 again. + ping3 := &v5wire.Ping{ReqID: conn.nextReqID()} + conn.write(l1, ping3, nil) + switch resp := conn.read(l1).(type) { + case *v5wire.Pong: + t.Fatalf("remote responded to PING from %v for session on IP %v", laddr(l1).IP, laddr(l2).IP) + case *v5wire.Whoareyou: + t.Logf("got WHOAREYOU for new session as expected") + default: + t.Fatal("expected WHOAREYOU, got", resp) + } +} + +// This test starts a handshake, but doesn't finish it and sends a second ordinary message +// packet instead of a handshake message packet. The remote node should respond with +// another WHOAREYOU challenge for the second packet. +func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) { + conn, l1 := s.listen1(t) + defer conn.close() + + // First PING triggers challenge. + ping := &v5wire.Ping{ReqID: conn.nextReqID()} + conn.write(l1, ping, nil) + switch resp := conn.read(l1).(type) { + case *v5wire.Whoareyou: + t.Logf("got WHOAREYOU for PING") + default: + t.Fatal("expected WHOAREYOU, got", resp) + } + + // Send second PING. + ping2 := &v5wire.Ping{ReqID: conn.nextReqID()} + switch resp := conn.reqresp(l1, ping2).(type) { + case *v5wire.Pong: + checkPong(t, resp, ping2, l1) + default: + t.Fatal("expected WHOAREYOU, got", resp) + } +} + +// This test sends TALKREQ and expects an empty TALKRESP response. +func (s *Suite) TestTalkRequest(t *utesting.T) { + conn, l1 := s.listen1(t) + defer conn.close() + + // Non-empty request ID. + id := conn.nextReqID() + resp := conn.reqresp(l1, &v5wire.TalkRequest{ReqID: id, Protocol: "test-protocol"}) + switch resp := resp.(type) { + case *v5wire.TalkResponse: + if !bytes.Equal(resp.ReqID, id) { + t.Fatalf("wrong request ID %x in TALKRESP, want %x", resp.ReqID, id) + } + if len(resp.Message) > 0 { + t.Fatalf("non-empty message %x in TALKRESP", resp.Message) + } + default: + t.Fatal("expected TALKRESP, got", resp.Name()) + } + + // Empty request ID. + resp = conn.reqresp(l1, &v5wire.TalkRequest{Protocol: "test-protocol"}) + switch resp := resp.(type) { + case *v5wire.TalkResponse: + if len(resp.ReqID) > 0 { + t.Fatalf("wrong request ID %x in TALKRESP, want empty byte array", resp.ReqID) + } + if len(resp.Message) > 0 { + t.Fatalf("non-empty message %x in TALKRESP", resp.Message) + } + default: + t.Fatal("expected TALKRESP, got", resp.Name()) + } +} + +// This test checks that the remote node returns itself for FINDNODE with distance zero. +func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { + conn, l1 := s.listen1(t) + defer conn.close() + + nodes, err := conn.findnode(l1, []uint{0}) + if err != nil { + t.Fatal(err) + } + if len(nodes) != 1 { + t.Fatalf("remote returned more than one node for FINDNODE [0]") + } + if nodes[0].ID() != conn.remote.ID() { + t.Errorf("ID of response node is %v, want %v", nodes[0].ID(), conn.remote.ID()) + } +} + +// In this test, multiple nodes ping the node under test. After waiting for them to be +// accepted into the remote table, the test checks that they are returned by FINDNODE. +func (s *Suite) TestFindnodeResults(t *utesting.T) { + // Create bystanders. + nodes := make([]*bystander, 5) + added := make(chan enode.ID, len(nodes)) + for i := range nodes { + nodes[i] = newBystander(t, s, added) + defer nodes[i].close() + } + + // Get them added to the remote table. + timeout := 60 * time.Second + timeoutCh := time.After(timeout) + for count := 0; count < len(nodes); { + select { + case id := <-added: + t.Logf("bystander node %v added to remote table", id) + count++ + case <-timeoutCh: + t.Errorf("remote added %d bystander nodes in %v, need %d to continue", count, timeout, len(nodes)) + t.Logf("this can happen if the node has a non-empty table from previous runs") + return + } + } + t.Logf("all %d bystander nodes were added", len(nodes)) + + // Collect our nodes by distance. + var dists []uint + expect := make(map[enode.ID]*enode.Node) + for _, bn := range nodes { + n := bn.conn.localNode.Node() + expect[n.ID()] = n + d := uint(enode.LogDist(n.ID(), s.Dest.ID())) + if !containsUint(dists, d) { + dists = append(dists, d) + } + } + + // Send FINDNODE for all distances. + conn, l1 := s.listen1(t) + defer conn.close() + foundNodes, err := conn.findnode(l1, dists) + if err != nil { + t.Fatal(err) + } + t.Logf("remote returned %d nodes for distance list %v", len(foundNodes), dists) + for _, n := range foundNodes { + delete(expect, n.ID()) + } + if len(expect) > 0 { + t.Errorf("missing %d nodes in FINDNODE result", len(expect)) + t.Logf("this can happen if the test is run multiple times in quick succession") + t.Logf("and the remote node hasn't removed dead nodes from previous runs yet") + } else { + t.Logf("all %d expected nodes were returned", len(nodes)) + } +} + +// A bystander is a node whose only purpose is filling a spot in the remote table. +type bystander struct { + dest *enode.Node + conn *conn + l net.PacketConn + + addedCh chan enode.ID + done sync.WaitGroup +} + +func newBystander(t *utesting.T, s *Suite, added chan enode.ID) *bystander { + conn, l := s.listen1(t) + conn.setEndpoint(l) // bystander nodes need IP/port to get pinged + bn := &bystander{ + conn: conn, + l: l, + dest: s.Dest, + addedCh: added, + } + bn.done.Add(1) + go bn.loop() + return bn +} + +// id returns the node ID of the bystander. +func (bn *bystander) id() enode.ID { + return bn.conn.localNode.ID() +} + +// close shuts down loop. +func (bn *bystander) close() { + bn.conn.close() + bn.done.Wait() +} + +// loop answers packets from the remote node until quit. +func (bn *bystander) loop() { + defer bn.done.Done() + + var ( + lastPing time.Time + wasAdded bool + ) + for { + // Ping the remote node. + if !wasAdded && time.Since(lastPing) > 10*time.Second { + bn.conn.reqresp(bn.l, &v5wire.Ping{ + ReqID: bn.conn.nextReqID(), + ENRSeq: bn.dest.Seq(), + }) + lastPing = time.Now() + } + // Answer packets. + switch p := bn.conn.read(bn.l).(type) { + case *v5wire.Ping: + bn.conn.write(bn.l, &v5wire.Pong{ + ReqID: p.ReqID, + ENRSeq: bn.conn.localNode.Seq(), + ToIP: bn.dest.IP(), + ToPort: uint16(bn.dest.UDP()), + }, nil) + wasAdded = true + bn.notifyAdded() + case *v5wire.Findnode: + bn.conn.write(bn.l, &v5wire.Nodes{ReqID: p.ReqID, Total: 1}, nil) + wasAdded = true + bn.notifyAdded() + case *v5wire.TalkRequest: + bn.conn.write(bn.l, &v5wire.TalkResponse{ReqID: p.ReqID}, nil) + case *readError: + if !netutil.IsTemporaryError(p.err) { + bn.conn.logf("shutting down: %v", p.err) + return + } + } + } +} + +func (bn *bystander) notifyAdded() { + if bn.addedCh != nil { + bn.addedCh <- bn.id() + bn.addedCh = nil + } +} diff --git a/cmd/devp2p/internal/v5test/framework.go b/cmd/devp2p/internal/v5test/framework.go new file mode 100644 index 0000000000..9eac37520f --- /dev/null +++ b/cmd/devp2p/internal/v5test/framework.go @@ -0,0 +1,263 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package v5test + +import ( + "bytes" + "crypto/ecdsa" + "encoding/binary" + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/discover/v5wire" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" +) + +// readError represents an error during packet reading. +// This exists to facilitate type-switching on the result of conn.read. +type readError struct { + err error +} + +func (p *readError) Kind() byte { return 99 } +func (p *readError) Name() string { return fmt.Sprintf("error: %v", p.err) } +func (p *readError) Error() string { return p.err.Error() } +func (p *readError) Unwrap() error { return p.err } +func (p *readError) RequestID() []byte { return nil } +func (p *readError) SetRequestID([]byte) {} + +// readErrorf creates a readError with the given text. +func readErrorf(format string, args ...interface{}) *readError { + return &readError{fmt.Errorf(format, args...)} +} + +// This is the response timeout used in tests. +const waitTime = 300 * time.Millisecond + +// conn is a connection to the node under test. +type conn struct { + localNode *enode.LocalNode + localKey *ecdsa.PrivateKey + remote *enode.Node + remoteAddr *net.UDPAddr + listeners []net.PacketConn + + log logger + codec *v5wire.Codec + lastRequest v5wire.Packet + lastChallenge *v5wire.Whoareyou + idCounter uint32 +} + +type logger interface { + Logf(string, ...interface{}) +} + +// newConn sets up a connection to the given node. +func newConn(dest *enode.Node, log logger) *conn { + key, err := crypto.GenerateKey() + if err != nil { + panic(err) + } + db, err := enode.OpenDB("") + if err != nil { + panic(err) + } + ln := enode.NewLocalNode(db, key) + + return &conn{ + localKey: key, + localNode: ln, + remote: dest, + remoteAddr: &net.UDPAddr{IP: dest.IP(), Port: dest.UDP()}, + codec: v5wire.NewCodec(ln, key, mclock.System{}), + log: log, + } +} + +func (tc *conn) setEndpoint(c net.PacketConn) { + tc.localNode.SetStaticIP(laddr(c).IP) + tc.localNode.SetFallbackUDP(laddr(c).Port) +} + +func (tc *conn) listen(ip string) net.PacketConn { + l, err := net.ListenPacket("udp", fmt.Sprintf("%v:0", ip)) + if err != nil { + panic(err) + } + tc.listeners = append(tc.listeners, l) + return l +} + +// close shuts down all listeners and the local node. +func (tc *conn) close() { + for _, l := range tc.listeners { + l.Close() + } + tc.localNode.Database().Close() +} + +// nextReqID creates a request id. +func (tc *conn) nextReqID() []byte { + id := make([]byte, 4) + tc.idCounter++ + binary.BigEndian.PutUint32(id, tc.idCounter) + return id +} + +// reqresp performs a request/response interaction on the given connection. +// The request is retried if a handshake is requested. +func (tc *conn) reqresp(c net.PacketConn, req v5wire.Packet) v5wire.Packet { + reqnonce := tc.write(c, req, nil) + switch resp := tc.read(c).(type) { + case *v5wire.Whoareyou: + if resp.Nonce != reqnonce { + return readErrorf("wrong nonce %x in WHOAREYOU (want %x)", resp.Nonce[:], reqnonce[:]) + } + resp.Node = tc.remote + tc.write(c, req, resp) + return tc.read(c) + default: + return resp + } +} + +// findnode sends a FINDNODE request and waits for its responses. +func (tc *conn) findnode(c net.PacketConn, dists []uint) ([]*enode.Node, error) { + var ( + findnode = &v5wire.Findnode{ReqID: tc.nextReqID(), Distances: dists} + reqnonce = tc.write(c, findnode, nil) + first = true + total uint8 + results []*enode.Node + ) + for n := 1; n > 0; { + switch resp := tc.read(c).(type) { + case *v5wire.Whoareyou: + // Handle handshake. + if resp.Nonce == reqnonce { + resp.Node = tc.remote + tc.write(c, findnode, resp) + } else { + return nil, fmt.Errorf("unexpected WHOAREYOU (nonce %x), waiting for NODES", resp.Nonce[:]) + } + case *v5wire.Ping: + // Handle ping from remote. + tc.write(c, &v5wire.Pong{ + ReqID: resp.ReqID, + ENRSeq: tc.localNode.Seq(), + }, nil) + case *v5wire.Nodes: + // Got NODES! Check request ID. + if !bytes.Equal(resp.ReqID, findnode.ReqID) { + return nil, fmt.Errorf("NODES response has wrong request id %x", resp.ReqID) + } + // Check total count. It should be greater than one + // and needs to be the same across all responses. + if first { + if resp.Total == 0 || resp.Total > 6 { + return nil, fmt.Errorf("invalid NODES response 'total' %d (not in (0,7))", resp.Total) + } + total = resp.Total + n = int(total) - 1 + first = false + } else { + n-- + if resp.Total != total { + return nil, fmt.Errorf("invalid NODES response 'total' %d (!= %d)", resp.Total, total) + } + } + // Check nodes. + nodes, err := checkRecords(resp.Nodes) + if err != nil { + return nil, fmt.Errorf("invalid node in NODES response: %v", err) + } + results = append(results, nodes...) + default: + return nil, fmt.Errorf("expected NODES, got %v", resp) + } + } + return results, nil +} + +// write sends a packet on the given connection. +func (tc *conn) write(c net.PacketConn, p v5wire.Packet, challenge *v5wire.Whoareyou) v5wire.Nonce { + packet, nonce, err := tc.codec.Encode(tc.remote.ID(), tc.remoteAddr.String(), p, challenge) + if err != nil { + panic(fmt.Errorf("can't encode %v packet: %v", p.Name(), err)) + } + if _, err := c.WriteTo(packet, tc.remoteAddr); err != nil { + tc.logf("Can't send %s: %v", p.Name(), err) + } else { + tc.logf(">> %s", p.Name()) + } + return nonce +} + +// read waits for an incoming packet on the given connection. +func (tc *conn) read(c net.PacketConn) v5wire.Packet { + buf := make([]byte, 1280) + if err := c.SetReadDeadline(time.Now().Add(waitTime)); err != nil { + return &readError{err} + } + n, fromAddr, err := c.ReadFrom(buf) + if err != nil { + return &readError{err} + } + _, _, p, err := tc.codec.Decode(buf[:n], fromAddr.String()) + if err != nil { + return &readError{err} + } + tc.logf("<< %s", p.Name()) + return p +} + +// logf prints to the test log. +func (tc *conn) logf(format string, args ...interface{}) { + if tc.log != nil { + tc.log.Logf("(%s) %s", tc.localNode.ID().TerminalString(), fmt.Sprintf(format, args...)) + } +} + +func laddr(c net.PacketConn) *net.UDPAddr { + return c.LocalAddr().(*net.UDPAddr) +} + +func checkRecords(records []*enr.Record) ([]*enode.Node, error) { + nodes := make([]*enode.Node, len(records)) + for i := range records { + n, err := enode.New(enode.ValidSchemes, records[i]) + if err != nil { + return nil, err + } + nodes[i] = n + } + return nodes, nil +} + +func containsUint(ints []uint, x uint) bool { + for i := range ints { + if ints[i] == x { + return true + } + } + return false +} diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 781e6e3026..009e9090a4 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -45,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -243,6 +244,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u cfg.SyncMode = downloader.LightSync cfg.NetworkId = network cfg.Genesis = genesis + utils.SetDNSDiscoveryDefaults(&cfg, genesis.ToBlock(nil).Hash()) lesBackend, err := les.New(stack, &cfg) if err != nil { return nil, fmt.Errorf("Failed to register the Ethereum service: %w", err) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 4455956411..2240400462 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -223,8 +223,8 @@ var ( utils.IPCDisabledFlag, utils.IPCPathFlag, utils.InsecureUnlockAllowedFlag, - utils.RPCGlobalGasCap, - utils.RPCGlobalTxFeeCap, + utils.RPCGlobalGasCapFlag, + utils.RPCGlobalTxFeeCapFlag, } whisperFlags = []cli.Flag{ diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index da41bca8da..22f0293d82 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -149,8 +149,8 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.GraphQLEnabledFlag, utils.GraphQLCORSDomainFlag, utils.GraphQLVirtualHostsFlag, - utils.RPCGlobalGasCap, - utils.RPCGlobalTxFeeCap, + utils.RPCGlobalGasCapFlag, + utils.RPCGlobalTxFeeCapFlag, utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c5a55cc3b9..ed14fa5835 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -470,12 +470,12 @@ var ( Name: "allow-insecure-unlock", Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http", } - RPCGlobalGasCap = cli.Uint64Flag{ + RPCGlobalGasCapFlag = cli.Uint64Flag{ Name: "rpc.gascap", Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", Value: eth.DefaultConfig.RPCGasCap, } - RPCGlobalTxFeeCap = cli.Float64Flag{ + RPCGlobalTxFeeCapFlag = cli.Float64Flag{ Name: "rpc.txfeecap", Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", Value: eth.DefaultConfig.RPCTxFeeCap, @@ -1837,16 +1837,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(EVMInterpreterFlag.Name) { cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name) } - if ctx.GlobalIsSet(RPCGlobalGasCap.Name) { - cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCap.Name) + if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) { + cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name) } if cfg.RPCGasCap != 0 { log.Info("Set global gas cap", "cap", cfg.RPCGasCap) } else { log.Info("Global gas cap disabled") } - if ctx.GlobalIsSet(RPCGlobalTxFeeCap.Name) { - cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCap.Name) + if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) { + cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name) } if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) { urls := ctx.GlobalString(DNSDiscoveryFlag.Name) @@ -1867,19 +1867,19 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { cfg.NetworkId = 3 } cfg.Genesis = core.DefaultRopstenGenesisBlock() - setDNSDiscoveryDefaults(cfg, params.RopstenGenesisHash) + SetDNSDiscoveryDefaults(cfg, params.RopstenGenesisHash) case ctx.GlobalBool(RinkebyFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { cfg.NetworkId = 4 } cfg.Genesis = core.DefaultRinkebyGenesisBlock() - setDNSDiscoveryDefaults(cfg, params.RinkebyGenesisHash) + SetDNSDiscoveryDefaults(cfg, params.RinkebyGenesisHash) case ctx.GlobalBool(GoerliFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { cfg.NetworkId = 5 } cfg.Genesis = core.DefaultGoerliGenesisBlock() - setDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash) + SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash) case ctx.GlobalBool(YoloV1Flag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { cfg.NetworkId = 133519467574833 // "yolov1" @@ -1933,14 +1933,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { } default: if cfg.NetworkId == 1 { - setDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) + SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) } } } -// setDNSDiscoveryDefaults configures DNS discovery with the given URL if +// SetDNSDiscoveryDefaults configures DNS discovery with the given URL if // no URLs are set. -func setDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) { +func SetDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) { if cfg.DiscoveryURLs != nil { return // already set through flags/config } diff --git a/common/math/big.go b/common/math/big.go index 17a57df9dc..1af5b4d879 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -67,6 +67,40 @@ func (i *HexOrDecimal256) MarshalText() ([]byte, error) { return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil } +// Decimal256 unmarshals big.Int as a decimal string. When unmarshalling, +// it however accepts either "0x"-prefixed (hex encoded) or non-prefixed (decimal) +type Decimal256 big.Int + +// NewHexOrDecimal256 creates a new Decimal256 +func NewDecimal256(x int64) *Decimal256 { + b := big.NewInt(x) + d := Decimal256(*b) + return &d +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (i *Decimal256) UnmarshalText(input []byte) error { + bigint, ok := ParseBig256(string(input)) + if !ok { + return fmt.Errorf("invalid hex or decimal integer %q", input) + } + *i = Decimal256(*bigint) + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (i *Decimal256) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// String implements Stringer. +func (i *Decimal256) String() string { + if i == nil { + return "0" + } + return fmt.Sprintf("%#d", (*big.Int)(i)) +} + // ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax. // Leading zeros are accepted. The empty string parses as zero. func ParseBig256(s string) (*big.Int, bool) { diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 35a00af5be..fcb1121f57 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -521,7 +521,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header c.lock.RUnlock() } // Set the correct difficulty - header.Difficulty = CalcDifficulty(snap, c.signer) + header.Difficulty = calcDifficulty(snap, c.signer) // Ensure the extra data has all its components if len(header.Extra) < extraVanity { @@ -653,20 +653,18 @@ func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, res } // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty -// that a new block should have based on the previous blocks in the chain and the -// current signer. +// that a new block should have: +// * DIFF_NOTURN(2) if BLOCK_NUMBER % SIGNER_COUNT != SIGNER_INDEX +// * DIFF_INTURN(1) if BLOCK_NUMBER % SIGNER_COUNT == SIGNER_INDEX func (c *Clique) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil) if err != nil { return nil } - return CalcDifficulty(snap, c.signer) + return calcDifficulty(snap, c.signer) } -// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty -// that a new block should have based on the previous blocks in the chain and the -// current signer. -func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int { +func calcDifficulty(snap *Snapshot, signer common.Address) *big.Int { if snap.inturn(snap.Number+1, signer) { return new(big.Int).Set(diffInTurn) } diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go index 3890fc51dd..039ba919bf 100644 --- a/consensus/clique/snapshot_test.go +++ b/consensus/clique/snapshot_test.go @@ -423,7 +423,7 @@ func TestClique(t *testing.T) { }) // Iterate through the blocks and seal them individually for j, block := range blocks { - // Geth the header and prepare it for signing + // Get the header and prepare it for signing header := block.Header() if j > 0 { header.ParentHash = blocks[j-1].Hash() diff --git a/console/bridge.go b/console/bridge.go index 9303496b28..1a23269194 100644 --- a/console/bridge.go +++ b/console/bridge.go @@ -353,14 +353,14 @@ func (b *bridge) SleepBlocks(call jsre.Call) (goja.Value, error) { } // Poll the current block number until either it or a timeout is reached. - var ( - deadline = time.Now().Add(time.Duration(sleep) * time.Second) - lastNumber = ^hexutil.Uint64(0) - ) + deadline := time.Now().Add(time.Duration(sleep) * time.Second) + var lastNumber hexutil.Uint64 + if err := b.client.Call(&lastNumber, "eth_blockNumber"); err != nil { + return nil, err + } for time.Now().Before(deadline) { var number hexutil.Uint64 - err := b.client.Call(&number, "eth_blockNumber") - if err != nil { + if err := b.client.Call(&number, "eth_blockNumber"); err != nil { return nil, err } if number != lastNumber { diff --git a/contracts/checkpointoracle/contract/oracle.go b/contracts/checkpointoracle/contract/oracle.go index 998ccb93c2..a4a308f5c5 100644 --- a/contracts/checkpointoracle/contract/oracle.go +++ b/contracts/checkpointoracle/contract/oracle.go @@ -27,10 +27,17 @@ var ( ) // CheckpointOracleABI is the input ABI used to generate the binding from. -const CheckpointOracleABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"GetAllAdmin\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetLatestCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"},{\"name\":\"\",\"type\":\"bytes32\"},{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_recentNumber\",\"type\":\"uint256\"},{\"name\":\"_recentHash\",\"type\":\"bytes32\"},{\"name\":\"_hash\",\"type\":\"bytes32\"},{\"name\":\"_sectionIndex\",\"type\":\"uint64\"},{\"name\":\"v\",\"type\":\"uint8[]\"},{\"name\":\"r\",\"type\":\"bytes32[]\"},{\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"SetCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_adminlist\",\"type\":\"address[]\"},{\"name\":\"_sectionSize\",\"type\":\"uint256\"},{\"name\":\"_processConfirms\",\"type\":\"uint256\"},{\"name\":\"_threshold\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"index\",\"type\":\"uint64\"},{\"indexed\":false,\"name\":\"checkpointHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"v\",\"type\":\"uint8\"},{\"indexed\":false,\"name\":\"r\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"NewCheckpointVote\",\"type\":\"event\"}]" +const CheckpointOracleABI = "[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_adminlist\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"_sectionSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_processConfirms\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"checkpointHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"NewCheckpointVote\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"GetAllAdmin\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GetLatestCheckpoint\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_recentNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_recentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"_hash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"_sectionIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint8[]\",\"name\":\"v\",\"type\":\"uint8[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"r\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"SetCheckpoint\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +// CheckpointOracleFuncSigs maps the 4-byte function signature to its string representation. +var CheckpointOracleFuncSigs = map[string]string{ + "45848dfc": "GetAllAdmin()", + "4d6a304c": "GetLatestCheckpoint()", + "d459fc46": "SetCheckpoint(uint256,bytes32,bytes32,uint64,uint8[],bytes32[],bytes32[])", +} // CheckpointOracleBin is the compiled bytecode used for deploying new contracts. -const CheckpointOracleBin = `0x608060405234801561001057600080fd5b506040516108153803806108158339818101604052608081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8201602081018481111561005e57600080fd5b815185602082028301116401000000008211171561007b57600080fd5b505060208201516040830151606090930151919450925060005b84518110156101415760016000808784815181106100af57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060018582815181106100fc57fe5b60209081029190910181015182546001808201855560009485529290932090920180546001600160a01b0319166001600160a01b039093169290921790915501610095565b50600592909255600655600755506106b78061015e6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806345848dfc146100465780634d6a304c1461009e578063d459fc46146100cf575b600080fd5b61004e6102b0565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561008a578181015183820152602001610072565b505050509050019250505060405180910390f35b6100a661034f565b6040805167ffffffffffffffff9094168452602084019290925282820152519081900360600190f35b61029c600480360360e08110156100e557600080fd5b81359160208101359160408201359167ffffffffffffffff6060820135169181019060a08101608082013564010000000081111561012257600080fd5b82018360208201111561013457600080fd5b8035906020019184602083028401116401000000008311171561015657600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156101a657600080fd5b8201836020820111156101b857600080fd5b803590602001918460208302840111640100000000831117156101da57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561022a57600080fd5b82018360208201111561023c57600080fd5b8035906020019184602083028401116401000000008311171561025e57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061036a945050505050565b604080519115158252519081900360200190f35b6060806001805490506040519080825280602002602001820160405280156102e2578160200160208202803883390190505b50905060005b60015481101561034957600181815481106102ff57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828151811061032957fe5b6001600160a01b03909216602092830291909101909101526001016102e8565b50905090565b60025460045460035467ffffffffffffffff90921691909192565b3360009081526020819052604081205460ff1661038657600080fd5b8688401461039357600080fd5b82518451146103a157600080fd5b81518451146103af57600080fd5b6006546005548660010167ffffffffffffffff1602014310156103d457506000610677565b60025467ffffffffffffffff90811690861610156103f457506000610677565b60025467ffffffffffffffff8681169116148015610426575067ffffffffffffffff8516151580610426575060035415155b1561043357506000610677565b8561044057506000610677565b60408051601960f81b6020808301919091526000602183018190523060601b60228401526001600160c01b031960c08a901b166036840152603e8084018b905284518085039091018152605e909301909352815191012090805b86518110156106715760006001848984815181106104b457fe5b60200260200101518985815181106104c857fe5b60200260200101518986815181106104dc57fe5b602002602001015160405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561053b573d6000803e3d6000fd5b505060408051601f1901516001600160a01b03811660009081526020819052919091205490925060ff16905061057057600080fd5b826001600160a01b0316816001600160a01b03161161058e57600080fd5b8092508867ffffffffffffffff167fce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a418b8a85815181106105ca57fe5b60200260200101518a86815181106105de57fe5b60200260200101518a87815181106105f257fe5b6020026020010151604051808581526020018460ff1660ff16815260200183815260200182815260200194505050505060405180910390a260075482600101106106685750505060048790555050436003556002805467ffffffffffffffff191667ffffffffffffffff86161790556001610677565b5060010161049a565b50600080fd5b97965050505050505056fea265627a7a723058207f6a191ce575596a2f1e907c8c0a01003d16b69fb2c4f432d10878e8c0a99a0264736f6c634300050a0032` +var CheckpointOracleBin = "0x608060405234801561001057600080fd5b506040516108703803806108708339818101604052608081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825186602082028301116401000000008211171561008557600080fd5b82525081516020918201928201910280838360005b838110156100b257818101518382015260200161009a565b50505050919091016040908152602083015190830151606090930151909450919250600090505b84518110156101855760016000808784815181106100f357fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600185828151811061014057fe5b60209081029190910181015182546001808201855560009485529290932090920180546001600160a01b0319166001600160a01b0390931692909217909155016100d9565b50600592909255600655600755506106ce806101a26000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806345848dfc146100465780634d6a304c1461009e578063d459fc46146100cf575b600080fd5b61004e6102b0565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561008a578181015183820152602001610072565b505050509050019250505060405180910390f35b6100a6610365565b6040805167ffffffffffffffff9094168452602084019290925282820152519081900360600190f35b61029c600480360360e08110156100e557600080fd5b81359160208101359160408201359167ffffffffffffffff6060820135169181019060a08101608082013564010000000081111561012257600080fd5b82018360208201111561013457600080fd5b8035906020019184602083028401116401000000008311171561015657600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156101a657600080fd5b8201836020820111156101b857600080fd5b803590602001918460208302840111640100000000831117156101da57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561022a57600080fd5b82018360208201111561023c57600080fd5b8035906020019184602083028401116401000000008311171561025e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610380945050505050565b604080519115158252519081900360200190f35b600154606090819067ffffffffffffffff811180156102ce57600080fd5b506040519080825280602002602001820160405280156102f8578160200160208202803683370190505b50905060005b60015481101561035f576001818154811061031557fe5b9060005260206000200160009054906101000a90046001600160a01b031682828151811061033f57fe5b6001600160a01b03909216602092830291909101909101526001016102fe565b50905090565b60025460045460035467ffffffffffffffff90921691909192565b3360009081526020819052604081205460ff1661039c57600080fd5b868840146103a957600080fd5b82518451146103b757600080fd5b81518451146103c557600080fd5b6006546005548660010167ffffffffffffffff1602014310156103ea5750600061068d565b60025467ffffffffffffffff908116908616101561040a5750600061068d565b60025467ffffffffffffffff868116911614801561043c575067ffffffffffffffff851615158061043c575060035415155b156104495750600061068d565b856104565750600061068d565b60408051601960f81b6020808301919091526000602183018190523060601b60228401526001600160c01b031960c08a901b166036840152603e8084018b905284518085039091018152605e909301909352815191012090805b86518110156106875760006001848984815181106104ca57fe5b60200260200101518985815181106104de57fe5b60200260200101518986815181106104f257fe5b602002602001015160405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610551573d6000803e3d6000fd5b505060408051601f1901516001600160a01b03811660009081526020819052919091205490925060ff16905061058657600080fd5b826001600160a01b0316816001600160a01b0316116105a457600080fd5b8092508867ffffffffffffffff167fce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a418b8a85815181106105e057fe5b60200260200101518a86815181106105f457fe5b60200260200101518a878151811061060857fe5b6020026020010151604051808581526020018460ff1660ff16815260200183815260200182815260200194505050505060405180910390a2600754826001011061067e5750505060048790555050436003556002805467ffffffffffffffff191667ffffffffffffffff8616179055600161068d565b506001016104b0565b50600080fd5b97965050505050505056fea26469706673582212202ddf9eda76bf59c0fc65584c0b22d84ecef2c703765de60439596d6ac34c2b7264736f6c634300060b0033" // DeployCheckpointOracle deploys a new Ethereum contract, binding an instance of CheckpointOracle to it. func DeployCheckpointOracle(auth *bind.TransactOpts, backend bind.ContractBackend, _adminlist []common.Address, _sectionSize *big.Int, _processConfirms *big.Int, _threshold *big.Int) (common.Address, *types.Transaction, *CheckpointOracle, error) { @@ -38,6 +45,7 @@ func DeployCheckpointOracle(auth *bind.TransactOpts, backend bind.ContractBacken if err != nil { return common.Address{}, nil, nil, err } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(CheckpointOracleBin), backend, _adminlist, _sectionSize, _processConfirms, _threshold) if err != nil { return common.Address{}, nil, nil, err @@ -153,7 +161,7 @@ func bindCheckpointOracle(address common.Address, caller bind.ContractCaller, tr // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_CheckpointOracle *CheckpointOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_CheckpointOracle *CheckpointOracleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _CheckpointOracle.Contract.CheckpointOracleCaller.contract.Call(opts, result, method, params...) } @@ -172,7 +180,7 @@ func (_CheckpointOracle *CheckpointOracleRaw) Transact(opts *bind.TransactOpts, // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_CheckpointOracle *CheckpointOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_CheckpointOracle *CheckpointOracleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _CheckpointOracle.Contract.contract.Call(opts, result, method, params...) } @@ -189,58 +197,64 @@ func (_CheckpointOracle *CheckpointOracleTransactorRaw) Transact(opts *bind.Tran // GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc. // -// Solidity: function GetAllAdmin() constant returns(address[]) +// Solidity: function GetAllAdmin() view returns(address[]) func (_CheckpointOracle *CheckpointOracleCaller) GetAllAdmin(opts *bind.CallOpts) ([]common.Address, error) { - var ( - ret0 = new([]common.Address) - ) - out := ret0 - err := _CheckpointOracle.contract.Call(opts, out, "GetAllAdmin") - return *ret0, err + var out []interface{} + err := _CheckpointOracle.contract.Call(opts, &out, "GetAllAdmin") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + } // GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc. // -// Solidity: function GetAllAdmin() constant returns(address[]) +// Solidity: function GetAllAdmin() view returns(address[]) func (_CheckpointOracle *CheckpointOracleSession) GetAllAdmin() ([]common.Address, error) { return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts) } // GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc. // -// Solidity: function GetAllAdmin() constant returns(address[]) +// Solidity: function GetAllAdmin() view returns(address[]) func (_CheckpointOracle *CheckpointOracleCallerSession) GetAllAdmin() ([]common.Address, error) { return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts) } // GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c. // -// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256) +// Solidity: function GetLatestCheckpoint() view returns(uint64, bytes32, uint256) func (_CheckpointOracle *CheckpointOracleCaller) GetLatestCheckpoint(opts *bind.CallOpts) (uint64, [32]byte, *big.Int, error) { - var ( - ret0 = new(uint64) - ret1 = new([32]byte) - ret2 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, + var out []interface{} + err := _CheckpointOracle.contract.Call(opts, &out, "GetLatestCheckpoint") + + if err != nil { + return *new(uint64), *new([32]byte), *new(*big.Int), err } - err := _CheckpointOracle.contract.Call(opts, out, "GetLatestCheckpoint") - return *ret0, *ret1, *ret2, err + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + out1 := *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) + out2 := *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + + return out0, out1, out2, err + } // GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c. // -// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256) +// Solidity: function GetLatestCheckpoint() view returns(uint64, bytes32, uint256) func (_CheckpointOracle *CheckpointOracleSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) { return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts) } // GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c. // -// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256) +// Solidity: function GetLatestCheckpoint() view returns(uint64, bytes32, uint256) func (_CheckpointOracle *CheckpointOracleCallerSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) { return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts) } diff --git a/contracts/checkpointoracle/contract/oracle.sol b/contracts/checkpointoracle/contract/oracle.sol index 0106447273..65bac09d28 100644 --- a/contracts/checkpointoracle/contract/oracle.sol +++ b/contracts/checkpointoracle/contract/oracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.5.10; +pragma solidity ^0.6.0; /** * @title CheckpointOracle diff --git a/core/block_validator.go b/core/block_validator.go index d3b98ab3ca..b86790a17f 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -62,7 +62,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash) } - if hash := types.DeriveSha(block.Transactions(), new(trie.Trie)); hash != header.TxHash { + if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash { return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) } if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { @@ -92,7 +92,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) } // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]])) - receiptSha := types.DeriveSha(receipts, new(trie.Trie)) + receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil)) if receiptSha != header.ReceiptHash { return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } diff --git a/core/bloombits/generator.go b/core/bloombits/generator.go index ae07481ada..646151db0b 100644 --- a/core/bloombits/generator.go +++ b/core/bloombits/generator.go @@ -65,18 +65,23 @@ func (b *Generator) AddBloom(index uint, bloom types.Bloom) error { } // Rotate the bloom and insert into our collection byteIndex := b.nextSec / 8 - bitMask := byte(1) << byte(7-b.nextSec%8) - - for i := 0; i < types.BloomBitLength; i++ { - bloomByteIndex := types.BloomByteLength - 1 - i/8 - bloomBitMask := byte(1) << byte(i%8) - - if (bloom[bloomByteIndex] & bloomBitMask) != 0 { - b.blooms[i][byteIndex] |= bitMask + bitIndex := byte(7 - b.nextSec%8) + for byt := 0; byt < types.BloomByteLength; byt++ { + bloomByte := bloom[types.BloomByteLength-1-byt] + if bloomByte == 0 { + continue } + base := 8 * byt + b.blooms[base+7][byteIndex] |= ((bloomByte >> 7) & 1) << bitIndex + b.blooms[base+6][byteIndex] |= ((bloomByte >> 6) & 1) << bitIndex + b.blooms[base+5][byteIndex] |= ((bloomByte >> 5) & 1) << bitIndex + b.blooms[base+4][byteIndex] |= ((bloomByte >> 4) & 1) << bitIndex + b.blooms[base+3][byteIndex] |= ((bloomByte >> 3) & 1) << bitIndex + b.blooms[base+2][byteIndex] |= ((bloomByte >> 2) & 1) << bitIndex + b.blooms[base+1][byteIndex] |= ((bloomByte >> 1) & 1) << bitIndex + b.blooms[base][byteIndex] |= (bloomByte & 1) << bitIndex } b.nextSec++ - return nil } diff --git a/core/bloombits/generator_test.go b/core/bloombits/generator_test.go index f9bcef96e0..88e3ed6b06 100644 --- a/core/bloombits/generator_test.go +++ b/core/bloombits/generator_test.go @@ -58,3 +58,42 @@ func TestGenerator(t *testing.T) { } } } + +func BenchmarkGenerator(b *testing.B) { + var input [types.BloomBitLength][types.BloomByteLength]byte + b.Run("empty", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Crunch the input through the generator and verify the result + gen, err := NewGenerator(types.BloomBitLength) + if err != nil { + b.Fatalf("failed to create bloombit generator: %v", err) + } + for j, bloom := range input { + if err := gen.AddBloom(uint(j), bloom); err != nil { + b.Fatalf("bloom %d: failed to add: %v", i, err) + } + } + } + }) + for i := 0; i < types.BloomBitLength; i++ { + rand.Read(input[i][:]) + } + b.Run("random", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Crunch the input through the generator and verify the result + gen, err := NewGenerator(types.BloomBitLength) + if err != nil { + b.Fatalf("failed to create bloombit generator: %v", err) + } + for j, bloom := range input { + if err := gen.AddBloom(uint(j), bloom); err != nil { + b.Fatalf("bloom %d: failed to add: %v", i, err) + } + } + } + }) +} diff --git a/core/chain_indexer.go b/core/chain_indexer.go index 066bca1000..4b326c970b 100644 --- a/core/chain_indexer.go +++ b/core/chain_indexer.go @@ -94,7 +94,7 @@ type ChainIndexer struct { throttling time.Duration // Disk throttling to prevent a heavy upgrade from hogging resources log log.Logger - lock sync.RWMutex + lock sync.Mutex } // NewChainIndexer creates a new chain indexer to do background processing on diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index cf9b2b0393..a6b3e4420d 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -129,7 +129,7 @@ func (dl *diskLayer) generate(stats *generatorStats) { stats.wiping = nil stats.start = time.Now() - // If generator was aboted during wipe, return + // If generator was aborted during wipe, return case abort := <-dl.genAbort: abort <- stats return @@ -203,7 +203,10 @@ func (dl *diskLayer) generate(stats *generatorStats) { if acc.Root != emptyRoot { storeTrie, err := trie.NewSecure(acc.Root, dl.triedb) if err != nil { - log.Crit("Storage trie inaccessible for snapshot generation", "err", err) + log.Error("Generator failed to access storage trie", "accroot", dl.root, "acchash", common.BytesToHash(accIt.Key), "stroot", acc.Root, "err", err) + abort := <-dl.genAbort + abort <- stats + return } var storeMarker []byte if accMarker != nil && bytes.Equal(accountHash[:], accMarker) && len(dl.genMarker) > common.HashLength { @@ -238,6 +241,12 @@ func (dl *diskLayer) generate(stats *generatorStats) { } } } + if err := storeIt.Err; err != nil { + log.Error("Generator failed to iterate storage trie", "accroot", dl.root, "acchash", common.BytesToHash(accIt.Key), "stroot", acc.Root, "err", err) + abort := <-dl.genAbort + abort <- stats + return + } } if time.Since(logged) > 8*time.Second { stats.Log("Generating state snapshot", dl.root, accIt.Key) @@ -246,6 +255,12 @@ func (dl *diskLayer) generate(stats *generatorStats) { // Some account processed, unmark the marker accMarker = nil } + if err := accIt.Err; err != nil { + log.Error("Generator failed to iterate account trie", "root", dl.root, "err", err) + abort := <-dl.genAbort + abort <- stats + return + } // Snapshot fully generated, set the marker to nil if batch.ValueSize() > 0 { batch.Write() diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go new file mode 100644 index 0000000000..03263f3976 --- /dev/null +++ b/core/state/snapshot/generate_test.go @@ -0,0 +1,190 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package snapshot + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// Tests that snapshot generation errors out correctly in case of a missing trie +// node in the account trie. +func TestGenerateCorruptAccountTrie(t *testing.T) { + // We can't use statedb to make a test trie (circular dependency), so make + // a fake one manually. We're going with a small account trie of 3 accounts, + // without any storage slots to keep the test smaller. + var ( + diskdb = memorydb.New() + triedb = trie.NewDatabase(diskdb) + ) + tr, _ := trie.NewSecure(common.Hash{}, triedb) + acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + tr.Update([]byte("acc-1"), val) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 + + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + val, _ = rlp.EncodeToBytes(acc) + tr.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + + acc = &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + val, _ = rlp.EncodeToBytes(acc) + tr.Update([]byte("acc-3"), val) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 + tr.Commit(nil) // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978 + + // Delete an account trie leaf and ensure the generator chokes + triedb.Commit(common.HexToHash("0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978"), false, nil) + diskdb.Delete(common.HexToHash("0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7").Bytes()) + + snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978"), nil) + select { + case <-snap.genPending: + // Snapshot generation succeeded + t.Errorf("Snapshot generated against corrupt account trie") + + case <-time.After(250 * time.Millisecond): + // Not generated fast enough, hopefully blocked inside on missing trie node fail + } + // Signal abortion to the generator and wait for it to tear down + stop := make(chan *generatorStats) + snap.genAbort <- stop + <-stop +} + +// Tests that snapshot generation errors out correctly in case of a missing root +// trie node for a storage trie. It's similar to internal corruption but it is +// handled differently inside the generator. +func TestGenerateMissingStorageTrie(t *testing.T) { + // We can't use statedb to make a test trie (circular dependency), so make + // a fake one manually. We're going with a small account trie of 3 accounts, + // two of which also has the same 3-slot storage trie attached. + var ( + diskdb = memorydb.New() + triedb = trie.NewDatabase(diskdb) + ) + stTrie, _ := trie.NewSecure(common.Hash{}, triedb) + stTrie.Update([]byte("key-1"), []byte("val-1")) // 0x1314700b81afc49f94db3623ef1df38f3ed18b73a1b7ea2f6c095118cf6118a0 + stTrie.Update([]byte("key-2"), []byte("val-2")) // 0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371 + stTrie.Update([]byte("key-3"), []byte("val-3")) // 0x51c71a47af0695957647fb68766d0becee77e953df17c29b3c2f25436f055c78 + stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + + accTrie, _ := trie.NewSecure(common.Hash{}, triedb) + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + val, _ = rlp.EncodeToBytes(acc) + accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + + acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + val, _ = rlp.EncodeToBytes(acc) + accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd + + // We can only corrupt the disk database, so flush the tries out + triedb.Reference( + common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), + common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"), + ) + triedb.Reference( + common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), + common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"), + ) + triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil) + + // Delete a storage trie root and ensure the generator chokes + diskdb.Delete(common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67").Bytes()) + + snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), nil) + select { + case <-snap.genPending: + // Snapshot generation succeeded + t.Errorf("Snapshot generated against corrupt storage trie") + + case <-time.After(250 * time.Millisecond): + // Not generated fast enough, hopefully blocked inside on missing trie node fail + } + // Signal abortion to the generator and wait for it to tear down + stop := make(chan *generatorStats) + snap.genAbort <- stop + <-stop +} + +// Tests that snapshot generation errors out correctly in case of a missing trie +// node in a storage trie. +func TestGenerateCorruptStorageTrie(t *testing.T) { + // We can't use statedb to make a test trie (circular dependency), so make + // a fake one manually. We're going with a small account trie of 3 accounts, + // two of which also has the same 3-slot storage trie attached. + var ( + diskdb = memorydb.New() + triedb = trie.NewDatabase(diskdb) + ) + stTrie, _ := trie.NewSecure(common.Hash{}, triedb) + stTrie.Update([]byte("key-1"), []byte("val-1")) // 0x1314700b81afc49f94db3623ef1df38f3ed18b73a1b7ea2f6c095118cf6118a0 + stTrie.Update([]byte("key-2"), []byte("val-2")) // 0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371 + stTrie.Update([]byte("key-3"), []byte("val-3")) // 0x51c71a47af0695957647fb68766d0becee77e953df17c29b3c2f25436f055c78 + stTrie.Commit(nil) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + + accTrie, _ := trie.NewSecure(common.Hash{}, triedb) + acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + + acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} + val, _ = rlp.EncodeToBytes(acc) + accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + + acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} + val, _ = rlp.EncodeToBytes(acc) + accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd + + // We can only corrupt the disk database, so flush the tries out + triedb.Reference( + common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), + common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"), + ) + triedb.Reference( + common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"), + common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"), + ) + triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil) + + // Delete a storage trie leaf and ensure the generator chokes + diskdb.Delete(common.HexToHash("0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371").Bytes()) + + snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), nil) + select { + case <-snap.genPending: + // Snapshot generation succeeded + t.Errorf("Snapshot generated against corrupt storage trie") + + case <-time.After(250 * time.Millisecond): + // Not generated fast enough, hopefully blocked inside on missing trie node fail + } + // Signal abortion to the generator and wait for it to tear down + stop := make(chan *generatorStats) + snap.genAbort <- stop + <-stop +} diff --git a/core/state/state_object.go b/core/state/state_object.go index 16e8d93cf5..8961c135b2 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -315,7 +315,7 @@ func (s *stateObject) updateTrie(db Database) Trie { if len(s.pendingStorage) == 0 { return s.trie } - // Track the amount of time wasted on updating the storge trie + // Track the amount of time wasted on updating the storage trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) } @@ -363,7 +363,7 @@ func (s *stateObject) updateRoot(db Database) { if s.updateTrie(db) == nil { return } - // Track the amount of time wasted on hashing the storge trie + // Track the amount of time wasted on hashing the storage trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now()) } @@ -380,7 +380,7 @@ func (s *stateObject) CommitTrie(db Database) error { if s.dbErr != nil { return s.dbErr } - // Track the amount of time wasted on committing the storge trie + // Track the amount of time wasted on committing the storage trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now()) } diff --git a/core/tx_list.go b/core/tx_list.go index bf304eedcf..cdd3df14c5 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -433,6 +433,7 @@ func (h *priceHeap) Pop() interface{} { old := *h n := len(old) x := old[n-1] + old[n-1] = nil *h = old[0 : n-1] return x } diff --git a/core/tx_pool.go b/core/tx_pool.go index 9c2a6a77bb..602d19718d 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -857,6 +857,7 @@ func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error { nilSlot++ } errs[nilSlot] = err + nilSlot++ } // Reorg the pool internals if needed and return done := pool.requestPromoteExecutables(dirtyAddrs) diff --git a/core/types/bloom9.go b/core/types/bloom9.go index 4970477d4d..abe02b4f8e 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -17,6 +17,7 @@ package types import ( + "encoding/binary" "fmt" "math/big" @@ -57,10 +58,16 @@ func (b *Bloom) SetBytes(d []byte) { } // Add adds d to the filter. Future calls of Test(d) will return true. -func (b *Bloom) Add(d *big.Int) { - bin := new(big.Int).SetBytes(b[:]) - bin.Or(bin, bloom9(d.Bytes())) - b.SetBytes(bin.Bytes()) +func (b *Bloom) Add(d []byte) { + b.add(d, make([]byte, 6)) +} + +// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes) +func (b *Bloom) add(d []byte, buf []byte) { + i1, v1, i2, v2, i3, v3 := bloomValues(d, buf) + b[i1] |= v1 + b[i2] |= v2 + b[i3] |= v3 } // Quorum @@ -73,21 +80,23 @@ func (b *Bloom) OrBloom(bl []byte) { } // Big converts b to a big integer. +// Note: Converting a bloom filter to a big.Int and then calling GetBytes +// does not return the same bytes, since big.Int will trim leading zeroes func (b Bloom) Big() *big.Int { return new(big.Int).SetBytes(b[:]) } +// Bytes returns the backing byte slice of the bloom func (b Bloom) Bytes() []byte { return b[:] } -func (b Bloom) Test(test *big.Int) bool { - return BloomLookup(b, test) -} - -func (b Bloom) TestBytes(test []byte) bool { - return b.Test(new(big.Int).SetBytes(test)) - +// Test checks if the given topic is present in the bloom filter +func (b Bloom) Test(topic []byte) bool { + i1, v1, i2, v2, i3, v3 := bloomValues(topic, make([]byte, 6)) + return v1 == v1&b[i1] && + v2 == v2&b[i2] && + v3 == v3&b[i3] } // MarshalText encodes b as a hex string with 0x prefix. @@ -100,46 +109,61 @@ func (b *Bloom) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("Bloom", input, b[:]) } +// CreateBloom creates a bloom filter out of the give Receipts (+Logs) func CreateBloom(receipts Receipts) Bloom { - bin := new(big.Int) + buf := make([]byte, 6) + var bin Bloom for _, receipt := range receipts { - bin.Or(bin, LogsBloom(receipt.Logs)) + for _, log := range receipt.Logs { + bin.add(log.Address.Bytes(), buf) + for _, b := range log.Topics { + bin.add(b[:], buf) + } + } } - - return BytesToBloom(bin.Bytes()) + return bin } -func LogsBloom(logs []*Log) *big.Int { - bin := new(big.Int) +// LogsBloom returns the bloom bytes for the given logs +func LogsBloom(logs []*Log) []byte { + buf := make([]byte, 6) + var bin Bloom for _, log := range logs { - bin.Or(bin, bloom9(log.Address.Bytes())) + bin.add(log.Address.Bytes(), buf) for _, b := range log.Topics { - bin.Or(bin, bloom9(b[:])) + bin.add(b[:], buf) } } - - return bin + return bin[:] } -func bloom9(b []byte) *big.Int { - b = crypto.Keccak256(b) - - r := new(big.Int) - - for i := 0; i < 6; i += 2 { - t := big.NewInt(1) - b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047 - r.Or(r, t.Lsh(t, b)) - } - - return r +// Bloom9 returns the bloom filter for the given data +func Bloom9(data []byte) []byte { + var b Bloom + b.SetBytes(data) + return b.Bytes() } -var Bloom9 = bloom9 +// bloomValues returns the bytes (index-value pairs) to set for the given data +func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) { + sha := hasherPool.Get().(crypto.KeccakState) + sha.Reset() + sha.Write(data) + sha.Read(hashbuf) + hasherPool.Put(sha) + // The actual bits to flip + v1 := byte(1 << (hashbuf[1] & 0x7)) + v2 := byte(1 << (hashbuf[3] & 0x7)) + v3 := byte(1 << (hashbuf[5] & 0x7)) + // The indices for the bytes to OR in + i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1 + i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1 + i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1 + + return i1, v1, i2, v2, i3, v3 +} +// BloomLookup is a convenience-method to check presence int he bloom filter func BloomLookup(bin Bloom, topic bytesBacked) bool { - bloom := bin.Big() - cmp := bloom9(topic.Bytes()) - - return bloom.And(bloom, cmp).Cmp(cmp) == 0 + return bin.Test(topic.Bytes()) } diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go index a28ac0e7af..893df486dd 100644 --- a/core/types/bloom9_test.go +++ b/core/types/bloom9_test.go @@ -17,8 +17,12 @@ package types import ( + "fmt" "math/big" "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" ) func TestBloom(t *testing.T) { @@ -35,47 +39,118 @@ func TestBloom(t *testing.T) { var bloom Bloom for _, data := range positive { - bloom.Add(new(big.Int).SetBytes([]byte(data))) + bloom.Add([]byte(data)) } for _, data := range positive { - if !bloom.TestBytes([]byte(data)) { + if !bloom.Test([]byte(data)) { t.Error("expected", data, "to test true") } } for _, data := range negative { - if bloom.TestBytes([]byte(data)) { + if bloom.Test([]byte(data)) { t.Error("did not expect", data, "to test true") } } } -/* -import ( - "testing" - - "github.com/ethereum/go-ethereum/core/state" -) +// TestBloomExtensively does some more thorough tests +func TestBloomExtensively(t *testing.T) { + var exp = common.HexToHash("c8d3ca65cdb4874300a9e39475508f23ed6da09fdbc487f89a2dcf50b09eb263") + var b Bloom + // Add 100 "random" things + for i := 0; i < 100; i++ { + data := fmt.Sprintf("xxxxxxxxxx data %d yyyyyyyyyyyyyy", i) + b.Add([]byte(data)) + //b.Add(new(big.Int).SetBytes([]byte(data))) + } + got := crypto.Keccak256Hash(b.Bytes()) + if got != exp { + t.Errorf("Got %x, exp %x", got, exp) + } + var b2 Bloom + b2.SetBytes(b.Bytes()) + got2 := crypto.Keccak256Hash(b2.Bytes()) + if got != got2 { + t.Errorf("Got %x, exp %x", got, got2) + } +} -func TestBloom9(t *testing.T) { - testCase := []byte("testtest") - bin := LogsBloom([]state.Log{ - {testCase, [][]byte{[]byte("hellohello")}, nil}, - }).Bytes() - res := BloomLookup(bin, testCase) +func BenchmarkBloom9(b *testing.B) { + test := []byte("testestestest") + for i := 0; i < b.N; i++ { + Bloom9(test) + } +} - if !res { - t.Errorf("Bloom lookup failed") +func BenchmarkBloom9Lookup(b *testing.B) { + toTest := []byte("testtest") + bloom := new(Bloom) + for i := 0; i < b.N; i++ { + bloom.Test(toTest) } } +func BenchmarkCreateBloom(b *testing.B) { -func TestAddress(t *testing.T) { - block := &Block{} - block.Coinbase = common.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") - fmt.Printf("%x\n", crypto.Keccak256(block.Coinbase)) + var txs = Transactions{ + NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil), + NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil), + } + var rSmall = Receipts{ + &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + {Address: common.BytesToAddress([]byte{0x11})}, + {Address: common.BytesToAddress([]byte{0x01, 0x11})}, + }, + TxHash: txs[0].Hash(), + ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), + GasUsed: 1, + }, + &Receipt{ + PostState: common.Hash{2}.Bytes(), + CumulativeGasUsed: 3, + Logs: []*Log{ + {Address: common.BytesToAddress([]byte{0x22})}, + {Address: common.BytesToAddress([]byte{0x02, 0x22})}, + }, + TxHash: txs[1].Hash(), + ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), + GasUsed: 2, + }, + } - bin := CreateBloom(block) - fmt.Printf("bin = %x\n", common.LeftPadBytes(bin, 64)) + var rLarge = make(Receipts, 200) + // Fill it with 200 receipts x 2 logs + for i := 0; i < 200; i += 2 { + copy(rLarge[i:], rSmall) + } + b.Run("small", func(b *testing.B) { + b.ReportAllocs() + var bl Bloom + for i := 0; i < b.N; i++ { + bl = CreateBloom(rSmall) + } + b.StopTimer() + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") + got := crypto.Keccak256Hash(bl.Bytes()) + if got != exp { + b.Errorf("Got %x, exp %x", got, exp) + } + }) + b.Run("large", func(b *testing.B) { + b.ReportAllocs() + var bl Bloom + for i := 0; i < b.N; i++ { + bl = CreateBloom(rLarge) + } + b.StopTimer() + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") + got := crypto.Keccak256Hash(bl.Bytes()) + if got != exp { + b.Errorf("Got %x, exp %x", got, exp) + } + }) } -*/ diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go index 7d40c7f660..51b8506bce 100644 --- a/core/types/derive_sha.go +++ b/core/types/derive_sha.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -// DerivableList is the interface which can derive the hash. type DerivableList interface { Len() int GetRlp(i int) []byte @@ -39,7 +38,22 @@ type Hasher interface { func DeriveSha(list DerivableList, hasher Hasher) common.Hash { hasher.Reset() keybuf := new(bytes.Buffer) - for i := 0; i < list.Len(); i++ { + + // StackTrie requires values to be inserted in increasing + // hash order, which is not the order that `list` provides + // hashes in. This insertion sequence ensures that the + // order is correct. + for i := 1; i < list.Len() && i <= 0x7f; i++ { + keybuf.Reset() + rlp.Encode(keybuf, uint(i)) + hasher.Update(keybuf.Bytes(), list.GetRlp(i)) + } + if list.Len() > 0 { + keybuf.Reset() + rlp.Encode(keybuf, uint(0)) + hasher.Update(keybuf.Bytes(), list.GetRlp(0)) + } + for i := 0x80; i < list.Len(); i++ { keybuf.Reset() rlp.Encode(keybuf, uint(i)) hasher.Update(keybuf.Bytes(), list.GetRlp(i)) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index b8cc5e5a14..751f911f56 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -164,18 +164,18 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi } // 0. If *gasleft* is less than or equal to 2300, fail the current call. -// 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted. +// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted. // 2. If current value does not equal new value: // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context): -// 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted. -// 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter. -// 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses: +// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted. +// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter. +// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses: // 2.2.1. If original value is not 0: -// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0. -// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter. +// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter. +// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter. // 2.2.2. If original value equals new value (this storage slot is reset): -// 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter. -// 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter. +// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter. +// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter. func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // If we fail the minimum gas availability invariant, fail (0) if contract.Gas <= params.SstoreSentryGasEIP2200 { @@ -189,33 +189,33 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m value := common.Hash(y.Bytes32()) if current == value { // noop (1) - return params.SstoreNoopGasEIP2200, nil + return params.SloadGasEIP2200, nil } original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) - return params.SstoreInitGasEIP2200, nil + return params.SstoreSetGasEIP2200, nil } if value == (common.Hash{}) { // delete slot (2.1.2b) - evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) } - return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2) + return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) } if original != (common.Hash{}) { if current == (common.Hash{}) { // recreate slot (2.2.1.1) - evm.StateDB.SubRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200) } else if value == (common.Hash{}) { // delete slot (2.2.1.2) - evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) } } if original == value { if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) - evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) } else { // reset to original existing slot (2.2.2.2) - evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) } } - return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2) + return params.SloadGasEIP2200, nil // dirty update (2.2) } func makeGasLog(n uint64) gasFunc { diff --git a/core/vm/logger.go b/core/vm/logger.go index e1d7c67ef1..3b166b5d26 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -322,20 +322,21 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) - if !t.cfg.DisableStack { // format stack + if !t.cfg.DisableStack { + // format stack var a []string for _, elem := range stack.data { a = append(a, fmt.Sprintf("%d", elem)) } b := fmt.Sprintf("[%v]", strings.Join(a, ",")) fmt.Fprintf(t.out, "%10v |", b) - } - if !t.cfg.DisableStack { // format return stack - var a []string + + // format return stack + a = a[:0] for _, elem := range rStack.data { a = append(a, fmt.Sprintf("%2d", elem)) } - b := fmt.Sprintf("[%v]", strings.Join(a, ",")) + b = fmt.Sprintf("[%v]", strings.Join(a, ",")) fmt.Fprintf(t.out, "%10v |", b) } fmt.Fprintln(t.out, "") diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 0fbf2d5b79..f06331c2d8 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -156,7 +156,7 @@ type Downloader struct { cancelWg sync.WaitGroup // Make sure all fetcher goroutines have exited. quitCh chan struct{} // Quit channel to signal termination - quitLock sync.RWMutex // Lock to prevent double closes + quitLock sync.Mutex // Lock to prevent double closes // Testing hooks syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run @@ -531,6 +531,8 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I d.ancientLimit = d.checkpoint } else if height > fullMaxForkAncestry+1 { d.ancientLimit = height - fullMaxForkAncestry - 1 + } else { + d.ancientLimit = 0 } frozen, _ := d.stateDB.Ancients() // Ignore the error here since light client can also hit here. @@ -622,9 +624,6 @@ func (d *Downloader) cancel() { func (d *Downloader) Cancel() { d.cancel() d.cancelWg.Wait() - - d.ancientLimit = 0 - log.Debug("Reset ancient limit to zero") } // Terminate interrupts the downloader, canceling all pending operations. diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index e38219d5cf..b4e68c3c3f 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -415,7 +415,6 @@ func (dl *downloadTester) dropPeer(id string) { type downloadTesterPeer struct { dl *downloadTester id string - lock sync.RWMutex chain *testChain missingStates map[common.Hash]bool // State entries that fast sync should not return } @@ -1605,7 +1604,7 @@ func TestRemoteHeaderRequestSpan(t *testing.T) { {15000, 13006, []int{14823, 14839, 14855, 14871, 14887, 14903, 14919, 14935, 14951, 14967, 14983, 14999}, }, - //Remote is pretty close to us. We don't have to fetch as many + // Remote is pretty close to us. We don't have to fetch as many {1200, 1150, []int{1149, 1154, 1159, 1164, 1169, 1174, 1179, 1184, 1189, 1194, 1199}, }, diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 745f7c7480..d2ec8ba694 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -712,6 +712,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh } } if accepted { + parentHash := headers[0].Hash() for i, header := range headers[1:] { hash := header.Hash() if want := request.From + 1 + uint64(i); header.Number.Uint64() != want { @@ -719,11 +720,13 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh accepted = false break } - if headers[i].Hash() != header.ParentHash { + if parentHash != header.ParentHash { log.Warn("Header broke chain ancestry", "peer", id, "number", header.Number, "hash", hash) accepted = false break } + // Set-up parent hash for next round + parentHash = hash } } // If the batch of headers wasn't accepted, mark as unavailable @@ -774,7 +777,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLi q.lock.Lock() defer q.lock.Unlock() validate := func(index int, header *types.Header) error { - if types.DeriveSha(types.Transactions(txLists[index]), new(trie.Trie)) != header.TxHash { + if types.DeriveSha(types.Transactions(txLists[index]), trie.NewStackTrie(nil)) != header.TxHash { return errInvalidBody } if types.CalcUncleHash(uncleLists[index]) != header.UncleHash { @@ -799,7 +802,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, q.lock.Lock() defer q.lock.Unlock() validate := func(index int, header *types.Header) error { - if types.DeriveSha(types.Receipts(receiptList[index]), new(trie.Trie)) != header.ReceiptHash { + if types.DeriveSha(types.Receipts(receiptList[index]), trie.NewStackTrie(nil)) != header.ReceiptHash { return errInvalidReceipt } return nil diff --git a/eth/handler.go b/eth/handler.go index 65acc9bb11..87b68a3413 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -782,7 +782,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { log.Warn("Propagated block has invalid uncles", "have", hash, "exp", request.Block.UncleHash()) break // TODO(karalabe): return error eventually, but wait a few releases } - if hash := types.DeriveSha(request.Block.Transactions(), new(trie.Trie)); hash != request.Block.TxHash() { + if hash := types.DeriveSha(request.Block.Transactions(), trie.NewStackTrie(nil)); hash != request.Block.TxHash() { log.Warn("Propagated block has invalid body", "have", hash, "exp", request.Block.TxHash()) break // TODO(karalabe): return error eventually, but wait a few releases } diff --git a/extension/extensionContracts/contract_extender.go b/extension/extensionContracts/contract_extender.go index 56e800b09c..234bcdaf0b 100644 --- a/extension/extensionContracts/contract_extender.go +++ b/extension/extensionContracts/contract_extender.go @@ -4,7 +4,6 @@ package extensionContracts import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -29,12 +27,12 @@ var ( ) // ContractExtenderABI is the input ABI used to generate the binding from. -const ContractExtenderABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"creator\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"contractToExtend\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"checkIfExtensionFinished\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalNumberOfVoters\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"walletAddressesToVote\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"isFinished\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"nextuuid\",\"type\":\"string\"}],\"name\":\"setUuid\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sharedDataHash\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"hash\",\"type\":\"string\"}],\"name\":\"setSharedStateHash\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"updatePartyMembers\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"voteOutcome\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"checkIfVoted\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"finish\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"votes\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"vote\",\"type\":\"bool\"},{\"name\":\"nextuuid\",\"type\":\"string\"}],\"name\":\"doVote\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"targetRecipientPTMKey\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"haveAllNodesVoted\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"contractAddress\",\"type\":\"address\"},{\"name\":\"recipientAddress\",\"type\":\"address\"},{\"name\":\"recipientPTMKey\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"toExtend\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"recipientPTMKey\",\"type\":\"string\"},{\"indexed\":false,\"name\":\"recipientAddress\",\"type\":\"address\"}],\"name\":\"NewContractExtensionContractCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"outcome\",\"type\":\"bool\"}],\"name\":\"AllNodesHaveAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CanPerformStateShare\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"ExtensionFinished\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"vote\",\"type\":\"bool\"},{\"indexed\":false,\"name\":\"voter\",\"type\":\"address\"}],\"name\":\"NewVote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"toExtend\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"tesserahash\",\"type\":\"string\"},{\"indexed\":false,\"name\":\"uuid\",\"type\":\"string\"}],\"name\":\"StateShared\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"toExtend\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"uuid\",\"type\":\"string\"}],\"name\":\"UpdateMembers\",\"type\":\"event\"}]" +const ContractExtenderABI = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"recipientPTMKey\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"outcome\",\"type\":\"bool\"}],\"name\":\"AllNodesHaveAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CanPerformStateShare\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"ExtensionFinished\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"toExtend\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"recipientPTMKey\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"}],\"name\":\"NewContractExtensionContractCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"vote\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"}],\"name\":\"NewVote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"toExtend\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"tesserahash\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"uuid\",\"type\":\"string\"}],\"name\":\"StateShared\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"toExtend\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"uuid\",\"type\":\"string\"}],\"name\":\"UpdateMembers\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"checkIfExtensionFinished\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"checkIfVoted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"contractToExtend\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"creator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bool\",\"name\":\"vote\",\"type\":\"bool\"},{\"internalType\":\"string\",\"name\":\"nextuuid\",\"type\":\"string\"}],\"name\":\"doVote\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"finish\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"haveAllNodesVoted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"isFinished\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"string\",\"name\":\"hash\",\"type\":\"string\"}],\"name\":\"setSharedStateHash\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"string\",\"name\":\"nextuuid\",\"type\":\"string\"}],\"name\":\"setUuid\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sharedDataHash\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"targetRecipientPTMKey\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalNumberOfVoters\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"updatePartyMembers\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"voteOutcome\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"votes\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"walletAddressesToVote\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]" var ContractExtenderParsedABI, _ = abi.JSON(strings.NewReader(ContractExtenderABI)) // ContractExtenderBin is the compiled bytecode used for deploying new contracts. -var ContractExtenderBin = "0x60806040523480156200001157600080fd5b506040516200135738038062001357833981018060405260608110156200003757600080fd5b81516020830151604084018051929491938201926401000000008111156200005e57600080fd5b820160208101848111156200007257600080fd5b81516401000000008111828201871017156200008d57600080fd5b5050600080546001600160a01b031916331790558051909350620000bb92506001915060208401906200028d565b50600280546001600160a01b038086166001600160a01b031992831617909255600380546001818101835560008381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b92830180548616331790558354918201909355018054938616939092169290921790556040805160208101918290528290526200014d91600a91906200028d565b506009805460ff19166001179055600060068190555b600354811015620001c1576001600560006003848154811015156200018457fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff191691151591909117905560010162000163565b50600354600455604080516001600160a01b0380861682528416918101919091526060602080830182815284519284019290925283517f04576ede6057794ada68966eebc285c98a2726cbc4929ffd1ad9900336728d9393879386938893608084019186019080838360005b83811015620002475781810151838201526020016200022d565b50505050905090810190601f168015620002755780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a150505062000332565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620002d057805160ff191683800117855562000300565b8280016001018555821562000300579182015b8281111562000300578251825591602001919060010190620002e3565b506200030e92915062000312565b5090565b6200032f91905b808211156200030e576000815560010162000319565b90565b61101580620003426000396000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063893971ba116100a2578063d56b288911610071578063d56b28891461037a578063d8bff5a514610382578063de5828cb146103a8578063e5af0f3014610457578063f57077d81461045f5761010b565b8063893971ba146102bc578063ac8b920514610362578063b5da45bb1461036a578063cb2805ec146103725761010b565b806379d41b8f116100de57806379d41b8f146101725780637b3529621461018f578063821e93da1461019757806388f520a01461023f5761010b565b806302d05d3f1461011057806315e56a6a146101345780631962cb9b1461013c5780633852772714610158575b600080fd5b610118610467565b604080516001600160a01b039092168252519081900360200190f35b610118610476565b610144610485565b604080519115158252519081900360200190f35b61016061048f565b60408051918252519081900360200190f35b6101186004803603602081101561018857600080fd5b5035610495565b6101446104bd565b61023d600480360360208110156101ad57600080fd5b8101906020810181356401000000008111156101c857600080fd5b8201836020820111156101da57600080fd5b803590602001918460018302840111640100000000831117156101fc57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506104c6945050505050565b005b610247610554565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610281578181015183820152602001610269565b50505050905090810190601f1680156102ae5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61023d600480360360208110156102d257600080fd5b8101906020810181356401000000008111156102ed57600080fd5b8201836020820111156102ff57600080fd5b8035906020019184600183028401116401000000008311171561032157600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506105e2945050505050565b61023d61094c565b610144610a47565b610144610a50565b61023d610a66565b6101446004803603602081101561039857600080fd5b50356001600160a01b0316610b01565b61023d600480360360408110156103be57600080fd5b8135151591908101906040810160208201356401000000008111156103e257600080fd5b8201836020820111156103f457600080fd5b8035906020019184600183028401116401000000008311171561041657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610b16945050505050565b610247610bbb565b610144610c15565b6000546001600160a01b031681565b6002546001600160a01b031681565b600c5460ff165b90565b60045481565b60038054829081106104a357fe5b6000918252602090912001546001600160a01b0316905081565b600c5460ff1681565b600c5460ff161561050b57604051600160e51b62461bcd028152600401808060200182810382526025815260200180610fc56025913960400191505060405180910390fd5b600b805460018101808355600092909252825161054f917f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db901906020850190610ee1565b505050565b600a805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156105da5780601f106105af576101008083540402835291602001916105da565b820191906000526020600020905b8154815290600101906020018083116105bd57829003601f168201915b505050505081565b6000546001600160a01b0316331461062e57604051600160e51b62461bcd028152600401808060200182810382526023815260200180610fa26023913960400191505060405180910390fd5b600c5460ff161561067357604051600160e51b62461bcd028152600401808060200182810382526025815260200180610fc56025913960400191505060405180910390fd5b600a8054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106ff5780601f106106d4576101008083540402835291602001916106ff565b820191906000526020600020905b8154815290600101906020018083116106e257829003601f168201915b505085519394508593151592506107639150505760408051600160e51b62461bcd02815260206004820152601860248201527f6e657720686173682063616e6e6f7420626520656d7074790000000000000000604482015290519081900360640190fd5b8151156107ba5760408051600160e51b62461bcd02815260206004820152601660248201527f7374617465206861736820616c72656164792073657400000000000000000000604482015290519081900360640190fd5b82516107cd90600a906020860190610ee1565b5060005b600b5481101561094357600254600b80547f67a92539f3cbd7c5a9b36c23c0e2beceb27d2e1b3cd8eda02c623689267ae71e926001600160a01b031691600a918590811061081b57fe5b6000918252602091829020604080516001600160a01b038716815260609481018581528654600260001961010060018416150201909116049582018690529290930193908301906080840190869080156108b65780601f1061088b576101008083540402835291602001916108b6565b820191906000526020600020905b81548152906001019060200180831161089957829003601f168201915b505083810382528454600260001961010060018416150201909116048082526020909101908590801561092a5780601f106108ff5761010080835404028352916020019161092a565b820191906000526020600020905b81548152906001019060200180831161090d57829003601f168201915b50509550505050505060405180910390a16001016107d1565b5061054f610a66565b60005b600b54811015610a4457600254600b80547f8adc4573f947f9930560525736f61b116be55049125cb63a36887a40f92f3b44926001600160a01b031691908490811061099757fe5b6000918252602091829020604080516001600160a01b0386168152938401818152919092018054600260001961010060018416150201909116049284018390529291606083019084908015610a2d5780601f10610a0257610100808354040283529160200191610a2d565b820191906000526020600020905b815481529060010190602001808311610a1057829003601f168201915b5050935050505060405180910390a160010161094f565b50565b60095460ff1681565b3360009081526007602052604090205460ff1690565b600c5460ff1615610aab57604051600160e51b62461bcd028152600401808060200182810382526025815260200180610fc56025913960400191505060405180910390fd5b6000546001600160a01b03163314610af757604051600160e51b62461bcd028152600401808060200182810382526023815260200180610fa26023913960400191505060405180910390fd5b610aff610c1f565b565b60086020526000908152604090205460ff1681565b600c5460ff1615610b5b57604051600160e51b62461bcd028152600401808060200182810382526025815260200180610fc56025913960400191505060405180910390fd5b610b6482610c57565b8115610b7357610b73816104c6565b610b7b610e28565b60408051831515815233602082015281517f225708d30006b0cc86d855ab91047edb5fe9c2e416412f36c18c6e90fe4e461f929181900390910190a15050565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156105da5780601f106105af576101008083540402835291602001916105da565b6006546003541490565b600c805460ff191660011790556040517f79c47b570b18a8a814b785800e5fcbf104e067663589cef1bba07756e3c6ede990600090a1565b600c5460ff1615610c9c57604051600160e51b62461bcd028152600401808060200182810382526028815260200180610f7a6028913960400191505060405180910390fd5b3360009081526005602052604090205460ff161515610d055760408051600160e51b62461bcd02815260206004820152601360248201527f6e6f7420616c6c6f77656420746f20766f746500000000000000000000000000604482015290519081900360640190fd5b3360009081526007602052604090205460ff1615610d6d5760408051600160e51b62461bcd02815260206004820152600d60248201527f616c726561647920766f74656400000000000000000000000000000000000000604482015290519081900360640190fd5b60095460ff161515610dc95760408051600160e51b62461bcd02815260206004820152601760248201527f766f74696e6720616c7265616479206465636c696e6564000000000000000000604482015290519081900360640190fd5b3360009081526007602090815260408083208054600160ff19918216811790925560089093529220805490911683151517905560068054909101905560095460ff168015610e145750805b6009805460ff191691151591909117905550565b60095460ff161515610e7557604080516000815290517ff20540914db019dd7c8d05ed165316a58d1583642772ac46f3d0c29b8644bd369181900360200190a1610e70610c1f565b610aff565b610e7d610c15565b15610aff57604080516001815290517ff20540914db019dd7c8d05ed165316a58d1583642772ac46f3d0c29b8644bd369181900360200190a16040517ffd46cafaa71d87561071b8095703a7f081265fad232945049f5cf2d2c39b3d2890600090a1565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610f2257805160ff1916838001178555610f4f565b82800160010185558215610f4f579182015b82811115610f4f578251825591602001919060010190610f34565b50610f5b929150610f5f565b5090565b61048c91905b80821115610f5b5760008155600101610f6556fe657874656e73696f6e2070726f6365737320636f6d706c657465642e2063616e6e6f7420766f74656f6e6c79206c6561646572206d617920706572666f726d207468697320616374696f6e657874656e73696f6e20686173206265656e206d61726b65642061732066696e6973686564a165627a7a723058201430571f2ddc1fc4db18fbe992d5cb9955a993fb01a34d163aebb1b5d891a6f00029" +var ContractExtenderBin = "0x60806040523480156200001157600080fd5b5060405162001cbb38038062001cbb833981810160405260608110156200003757600080fd5b810190808051906020019092919080519060200190929190805160405193929190846401000000008211156200006c57600080fd5b838201915060208201858111156200008357600080fd5b8251866001820283011164010000000082111715620000a157600080fd5b8083526020830192505050908051906020019080838360005b83811015620000d7578082015181840152602081019050620000ba565b50505050905090810190601f168015620001055780820380516001836020036101000a031916815260200191505b50604052505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190620001649291906200048c565b5082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060033390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505060038290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505060405180602001604052806000815250600a9080519060200190620002999291906200048c565b506001600960006101000a81548160ff021916908315150217905550600060068190555060008090505b6003805490508110156200036f5760016005600060038481548110620002e557fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508080600101915050620002c3565b506003805490506004819055507f04576ede6057794ada68966eebc285c98a2726cbc4929ffd1ad9900336728d93838284604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001806020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828103825284818151815260200191508051906020019080838360005b838110156200044657808201518184015260208101905062000429565b50505050905090810190601f168015620004745780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a15050506200053b565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620004cf57805160ff191683800117855562000500565b8280016001018555821562000500579182015b82811115620004ff578251825591602001919060010190620004e2565b5b5090506200050f919062000513565b5090565b6200053891905b80821115620005345760008160009055506001016200051a565b5090565b90565b611770806200054b6000396000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063893971ba116100a2578063d56b288911610071578063d56b2889146104bb578063d8bff5a5146104c5578063de5828cb14610521578063e5af0f30146105e8578063f57077d81461066b5761010b565b8063893971ba146103b2578063ac8b92051461046d578063b5da45bb14610477578063cb2805ec146104995761010b565b806379d41b8f116100de57806379d41b8f146101e45780637b35296214610252578063821e93da1461027457806388f520a01461032f5761010b565b806302d05d3f1461011057806315e56a6a1461015a5780631962cb9b146101a457806338527727146101c6575b600080fd5b61011861068d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101626106b2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101ac6106d8565b604051808215151515815260200191505060405180910390f35b6101ce6106ef565b6040518082815260200191505060405180910390f35b610210600480360360208110156101fa57600080fd5b81019080803590602001909291905050506106f5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61025a610731565b604051808215151515815260200191505060405180910390f35b61032d6004803603602081101561028a57600080fd5b81019080803590602001906401000000008111156102a757600080fd5b8201836020820111156102b957600080fd5b803590602001918460018302840111640100000000831117156102db57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610744565b005b6103376107ec565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561037757808201518184015260208101905061035c565b50505050905090810190601f1680156103a45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046b600480360360208110156103c857600080fd5b81019080803590602001906401000000008111156103e557600080fd5b8201836020820111156103f757600080fd5b8035906020019184600183028401116401000000008311171561041957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061088a565b005b610475610d1d565b005b61047f610e65565b604051808215151515815260200191505060405180910390f35b6104a1610e78565b604051808215151515815260200191505060405180910390f35b6104c3610ecc565b005b610507600480360360208110156104db57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610fe1565b604051808215151515815260200191505060405180910390f35b6105e66004803603604081101561053757600080fd5b810190808035151590602001909291908035906020019064010000000081111561056057600080fd5b82018360208201111561057257600080fd5b8035906020019184600183028401116401000000008311171561059457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611001565b005b6105f06110fb565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610630578082015181840152602081019050610615565b50505050905090810190601f16801561065d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610673611199565b604051808215151515815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600c60009054906101000a900460ff16905090565b60045481565b6003818154811061070257fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600c60009054906101000a900460ff1681565b600c60009054906101000a900460ff16156107aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806117176025913960400191505060405180910390fd5b600b8190806001815401808255809150509060018203906000526020600020016000909192909190915090805190602001906107e7929190611626565b505050565b600a8054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108825780601f1061085757610100808354040283529160200191610882565b820191906000526020600020905b81548152906001019060200180831161086557829003601f168201915b505050505081565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461092f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806116f46023913960400191505060405180910390fd5b600c60009054906101000a900460ff1615610995576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806117176025913960400191505060405180910390fd5b6060600a8054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a2d5780601f10610a0257610100808354040283529160200191610a2d565b820191906000526020600020905b815481529060010190602001808311610a1057829003601f168201915b505050505090506060829050600081511415610ab1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6e657720686173682063616e6e6f7420626520656d707479000000000000000081525060200191505060405180910390fd5b6000825114610b28576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f7374617465206861736820616c7265616479207365740000000000000000000081525060200191505060405180910390fd5b82600a9080519060200190610b3e929190611626565b5060008090505b600b80549050811015610d0f577f67a92539f3cbd7c5a9b36c23c0e2beceb27d2e1b3cd8eda02c623689267ae71e600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600a600b8481548110610ba557fe5b90600052602060002001604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200180602001838103835285818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610c6e5780601f10610c4357610100808354040283529160200191610c6e565b820191906000526020600020905b815481529060010190602001808311610c5157829003601f168201915b5050838103825284818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610cf15780601f10610cc657610100808354040283529160200191610cf1565b820191906000526020600020905b815481529060010190602001808311610cd457829003601f168201915b50509550505050505060405180910390a18080600101915050610b45565b50610d18610ecc565b505050565b60008090505b600b80549050811015610e62577f8adc4573f947f9930560525736f61b116be55049125cb63a36887a40f92f3b44600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600b8381548110610d8157fe5b90600052602060002001604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610e465780601f10610e1b57610100808354040283529160200191610e46565b820191906000526020600020905b815481529060010190602001808311610e2957829003601f168201915b5050935050505060405180910390a18080600101915050610d23565b50565b600960009054906101000a900460ff1681565b6000600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905090565b600c60009054906101000a900460ff1615610f32576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806117176025913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610fd7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806116f46023913960400191505060405180910390fd5b610fdf6111aa565b565b60086020528060005260406000206000915054906101000a900460ff1681565b600c60009054906101000a900460ff1615611067576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806117176025913960400191505060405180910390fd5b611070826111f3565b81156110805761107f81610744565b5b611088611550565b7f225708d30006b0cc86d855ab91047edb5fe9c2e416412f36c18c6e90fe4e461f823360405180831515151581526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a15050565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156111915780601f1061116657610100808354040283529160200191611191565b820191906000526020600020905b81548152906001019060200180831161117457829003601f168201915b505050505081565b600060065460038054905014905090565b6001600c60006101000a81548160ff0219169083151502179055507f79c47b570b18a8a814b785800e5fcbf104e067663589cef1bba07756e3c6ede960405160405180910390a1565b600c60009054906101000a900460ff1615611259576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001806116cc6028913960400191505060405180910390fd5b600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16611318576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6e6f7420616c6c6f77656420746f20766f74650000000000000000000000000081525060200191505060405180910390fd5b600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156113d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f616c726561647920766f7465640000000000000000000000000000000000000081525060200191505060405180910390fd5b600960009054906101000a900460ff1661145a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f766f74696e6720616c7265616479206465636c696e656400000000000000000081525060200191505060405180910390fd5b6001600760003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555080600860003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600660008154809291906001019190505550600960009054906101000a900460ff1680156115345750805b600960006101000a81548160ff02191690831515021790555050565b600960009054906101000a900460ff166115ad577ff20540914db019dd7c8d05ed165316a58d1583642772ac46f3d0c29b8644bd366000604051808215151515815260200191505060405180910390a16115a86111aa565b611624565b6115b5611199565b15611623577ff20540914db019dd7c8d05ed165316a58d1583642772ac46f3d0c29b8644bd366001604051808215151515815260200191505060405180910390a17ffd46cafaa71d87561071b8095703a7f081265fad232945049f5cf2d2c39b3d2860405160405180910390a15b5b565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061166757805160ff1916838001178555611695565b82800160010185558215611695579182015b82811115611694578251825591602001919060010190611679565b5b5090506116a291906116a6565b5090565b6116c891905b808211156116c45760008160009055506001016116ac565b5090565b9056fe657874656e73696f6e2070726f6365737320636f6d706c657465642e2063616e6e6f7420766f74656f6e6c79206c6561646572206d617920706572666f726d207468697320616374696f6e657874656e73696f6e20686173206265656e206d61726b65642061732066696e6973686564a265627a7a72315820625108b92f7ff30d44757ae1bb19335828b2892b67a277794ea401fa969f7bdf64736f6c63430005110032" // DeployContractExtender deploys a new Ethereum contract, binding an instance of ContractExtender to it. func DeployContractExtender(auth *bind.TransactOpts, backend bind.ContractBackend, contractAddress common.Address, recipientAddress common.Address, recipientPTMKey string) (common.Address, *types.Transaction, *ContractExtender, error) { @@ -158,7 +156,7 @@ func bindContractExtender(address common.Address, caller bind.ContractCaller, tr // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_ContractExtender *ContractExtenderRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_ContractExtender *ContractExtenderRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _ContractExtender.Contract.ContractExtenderCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_ContractExtender *ContractExtenderRaw) Transact(opts *bind.TransactOpts, // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_ContractExtender *ContractExtenderCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_ContractExtender *ContractExtenderCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _ContractExtender.Contract.contract.Call(opts, result, method, params...) } @@ -194,312 +192,372 @@ func (_ContractExtender *ContractExtenderTransactorRaw) Transact(opts *bind.Tran // CheckIfExtensionFinished is a free data retrieval call binding the contract method 0x1962cb9b. // -// Solidity: function checkIfExtensionFinished() constant returns(bool) +// Solidity: function checkIfExtensionFinished() view returns(bool) func (_ContractExtender *ContractExtenderCaller) CheckIfExtensionFinished(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "checkIfExtensionFinished") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "checkIfExtensionFinished") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckIfExtensionFinished is a free data retrieval call binding the contract method 0x1962cb9b. // -// Solidity: function checkIfExtensionFinished() constant returns(bool) +// Solidity: function checkIfExtensionFinished() view returns(bool) func (_ContractExtender *ContractExtenderSession) CheckIfExtensionFinished() (bool, error) { return _ContractExtender.Contract.CheckIfExtensionFinished(&_ContractExtender.CallOpts) } // CheckIfExtensionFinished is a free data retrieval call binding the contract method 0x1962cb9b. // -// Solidity: function checkIfExtensionFinished() constant returns(bool) +// Solidity: function checkIfExtensionFinished() view returns(bool) func (_ContractExtender *ContractExtenderCallerSession) CheckIfExtensionFinished() (bool, error) { return _ContractExtender.Contract.CheckIfExtensionFinished(&_ContractExtender.CallOpts) } // CheckIfVoted is a free data retrieval call binding the contract method 0xcb2805ec. // -// Solidity: function checkIfVoted() constant returns(bool) +// Solidity: function checkIfVoted() view returns(bool) func (_ContractExtender *ContractExtenderCaller) CheckIfVoted(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "checkIfVoted") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "checkIfVoted") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckIfVoted is a free data retrieval call binding the contract method 0xcb2805ec. // -// Solidity: function checkIfVoted() constant returns(bool) +// Solidity: function checkIfVoted() view returns(bool) func (_ContractExtender *ContractExtenderSession) CheckIfVoted() (bool, error) { return _ContractExtender.Contract.CheckIfVoted(&_ContractExtender.CallOpts) } // CheckIfVoted is a free data retrieval call binding the contract method 0xcb2805ec. // -// Solidity: function checkIfVoted() constant returns(bool) +// Solidity: function checkIfVoted() view returns(bool) func (_ContractExtender *ContractExtenderCallerSession) CheckIfVoted() (bool, error) { return _ContractExtender.Contract.CheckIfVoted(&_ContractExtender.CallOpts) } // ContractToExtend is a free data retrieval call binding the contract method 0x15e56a6a. // -// Solidity: function contractToExtend() constant returns(address) +// Solidity: function contractToExtend() view returns(address) func (_ContractExtender *ContractExtenderCaller) ContractToExtend(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "contractToExtend") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "contractToExtend") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // ContractToExtend is a free data retrieval call binding the contract method 0x15e56a6a. // -// Solidity: function contractToExtend() constant returns(address) +// Solidity: function contractToExtend() view returns(address) func (_ContractExtender *ContractExtenderSession) ContractToExtend() (common.Address, error) { return _ContractExtender.Contract.ContractToExtend(&_ContractExtender.CallOpts) } // ContractToExtend is a free data retrieval call binding the contract method 0x15e56a6a. // -// Solidity: function contractToExtend() constant returns(address) +// Solidity: function contractToExtend() view returns(address) func (_ContractExtender *ContractExtenderCallerSession) ContractToExtend() (common.Address, error) { return _ContractExtender.Contract.ContractToExtend(&_ContractExtender.CallOpts) } // Creator is a free data retrieval call binding the contract method 0x02d05d3f. // -// Solidity: function creator() constant returns(address) +// Solidity: function creator() view returns(address) func (_ContractExtender *ContractExtenderCaller) Creator(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "creator") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "creator") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // Creator is a free data retrieval call binding the contract method 0x02d05d3f. // -// Solidity: function creator() constant returns(address) +// Solidity: function creator() view returns(address) func (_ContractExtender *ContractExtenderSession) Creator() (common.Address, error) { return _ContractExtender.Contract.Creator(&_ContractExtender.CallOpts) } // Creator is a free data retrieval call binding the contract method 0x02d05d3f. // -// Solidity: function creator() constant returns(address) +// Solidity: function creator() view returns(address) func (_ContractExtender *ContractExtenderCallerSession) Creator() (common.Address, error) { return _ContractExtender.Contract.Creator(&_ContractExtender.CallOpts) } // HaveAllNodesVoted is a free data retrieval call binding the contract method 0xf57077d8. // -// Solidity: function haveAllNodesVoted() constant returns(bool) +// Solidity: function haveAllNodesVoted() view returns(bool) func (_ContractExtender *ContractExtenderCaller) HaveAllNodesVoted(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "haveAllNodesVoted") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "haveAllNodesVoted") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // HaveAllNodesVoted is a free data retrieval call binding the contract method 0xf57077d8. // -// Solidity: function haveAllNodesVoted() constant returns(bool) +// Solidity: function haveAllNodesVoted() view returns(bool) func (_ContractExtender *ContractExtenderSession) HaveAllNodesVoted() (bool, error) { return _ContractExtender.Contract.HaveAllNodesVoted(&_ContractExtender.CallOpts) } // HaveAllNodesVoted is a free data retrieval call binding the contract method 0xf57077d8. // -// Solidity: function haveAllNodesVoted() constant returns(bool) +// Solidity: function haveAllNodesVoted() view returns(bool) func (_ContractExtender *ContractExtenderCallerSession) HaveAllNodesVoted() (bool, error) { return _ContractExtender.Contract.HaveAllNodesVoted(&_ContractExtender.CallOpts) } // IsFinished is a free data retrieval call binding the contract method 0x7b352962. // -// Solidity: function isFinished() constant returns(bool) +// Solidity: function isFinished() view returns(bool) func (_ContractExtender *ContractExtenderCaller) IsFinished(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "isFinished") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "isFinished") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsFinished is a free data retrieval call binding the contract method 0x7b352962. // -// Solidity: function isFinished() constant returns(bool) +// Solidity: function isFinished() view returns(bool) func (_ContractExtender *ContractExtenderSession) IsFinished() (bool, error) { return _ContractExtender.Contract.IsFinished(&_ContractExtender.CallOpts) } // IsFinished is a free data retrieval call binding the contract method 0x7b352962. // -// Solidity: function isFinished() constant returns(bool) +// Solidity: function isFinished() view returns(bool) func (_ContractExtender *ContractExtenderCallerSession) IsFinished() (bool, error) { return _ContractExtender.Contract.IsFinished(&_ContractExtender.CallOpts) } // SharedDataHash is a free data retrieval call binding the contract method 0x88f520a0. // -// Solidity: function sharedDataHash() constant returns(string) +// Solidity: function sharedDataHash() view returns(string) func (_ContractExtender *ContractExtenderCaller) SharedDataHash(opts *bind.CallOpts) (string, error) { - var ( - ret0 = new(string) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "sharedDataHash") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "sharedDataHash") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + } // SharedDataHash is a free data retrieval call binding the contract method 0x88f520a0. // -// Solidity: function sharedDataHash() constant returns(string) +// Solidity: function sharedDataHash() view returns(string) func (_ContractExtender *ContractExtenderSession) SharedDataHash() (string, error) { return _ContractExtender.Contract.SharedDataHash(&_ContractExtender.CallOpts) } // SharedDataHash is a free data retrieval call binding the contract method 0x88f520a0. // -// Solidity: function sharedDataHash() constant returns(string) +// Solidity: function sharedDataHash() view returns(string) func (_ContractExtender *ContractExtenderCallerSession) SharedDataHash() (string, error) { return _ContractExtender.Contract.SharedDataHash(&_ContractExtender.CallOpts) } // TargetRecipientPTMKey is a free data retrieval call binding the contract method 0xe5af0f30. // -// Solidity: function targetRecipientPTMKey() constant returns(string) +// Solidity: function targetRecipientPTMKey() view returns(string) func (_ContractExtender *ContractExtenderCaller) TargetRecipientPTMKey(opts *bind.CallOpts) (string, error) { - var ( - ret0 = new(string) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "targetRecipientPTMKey") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "targetRecipientPTMKey") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + } // TargetRecipientPTMKey is a free data retrieval call binding the contract method 0xe5af0f30. // -// Solidity: function targetRecipientPTMKey() constant returns(string) +// Solidity: function targetRecipientPTMKey() view returns(string) func (_ContractExtender *ContractExtenderSession) TargetRecipientPTMKey() (string, error) { return _ContractExtender.Contract.TargetRecipientPTMKey(&_ContractExtender.CallOpts) } // TargetRecipientPTMKey is a free data retrieval call binding the contract method 0xe5af0f30. // -// Solidity: function targetRecipientPTMKey() constant returns(string) +// Solidity: function targetRecipientPTMKey() view returns(string) func (_ContractExtender *ContractExtenderCallerSession) TargetRecipientPTMKey() (string, error) { return _ContractExtender.Contract.TargetRecipientPTMKey(&_ContractExtender.CallOpts) } // TotalNumberOfVoters is a free data retrieval call binding the contract method 0x38527727. // -// Solidity: function totalNumberOfVoters() constant returns(uint256) +// Solidity: function totalNumberOfVoters() view returns(uint256) func (_ContractExtender *ContractExtenderCaller) TotalNumberOfVoters(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "totalNumberOfVoters") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "totalNumberOfVoters") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // TotalNumberOfVoters is a free data retrieval call binding the contract method 0x38527727. // -// Solidity: function totalNumberOfVoters() constant returns(uint256) +// Solidity: function totalNumberOfVoters() view returns(uint256) func (_ContractExtender *ContractExtenderSession) TotalNumberOfVoters() (*big.Int, error) { return _ContractExtender.Contract.TotalNumberOfVoters(&_ContractExtender.CallOpts) } // TotalNumberOfVoters is a free data retrieval call binding the contract method 0x38527727. // -// Solidity: function totalNumberOfVoters() constant returns(uint256) +// Solidity: function totalNumberOfVoters() view returns(uint256) func (_ContractExtender *ContractExtenderCallerSession) TotalNumberOfVoters() (*big.Int, error) { return _ContractExtender.Contract.TotalNumberOfVoters(&_ContractExtender.CallOpts) } // VoteOutcome is a free data retrieval call binding the contract method 0xb5da45bb. // -// Solidity: function voteOutcome() constant returns(bool) +// Solidity: function voteOutcome() view returns(bool) func (_ContractExtender *ContractExtenderCaller) VoteOutcome(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "voteOutcome") - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "voteOutcome") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // VoteOutcome is a free data retrieval call binding the contract method 0xb5da45bb. // -// Solidity: function voteOutcome() constant returns(bool) +// Solidity: function voteOutcome() view returns(bool) func (_ContractExtender *ContractExtenderSession) VoteOutcome() (bool, error) { return _ContractExtender.Contract.VoteOutcome(&_ContractExtender.CallOpts) } // VoteOutcome is a free data retrieval call binding the contract method 0xb5da45bb. // -// Solidity: function voteOutcome() constant returns(bool) +// Solidity: function voteOutcome() view returns(bool) func (_ContractExtender *ContractExtenderCallerSession) VoteOutcome() (bool, error) { return _ContractExtender.Contract.VoteOutcome(&_ContractExtender.CallOpts) } // Votes is a free data retrieval call binding the contract method 0xd8bff5a5. // -// Solidity: function votes(address ) constant returns(bool) +// Solidity: function votes(address ) view returns(bool) func (_ContractExtender *ContractExtenderCaller) Votes(opts *bind.CallOpts, arg0 common.Address) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "votes", arg0) - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "votes", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // Votes is a free data retrieval call binding the contract method 0xd8bff5a5. // -// Solidity: function votes(address ) constant returns(bool) +// Solidity: function votes(address ) view returns(bool) func (_ContractExtender *ContractExtenderSession) Votes(arg0 common.Address) (bool, error) { return _ContractExtender.Contract.Votes(&_ContractExtender.CallOpts, arg0) } // Votes is a free data retrieval call binding the contract method 0xd8bff5a5. // -// Solidity: function votes(address ) constant returns(bool) +// Solidity: function votes(address ) view returns(bool) func (_ContractExtender *ContractExtenderCallerSession) Votes(arg0 common.Address) (bool, error) { return _ContractExtender.Contract.Votes(&_ContractExtender.CallOpts, arg0) } // WalletAddressesToVote is a free data retrieval call binding the contract method 0x79d41b8f. // -// Solidity: function walletAddressesToVote(uint256 ) constant returns(address) +// Solidity: function walletAddressesToVote(uint256 ) view returns(address) func (_ContractExtender *ContractExtenderCaller) WalletAddressesToVote(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _ContractExtender.contract.Call(opts, out, "walletAddressesToVote", arg0) - return *ret0, err + var out []interface{} + err := _ContractExtender.contract.Call(opts, &out, "walletAddressesToVote", arg0) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // WalletAddressesToVote is a free data retrieval call binding the contract method 0x79d41b8f. // -// Solidity: function walletAddressesToVote(uint256 ) constant returns(address) +// Solidity: function walletAddressesToVote(uint256 ) view returns(address) func (_ContractExtender *ContractExtenderSession) WalletAddressesToVote(arg0 *big.Int) (common.Address, error) { return _ContractExtender.Contract.WalletAddressesToVote(&_ContractExtender.CallOpts, arg0) } // WalletAddressesToVote is a free data retrieval call binding the contract method 0x79d41b8f. // -// Solidity: function walletAddressesToVote(uint256 ) constant returns(address) +// Solidity: function walletAddressesToVote(uint256 ) view returns(address) func (_ContractExtender *ContractExtenderCallerSession) WalletAddressesToVote(arg0 *big.Int) (common.Address, error) { return _ContractExtender.Contract.WalletAddressesToVote(&_ContractExtender.CallOpts, arg0) } diff --git a/extension/extensionContracts/extensionHandler.go b/extension/extensionContracts/extensionHandler.go index 39f43aaf8b..e80945b87e 100644 --- a/extension/extensionContracts/extensionHandler.go +++ b/extension/extensionContracts/extensionHandler.go @@ -4,7 +4,7 @@ import "github.com/ethereum/go-ethereum/common" func UnpackStateSharedLog(logData []byte) (common.Address, string, string, error) { decodedLog := new(ContractExtenderStateShared) - if err := ContractExtenderParsedABI.Unpack(decodedLog, "StateShared", logData); err != nil { + if err := ContractExtenderParsedABI.UnpackIntoInterface(decodedLog, "StateShared", logData); err != nil { return common.Address{}, "", "", err } return decodedLog.ToExtend, decodedLog.Tesserahash, decodedLog.Uuid, nil @@ -12,7 +12,7 @@ func UnpackStateSharedLog(logData []byte) (common.Address, string, string, error func UnpackNewExtensionCreatedLog(data []byte) (*ContractExtenderNewContractExtensionContractCreated, error) { newExtensionEvent := new(ContractExtenderNewContractExtensionContractCreated) - err := ContractExtenderParsedABI.Unpack(newExtensionEvent, "NewContractExtensionContractCreated", data) + err := ContractExtenderParsedABI.UnpackIntoInterface(newExtensionEvent, "NewContractExtensionContractCreated", data) return newExtensionEvent, err } diff --git a/extension/extensionContracts/gen.go b/extension/extensionContracts/gen.go new file mode 100644 index 0000000000..23044a0e53 --- /dev/null +++ b/extension/extensionContracts/gen.go @@ -0,0 +1,5 @@ +package extensionContracts + +//go:generate solc --abi --bin -o . contract_extender.sol +//go:generate abigen -pkg extensionContract -abi ./ContractExtender.abi -bin ./ContractExtender.bin -type ContractExtender -out ./contract_extender.go +//go:generate rm ContractExtender.abi ContractExtender.bin diff --git a/go.mod b/go.mod index 0ee17436c0..17eb613f9d 100755 --- a/go.mod +++ b/go.mod @@ -95,5 +95,4 @@ require ( gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 gopkg.in/oleiade/lane.v1 v1.0.0 gopkg.in/urfave/cli.v1 v1.20.0 - gotest.tools v2.2.0+incompatible // indirect ) diff --git a/internal/debug/api.go b/internal/debug/api.go index 86a4218f6a..efd8626776 100644 --- a/internal/debug/api.go +++ b/internal/debug/api.go @@ -196,7 +196,7 @@ func (*HandlerT) Stacks() string { return buf.String() } -// FreeOSMemory returns unused memory to the OS. +// FreeOSMemory forces a garbage collection. func (*HandlerT) FreeOSMemory() { debug.FreeOSMemory() } diff --git a/internal/utesting/utesting.go b/internal/utesting/utesting.go index 23c748cae9..4de0ecf99a 100644 --- a/internal/utesting/utesting.go +++ b/internal/utesting/utesting.go @@ -65,10 +65,17 @@ func MatchTests(tests []Test, expr string) []Test { func RunTests(tests []Test, report io.Writer) []Result { results := make([]Result, len(tests)) for i, test := range tests { + var output io.Writer + buffer := new(bytes.Buffer) + output = buffer + if report != nil { + output = io.MultiWriter(buffer, report) + } start := time.Now() results[i].Name = test.Name - results[i].Failed, results[i].Output = Run(test) + results[i].Failed = run(test, output) results[i].Duration = time.Since(start) + results[i].Output = buffer.String() if report != nil { printResult(results[i], report) } @@ -80,7 +87,6 @@ func printResult(r Result, w io.Writer) { pd := r.Duration.Truncate(100 * time.Microsecond) if r.Failed { fmt.Fprintf(w, "-- FAIL %s (%v)\n", r.Name, pd) - fmt.Fprintln(w, r.Output) } else { fmt.Fprintf(w, "-- OK %s (%v)\n", r.Name, pd) } @@ -99,7 +105,13 @@ func CountFailures(rr []Result) int { // Run executes a single test. func Run(test Test) (bool, string) { - t := new(T) + output := new(bytes.Buffer) + failed := run(test, output) + return failed, output.String() +} + +func run(test Test, output io.Writer) bool { + t := &T{output: output} done := make(chan struct{}) go func() { defer close(done) @@ -114,7 +126,7 @@ func Run(test Test) (bool, string) { test.Fn(t) }() <-done - return t.failed, t.output.String() + return t.failed } // T is the value given to the test function. The test can signal failures @@ -122,7 +134,7 @@ func Run(test Test) (bool, string) { type T struct { mu sync.Mutex failed bool - output bytes.Buffer + output io.Writer } // FailNow marks the test as having failed and stops its execution by calling @@ -151,7 +163,7 @@ func (t *T) Failed() bool { func (t *T) Log(vs ...interface{}) { t.mu.Lock() defer t.mu.Unlock() - fmt.Fprintln(&t.output, vs...) + fmt.Fprintln(t.output, vs...) } // Logf formats its arguments according to the format, analogous to Printf, and records @@ -162,7 +174,7 @@ func (t *T) Logf(format string, vs ...interface{}) { if len(format) == 0 || format[len(format)-1] != '\n' { format += "\n" } - fmt.Fprintf(&t.output, format, vs...) + fmt.Fprintf(t.output, format, vs...) } // Error is equivalent to Log followed by Fail. diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 0f6592fc72..09df41dde8 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -248,7 +248,8 @@ web3._extend({ new web3._extend.Method({ name: 'printBlock', call: 'debug_printBlock', - params: 1 + params: 1, + outputFormatter: console.log }), new web3._extend.Method({ name: 'getBlockRlp', @@ -259,7 +260,7 @@ web3._extend({ name: 'testSignCliqueBlock', call: 'debug_testSignCliqueBlock', params: 2, - inputFormatters: [web3._extend.formatters.inputAddressFormatter, null], + inputFormatter: [web3._extend.formatters.inputAddressFormatter, null], }), new web3._extend.Method({ name: 'setHead', @@ -274,8 +275,8 @@ web3._extend({ new web3._extend.Method({ name: 'dumpBlock', call: 'debug_dumpBlock', - params: 2, - inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, ""] + params: 1, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ name: 'privateStateRoot', @@ -439,7 +440,7 @@ web3._extend({ name: 'traceBlockByNumber', call: 'debug_traceBlockByNumber', params: 2, - inputFormatter: [null, null] + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, null] }), new web3._extend.Method({ name: 'traceBlockByHash', @@ -557,7 +558,8 @@ web3._extend({ new web3._extend.Method({ name: 'getHeaderByNumber', call: 'eth_getHeaderByNumber', - params: 1 + params: 1, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ name: 'getHeaderByHash', @@ -567,12 +569,14 @@ web3._extend({ new web3._extend.Method({ name: 'getBlockByNumber', call: 'eth_getBlockByNumber', - params: 2 + params: 2, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, function (val) { return !!val; }] }), new web3._extend.Method({ name: 'getBlockByHash', call: 'eth_getBlockByHash', - params: 2 + params: 2, + inputFormatter: [null, function (val) { return !!val; }] }), new web3._extend.Method({ name: 'getRawTransaction', diff --git a/les/txrelay.go b/les/txrelay.go index 4f6c15025e..57f2412eba 100644 --- a/les/txrelay.go +++ b/les/txrelay.go @@ -35,7 +35,7 @@ type lesTxRelay struct { txPending map[common.Hash]struct{} peerList []*serverPeer peerStartPos int - lock sync.RWMutex + lock sync.Mutex stop chan struct{} retriever *retrieveManager diff --git a/light/odr.go b/light/odr.go index 0b854b0b6c..7016ef8ef2 100644 --- a/light/odr.go +++ b/light/odr.go @@ -133,7 +133,7 @@ func (req *ReceiptsRequest) StoreResult(db ethdb.Database) { } } -// ChtRequest is the ODR request type for state/storage trie entries +// ChtRequest is the ODR request type for retrieving header by Canonical Hash Trie type ChtRequest struct { Untrusted bool // Indicator whether the result retrieved is trusted or not PeerId string // The specified peer id from which to retrieve data. diff --git a/miner/miner.go b/miner/miner.go index 6c46f08d51..a4840e60e8 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -88,15 +88,22 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even // and halt your mining operation for as long as the DOS continues. func (miner *Miner) update() { events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) - defer events.Unsubscribe() + defer func() { + if !events.Closed() { + events.Unsubscribe() + } + }() shouldStart := false canStart := true + dlEventCh := events.Chan() for { select { - case ev := <-events.Chan(): + case ev := <-dlEventCh: if ev == nil { - return + // Unsubscription done, stop listening + dlEventCh = nil + continue } switch ev.Data.(type) { case downloader.StartEvent: @@ -108,16 +115,24 @@ func (miner *Miner) update() { shouldStart = true log.Info("Mining aborted due to sync") } - case downloader.DoneEvent, downloader.FailedEvent: + case downloader.FailedEvent: + canStart = true + if shouldStart { + miner.SetEtherbase(miner.coinbase) + miner.worker.start() + } + case downloader.DoneEvent: canStart = true if shouldStart { miner.SetEtherbase(miner.coinbase) miner.worker.start() } + // Stop reacting to downloader events + events.Unsubscribe() } case addr := <-miner.startCh: + miner.SetEtherbase(addr) if canStart { - miner.SetEtherbase(addr) miner.worker.start() } shouldStart = true diff --git a/miner/miner_test.go b/miner/miner_test.go index cccc9a1201..690dbaca6b 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -99,12 +99,75 @@ func TestMiner(t *testing.T) { // Stop the downloader and wait for the update loop to run mux.Post(downloader.DoneEvent{}) waitForMiningState(t, miner, true) - // Start the downloader and wait for the update loop to run + + // Subsequent downloader events after a successful DoneEvent should not cause the + // miner to start or stop. This prevents a security vulnerability + // that would allow entities to present fake high blocks that would + // stop mining operations by causing a downloader sync + // until it was discovered they were invalid, whereon mining would resume. + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, true) + + mux.Post(downloader.FailedEvent{}) + waitForMiningState(t, miner, true) +} + +// TestMinerDownloaderFirstFails tests that mining is only +// permitted to run indefinitely once the downloader sees a DoneEvent (success). +// An initial FailedEvent should allow mining to stop on a subsequent +// downloader StartEvent. +func TestMinerDownloaderFirstFails(t *testing.T) { + miner, mux := createMiner(t) + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + // Start the downloader mux.Post(downloader.StartEvent{}) waitForMiningState(t, miner, false) + // Stop the downloader and wait for the update loop to run mux.Post(downloader.FailedEvent{}) waitForMiningState(t, miner, true) + + // Since the downloader hasn't yet emitted a successful DoneEvent, + // we expect the miner to stop on next StartEvent. + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + + // Downloader finally succeeds. + mux.Post(downloader.DoneEvent{}) + waitForMiningState(t, miner, true) + + // Downloader starts again. + // Since it has achieved a DoneEvent once, we expect miner + // state to be unchanged. + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, true) + + mux.Post(downloader.FailedEvent{}) + waitForMiningState(t, miner, true) +} + +func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { + miner, mux := createMiner(t) + + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + // Start the downloader + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + + // Downloader finally succeeds. + mux.Post(downloader.DoneEvent{}) + waitForMiningState(t, miner, true) + + miner.Stop() + waitForMiningState(t, miner, false) + + miner.Start(common.HexToAddress("0x678910")) + waitForMiningState(t, miner, true) + + miner.Stop() + waitForMiningState(t, miner, false) } func TestStartWhileDownload(t *testing.T) { @@ -139,6 +202,28 @@ func TestCloseMiner(t *testing.T) { waitForMiningState(t, miner, false) } +// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't +// possible at the moment +func TestMinerSetEtherbase(t *testing.T) { + miner, mux := createMiner(t) + // Start with a 'bad' mining address + miner.Start(common.HexToAddress("0xdead")) + waitForMiningState(t, miner, true) + // Start the downloader + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + // Now user tries to configure proper mining address + miner.Start(common.HexToAddress("0x1337")) + // Stop the downloader and wait for the update loop to run + mux.Post(downloader.DoneEvent{}) + + waitForMiningState(t, miner, true) + // The miner should now be using the good address + if got, exp := miner.coinbase, common.HexToAddress("0x1337"); got != exp { + t.Fatalf("Wrong coinbase, got %x expected %x", got, exp) + } +} + // waitForMiningState waits until either // * the desired mining state was reached // * a timeout was reached which fails the test @@ -147,10 +232,10 @@ func waitForMiningState(t *testing.T, m *Miner, mining bool) { var state bool for i := 0; i < 100; i++ { + time.Sleep(10 * time.Millisecond) if state = m.Mining(); state == mining { return } - time.Sleep(10 * time.Millisecond) } t.Fatalf("Mining() == %t, want %t", state, mining) } diff --git a/miner/unconfirmed.go b/miner/unconfirmed.go index 3a176e8bd6..0489f1ea4a 100644 --- a/miner/unconfirmed.go +++ b/miner/unconfirmed.go @@ -50,7 +50,7 @@ type unconfirmedBlocks struct { chain chainRetriever // Blockchain to verify canonical status through depth uint // Depth after which to discard previous blocks blocks *ring.Ring // Block infos to allow canonical chain cross checks - lock sync.RWMutex // Protects the fields from concurrent access + lock sync.Mutex // Protects the fields from concurrent access } // newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. diff --git a/mobile/big.go b/mobile/big.go index 86ea93245a..c08bcf93f2 100644 --- a/mobile/big.go +++ b/mobile/big.go @@ -35,6 +35,16 @@ func NewBigInt(x int64) *BigInt { return &BigInt{big.NewInt(x)} } +// NewBigIntFromString allocates and returns a new BigInt set to x +// interpreted in the provided base. +func NewBigIntFromString(x string, base int) *BigInt { + b, success := new(big.Int).SetString(x, base) + if !success { + return nil + } + return &BigInt{b} +} + // GetBytes returns the absolute value of x as a big-endian byte slice. func (bi *BigInt) GetBytes() []byte { return bi.bigint.Bytes() diff --git a/mobile/bind.go b/mobile/bind.go index f64b37ec15..afa97b5382 100644 --- a/mobile/bind.go +++ b/mobile/bind.go @@ -171,20 +171,12 @@ func (c *BoundContract) GetDeployer() *Transaction { // Call invokes the (constant) contract method with params as input values and // sets the output to result. func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { - if len(out.objects) == 1 { - result := out.objects[0] - if err := c.contract.Call(&opts.opts, result, method, args.objects...); err != nil { - return err - } - out.objects[0] = result - } else { - results := make([]interface{}, len(out.objects)) - copy(results, out.objects) - if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { - return err - } - copy(out.objects, results) + results := make([]interface{}, len(out.objects)) + copy(results, out.objects) + if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { + return err } + copy(out.objects, results) return nil } diff --git a/node/rpcstack.go b/node/rpcstack.go index 1c032f6e5d..71ad367248 100644 --- a/node/rpcstack.go +++ b/node/rpcstack.go @@ -330,7 +330,7 @@ func (h *httpServer) wsAllowed() bool { // isWebsocket checks the header of an http request for a websocket upgrade request. func isWebsocket(r *http.Request) bool { return strings.ToLower(r.Header.Get("Upgrade")) == "websocket" && - strings.ToLower(r.Header.Get("Connection")) == "upgrade" + strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade") } // NewHTTPHandlerStack returns wrapped http-related handlers diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go index 7f7889e81c..a56383d8c5 100644 --- a/node/rpcstack_test.go +++ b/node/rpcstack_test.go @@ -73,6 +73,21 @@ func TestWebsocketOrigins(t *testing.T) { assert.Error(t, err) } +// TestIsWebsocket tests if an incoming websocket upgrade request is handled properly. +func TestIsWebsocket(t *testing.T) { + r, _ := http.NewRequest("GET", "/", nil) + + assert.False(t, isWebsocket(r)) + r.Header.Set("upgrade", "websocket") + assert.False(t, isWebsocket(r)) + r.Header.Set("connection", "upgrade") + assert.True(t, isWebsocket(r)) + r.Header.Set("connection", "upgrade,keep-alive") + assert.True(t, isWebsocket(r)) + r.Header.Set("connection", " UPGRADE,keep-alive") + assert.True(t, isWebsocket(r)) +} + func createAndStartServer(t *testing.T, conf httpConfig, ws bool, wsConf wsConfig) *httpServer { t.Helper() diff --git a/p2p/discover/node.go b/p2p/discover/node.go index e635c64ac9..9ffe101ccf 100644 --- a/p2p/discover/node.go +++ b/p2p/discover/node.go @@ -46,7 +46,10 @@ func encodePubkey(key *ecdsa.PublicKey) encPubkey { return e } -func decodePubkey(curve elliptic.Curve, e encPubkey) (*ecdsa.PublicKey, error) { +func decodePubkey(curve elliptic.Curve, e []byte) (*ecdsa.PublicKey, error) { + if len(e) != len(encPubkey{}) { + return nil, errors.New("wrong size public key data") + } p := &ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)} half := len(e) / 2 p.X.SetBytes(e[:half]) diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go index 44b62e751b..47a2e7ac3c 100644 --- a/p2p/discover/table_util_test.go +++ b/p2p/discover/table_util_test.go @@ -146,7 +146,6 @@ func (t *pingRecorder) updateRecord(n *enode.Node) { func (t *pingRecorder) Self() *enode.Node { return nullNode } func (t *pingRecorder) lookupSelf() []*enode.Node { return nil } func (t *pingRecorder) lookupRandom() []*enode.Node { return nil } -func (t *pingRecorder) close() {} // ping simulates a ping request. func (t *pingRecorder) ping(n *enode.Node) (seq uint64, err error) { @@ -188,15 +187,16 @@ func hasDuplicates(slice []*node) bool { return false } +// checkNodesEqual checks whether the two given node lists contain the same nodes. func checkNodesEqual(got, want []*enode.Node) error { if len(got) == len(want) { for i := range got { if !nodeEqual(got[i], want[i]) { goto NotEqual } - return nil } } + return nil NotEqual: output := new(bytes.Buffer) @@ -227,6 +227,7 @@ func sortedByDistanceTo(distbase enode.ID, slice []*node) bool { }) } +// hexEncPrivkey decodes h as a private key. func hexEncPrivkey(h string) *ecdsa.PrivateKey { b, err := hex.DecodeString(h) if err != nil { @@ -239,6 +240,7 @@ func hexEncPrivkey(h string) *ecdsa.PrivateKey { return key } +// hexEncPubkey decodes h as a public key. func hexEncPubkey(h string) (ret encPubkey) { b, err := hex.DecodeString(h) if err != nil { diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go index 2009385262..a00de9ca18 100644 --- a/p2p/discover/v4_lookup_test.go +++ b/p2p/discover/v4_lookup_test.go @@ -34,7 +34,7 @@ func TestUDPv4_Lookup(t *testing.T) { test := newUDPTest(t) // Lookup on empty table returns no nodes. - targetKey, _ := decodePubkey(crypto.S256(), lookupTestnet.target) + targetKey, _ := decodePubkey(crypto.S256(), lookupTestnet.target[:]) if results := test.udp.LookupPubkey(targetKey); len(results) > 0 { t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) } @@ -279,17 +279,21 @@ func (tn *preminedTestnet) nodesAtDistance(dist int) []v4wire.Node { return result } -func (tn *preminedTestnet) neighborsAtDistance(base *enode.Node, distance uint, elems int) []*enode.Node { - nodes := nodesByDistance{target: base.ID()} +func (tn *preminedTestnet) neighborsAtDistances(base *enode.Node, distances []uint, elems int) []*enode.Node { + var result []*enode.Node for d := range lookupTestnet.dists { for i := range lookupTestnet.dists[d] { n := lookupTestnet.node(d, i) - if uint(enode.LogDist(n.ID(), base.ID())) == distance { - nodes.push(wrapNode(n), elems) + d := enode.LogDist(base.ID(), n.ID()) + if containsUint(uint(d), distances) { + result = append(result, n) + if len(result) >= elems { + return result + } } } } - return unwrapNodes(nodes.entries) + return result } func (tn *preminedTestnet) closest(n int) (nodes []*enode.Node) { diff --git a/p2p/discover/v5_encoding.go b/p2p/discover/v5_encoding.go deleted file mode 100644 index 842234e790..0000000000 --- a/p2p/discover/v5_encoding.go +++ /dev/null @@ -1,659 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discover - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/ecdsa" - "crypto/elliptic" - crand "crypto/rand" - "crypto/sha256" - "errors" - "fmt" - "hash" - "net" - "time" - - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/enr" - "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/hkdf" -) - -// TODO concurrent WHOAREYOU tie-breaker -// TODO deal with WHOAREYOU amplification factor (min packet size?) -// TODO add counter to nonce -// TODO rehandshake after X packets - -// Discovery v5 packet types. -const ( - p_pingV5 byte = iota + 1 - p_pongV5 - p_findnodeV5 - p_nodesV5 - p_requestTicketV5 - p_ticketV5 - p_regtopicV5 - p_regconfirmationV5 - p_topicqueryV5 - p_unknownV5 = byte(255) // any non-decryptable packet - p_whoareyouV5 = byte(254) // the WHOAREYOU packet -) - -// Discovery v5 packet structures. -type ( - // unknownV5 represents any packet that can't be decrypted. - unknownV5 struct { - AuthTag []byte - } - - // WHOAREYOU contains the handshake challenge. - whoareyouV5 struct { - AuthTag []byte - IDNonce [32]byte // To be signed by recipient. - RecordSeq uint64 // ENR sequence number of recipient - - node *enode.Node - sent mclock.AbsTime - } - - // PING is sent during liveness checks. - pingV5 struct { - ReqID []byte - ENRSeq uint64 - } - - // PONG is the reply to PING. - pongV5 struct { - ReqID []byte - ENRSeq uint64 - ToIP net.IP // These fields should mirror the UDP envelope address of the ping - ToPort uint16 // packet, which provides a way to discover the the external address (after NAT). - } - - // FINDNODE is a query for nodes in the given bucket. - findnodeV5 struct { - ReqID []byte - Distance uint - } - - // NODES is the reply to FINDNODE and TOPICQUERY. - nodesV5 struct { - ReqID []byte - Total uint8 - Nodes []*enr.Record - } - - // REQUESTTICKET requests a ticket for a topic queue. - requestTicketV5 struct { - ReqID []byte - Topic []byte - } - - // TICKET is the response to REQUESTTICKET. - ticketV5 struct { - ReqID []byte - Ticket []byte - } - - // REGTOPIC registers the sender in a topic queue using a ticket. - regtopicV5 struct { - ReqID []byte - Ticket []byte - ENR *enr.Record - } - - // REGCONFIRMATION is the reply to REGTOPIC. - regconfirmationV5 struct { - ReqID []byte - Registered bool - } - - // TOPICQUERY asks for nodes with the given topic. - topicqueryV5 struct { - ReqID []byte - Topic []byte - } -) - -const ( - // Encryption/authentication parameters. - authSchemeName = "gcm" - aesKeySize = 16 - gcmNonceSize = 12 - idNoncePrefix = "discovery-id-nonce" - handshakeTimeout = time.Second -) - -var ( - errTooShort = errors.New("packet too short") - errUnexpectedHandshake = errors.New("unexpected auth response, not in handshake") - errHandshakeNonceMismatch = errors.New("wrong nonce in auth response") - errInvalidAuthKey = errors.New("invalid ephemeral pubkey") - errUnknownAuthScheme = errors.New("unknown auth scheme in handshake") - errNoRecord = errors.New("expected ENR in handshake but none sent") - errInvalidNonceSig = errors.New("invalid ID nonce signature") - zeroNonce = make([]byte, gcmNonceSize) -) - -// wireCodec encodes and decodes discovery v5 packets. -type wireCodec struct { - sha256 hash.Hash - localnode *enode.LocalNode - privkey *ecdsa.PrivateKey - myChtagHash enode.ID - myWhoareyouMagic []byte - - sc *sessionCache -} - -type handshakeSecrets struct { - writeKey, readKey, authRespKey []byte -} - -type authHeader struct { - authHeaderList - isHandshake bool -} - -type authHeaderList struct { - Auth []byte // authentication info of packet - IDNonce [32]byte // IDNonce of WHOAREYOU - Scheme string // name of encryption/authentication scheme - EphemeralKey []byte // ephemeral public key - Response []byte // encrypted authResponse -} - -type authResponse struct { - Version uint - Signature []byte - Record *enr.Record `rlp:"nil"` // sender's record -} - -func (h *authHeader) DecodeRLP(r *rlp.Stream) error { - k, _, err := r.Kind() - if err != nil { - return err - } - if k == rlp.Byte || k == rlp.String { - return r.Decode(&h.Auth) - } - h.isHandshake = true - return r.Decode(&h.authHeaderList) -} - -// ephemeralKey decodes the ephemeral public key in the header. -func (h *authHeaderList) ephemeralKey(curve elliptic.Curve) *ecdsa.PublicKey { - var key encPubkey - copy(key[:], h.EphemeralKey) - pubkey, _ := decodePubkey(curve, key) - return pubkey -} - -// newWireCodec creates a wire codec. -func newWireCodec(ln *enode.LocalNode, key *ecdsa.PrivateKey, clock mclock.Clock) *wireCodec { - c := &wireCodec{ - sha256: sha256.New(), - localnode: ln, - privkey: key, - sc: newSessionCache(1024, clock), - } - // Create magic strings for packet matching. - self := ln.ID() - c.myWhoareyouMagic = c.sha256sum(self[:], []byte("WHOAREYOU")) - copy(c.myChtagHash[:], c.sha256sum(self[:])) - return c -} - -// encode encodes a packet to a node. 'id' and 'addr' specify the destination node. The -// 'challenge' parameter should be the most recently received WHOAREYOU packet from that -// node. -func (c *wireCodec) encode(id enode.ID, addr string, packet packetV5, challenge *whoareyouV5) ([]byte, []byte, error) { - if packet.kind() == p_whoareyouV5 { - p := packet.(*whoareyouV5) - enc, err := c.encodeWhoareyou(id, p) - if err == nil { - c.sc.storeSentHandshake(id, addr, p) - } - return enc, nil, err - } - // Ensure calling code sets node if needed. - if challenge != nil && challenge.node == nil { - panic("BUG: missing challenge.node in encode") - } - writeKey := c.sc.writeKey(id, addr) - if writeKey != nil || challenge != nil { - return c.encodeEncrypted(id, addr, packet, writeKey, challenge) - } - return c.encodeRandom(id) -} - -// encodeRandom encodes a random packet. -func (c *wireCodec) encodeRandom(toID enode.ID) ([]byte, []byte, error) { - tag := xorTag(c.sha256sum(toID[:]), c.localnode.ID()) - r := make([]byte, 44) // TODO randomize size - if _, err := crand.Read(r); err != nil { - return nil, nil, err - } - nonce := make([]byte, gcmNonceSize) - if _, err := crand.Read(nonce); err != nil { - return nil, nil, fmt.Errorf("can't get random data: %v", err) - } - b := new(bytes.Buffer) - b.Write(tag[:]) - rlp.Encode(b, nonce) - b.Write(r) - return b.Bytes(), nonce, nil -} - -// encodeWhoareyou encodes WHOAREYOU. -func (c *wireCodec) encodeWhoareyou(toID enode.ID, packet *whoareyouV5) ([]byte, error) { - // Sanity check node field to catch misbehaving callers. - if packet.RecordSeq > 0 && packet.node == nil { - panic("BUG: missing node in whoareyouV5 with non-zero seq") - } - b := new(bytes.Buffer) - b.Write(c.sha256sum(toID[:], []byte("WHOAREYOU"))) - err := rlp.Encode(b, packet) - return b.Bytes(), err -} - -// encodeEncrypted encodes an encrypted packet. -func (c *wireCodec) encodeEncrypted(toID enode.ID, toAddr string, packet packetV5, writeKey []byte, challenge *whoareyouV5) (enc []byte, authTag []byte, err error) { - nonce := make([]byte, gcmNonceSize) - if _, err := crand.Read(nonce); err != nil { - return nil, nil, fmt.Errorf("can't get random data: %v", err) - } - - var headEnc []byte - if challenge == nil { - // Regular packet, use existing key and simply encode nonce. - headEnc, _ = rlp.EncodeToBytes(nonce) - } else { - // We're answering WHOAREYOU, generate new keys and encrypt with those. - header, sec, err := c.makeAuthHeader(nonce, challenge) - if err != nil { - return nil, nil, err - } - if headEnc, err = rlp.EncodeToBytes(header); err != nil { - return nil, nil, err - } - c.sc.storeNewSession(toID, toAddr, sec.readKey, sec.writeKey) - writeKey = sec.writeKey - } - - // Encode the packet. - body := new(bytes.Buffer) - body.WriteByte(packet.kind()) - if err := rlp.Encode(body, packet); err != nil { - return nil, nil, err - } - tag := xorTag(c.sha256sum(toID[:]), c.localnode.ID()) - headsize := len(tag) + len(headEnc) - headbuf := make([]byte, headsize) - copy(headbuf[:], tag[:]) - copy(headbuf[len(tag):], headEnc) - - // Encrypt the body. - enc, err = encryptGCM(headbuf, writeKey, nonce, body.Bytes(), tag[:]) - return enc, nonce, err -} - -// encodeAuthHeader creates the auth header on a call packet following WHOAREYOU. -func (c *wireCodec) makeAuthHeader(nonce []byte, challenge *whoareyouV5) (*authHeaderList, *handshakeSecrets, error) { - resp := &authResponse{Version: 5} - - // Add our record to response if it's newer than what remote - // side has. - ln := c.localnode.Node() - if challenge.RecordSeq < ln.Seq() { - resp.Record = ln.Record() - } - - // Create the ephemeral key. This needs to be first because the - // key is part of the ID nonce signature. - var remotePubkey = new(ecdsa.PublicKey) - if err := challenge.node.Load((*enode.Secp256k1)(remotePubkey)); err != nil { - return nil, nil, fmt.Errorf("can't find secp256k1 key for recipient") - } - ephkey, err := crypto.GenerateKey() - if err != nil { - return nil, nil, fmt.Errorf("can't generate ephemeral key") - } - ephpubkey := encodePubkey(&ephkey.PublicKey) - - // Add ID nonce signature to response. - idsig, err := c.signIDNonce(challenge.IDNonce[:], ephpubkey[:]) - if err != nil { - return nil, nil, fmt.Errorf("can't sign: %v", err) - } - resp.Signature = idsig - - // Create session keys. - sec := c.deriveKeys(c.localnode.ID(), challenge.node.ID(), ephkey, remotePubkey, challenge) - if sec == nil { - return nil, nil, fmt.Errorf("key derivation failed") - } - - // Encrypt the authentication response and assemble the auth header. - respRLP, err := rlp.EncodeToBytes(resp) - if err != nil { - return nil, nil, fmt.Errorf("can't encode auth response: %v", err) - } - respEnc, err := encryptGCM(nil, sec.authRespKey, zeroNonce, respRLP, nil) - if err != nil { - return nil, nil, fmt.Errorf("can't encrypt auth response: %v", err) - } - head := &authHeaderList{ - Auth: nonce, - Scheme: authSchemeName, - IDNonce: challenge.IDNonce, - EphemeralKey: ephpubkey[:], - Response: respEnc, - } - return head, sec, err -} - -// deriveKeys generates session keys using elliptic-curve Diffie-Hellman key agreement. -func (c *wireCodec) deriveKeys(n1, n2 enode.ID, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, challenge *whoareyouV5) *handshakeSecrets { - eph := ecdh(priv, pub) - if eph == nil { - return nil - } - - info := []byte("discovery v5 key agreement") - info = append(info, n1[:]...) - info = append(info, n2[:]...) - kdf := hkdf.New(c.sha256reset, eph, challenge.IDNonce[:], info) - sec := handshakeSecrets{ - writeKey: make([]byte, aesKeySize), - readKey: make([]byte, aesKeySize), - authRespKey: make([]byte, aesKeySize), - } - kdf.Read(sec.writeKey) - kdf.Read(sec.readKey) - kdf.Read(sec.authRespKey) - for i := range eph { - eph[i] = 0 - } - return &sec -} - -// signIDNonce creates the ID nonce signature. -func (c *wireCodec) signIDNonce(nonce, ephkey []byte) ([]byte, error) { - idsig, err := crypto.Sign(c.idNonceHash(nonce, ephkey), c.privkey) - if err != nil { - return nil, fmt.Errorf("can't sign: %v", err) - } - return idsig[:len(idsig)-1], nil // remove recovery ID -} - -// idNonceHash computes the hash of id nonce with prefix. -func (c *wireCodec) idNonceHash(nonce, ephkey []byte) []byte { - h := c.sha256reset() - h.Write([]byte(idNoncePrefix)) - h.Write(nonce) - h.Write(ephkey) - return h.Sum(nil) -} - -// decode decodes a discovery packet. -func (c *wireCodec) decode(input []byte, addr string) (enode.ID, *enode.Node, packetV5, error) { - // Delete timed-out handshakes. This must happen before decoding to avoid - // processing the same handshake twice. - c.sc.handshakeGC() - - if len(input) < 32 { - return enode.ID{}, nil, nil, errTooShort - } - if bytes.HasPrefix(input, c.myWhoareyouMagic) { - p, err := c.decodeWhoareyou(input) - return enode.ID{}, nil, p, err - } - sender := xorTag(input[:32], c.myChtagHash) - p, n, err := c.decodeEncrypted(sender, addr, input) - return sender, n, p, err -} - -// decodeWhoareyou decode a WHOAREYOU packet. -func (c *wireCodec) decodeWhoareyou(input []byte) (packetV5, error) { - packet := new(whoareyouV5) - err := rlp.DecodeBytes(input[32:], packet) - return packet, err -} - -// decodeEncrypted decodes an encrypted discovery packet. -func (c *wireCodec) decodeEncrypted(fromID enode.ID, fromAddr string, input []byte) (packetV5, *enode.Node, error) { - // Decode packet header. - var head authHeader - r := bytes.NewReader(input[32:]) - err := rlp.Decode(r, &head) - if err != nil { - return nil, nil, err - } - - // Decrypt and process auth response. - readKey, node, err := c.decodeAuth(fromID, fromAddr, &head) - if err != nil { - return nil, nil, err - } - - // Decrypt and decode the packet body. - headsize := len(input) - r.Len() - bodyEnc := input[headsize:] - body, err := decryptGCM(readKey, head.Auth, bodyEnc, input[:32]) - if err != nil { - if !head.isHandshake { - // Can't decrypt, start handshake. - return &unknownV5{AuthTag: head.Auth}, nil, nil - } - return nil, nil, fmt.Errorf("handshake failed: %v", err) - } - if len(body) == 0 { - return nil, nil, errTooShort - } - p, err := decodePacketBodyV5(body[0], body[1:]) - return p, node, err -} - -// decodeAuth processes an auth header. -func (c *wireCodec) decodeAuth(fromID enode.ID, fromAddr string, head *authHeader) ([]byte, *enode.Node, error) { - if !head.isHandshake { - return c.sc.readKey(fromID, fromAddr), nil, nil - } - - // Remote is attempting handshake. Verify against our last WHOAREYOU. - challenge := c.sc.getHandshake(fromID, fromAddr) - if challenge == nil { - return nil, nil, errUnexpectedHandshake - } - if head.IDNonce != challenge.IDNonce { - return nil, nil, errHandshakeNonceMismatch - } - sec, n, err := c.decodeAuthResp(fromID, fromAddr, &head.authHeaderList, challenge) - if err != nil { - return nil, n, err - } - // Swap keys to match remote. - sec.readKey, sec.writeKey = sec.writeKey, sec.readKey - c.sc.storeNewSession(fromID, fromAddr, sec.readKey, sec.writeKey) - c.sc.deleteHandshake(fromID, fromAddr) - return sec.readKey, n, err -} - -// decodeAuthResp decodes and verifies an authentication response. -func (c *wireCodec) decodeAuthResp(fromID enode.ID, fromAddr string, head *authHeaderList, challenge *whoareyouV5) (*handshakeSecrets, *enode.Node, error) { - // Decrypt / decode the response. - if head.Scheme != authSchemeName { - return nil, nil, errUnknownAuthScheme - } - ephkey := head.ephemeralKey(c.privkey.Curve) - if ephkey == nil { - return nil, nil, errInvalidAuthKey - } - sec := c.deriveKeys(fromID, c.localnode.ID(), c.privkey, ephkey, challenge) - respPT, err := decryptGCM(sec.authRespKey, zeroNonce, head.Response, nil) - if err != nil { - return nil, nil, fmt.Errorf("can't decrypt auth response header: %v", err) - } - var resp authResponse - if err := rlp.DecodeBytes(respPT, &resp); err != nil { - return nil, nil, fmt.Errorf("invalid auth response: %v", err) - } - - // Verify response node record. The remote node should include the record - // if we don't have one or if ours is older than the latest version. - node := challenge.node - if resp.Record != nil { - if node == nil || node.Seq() < resp.Record.Seq() { - n, err := enode.New(enode.ValidSchemes, resp.Record) - if err != nil { - return nil, nil, fmt.Errorf("invalid node record: %v", err) - } - if n.ID() != fromID { - return nil, nil, fmt.Errorf("record in auth respose has wrong ID: %v", n.ID()) - } - node = n - } - } - if node == nil { - return nil, nil, errNoRecord - } - - // Verify ID nonce signature. - err = c.verifyIDSignature(challenge.IDNonce[:], head.EphemeralKey, resp.Signature, node) - if err != nil { - return nil, nil, err - } - return sec, node, nil -} - -// verifyIDSignature checks that signature over idnonce was made by the node with given record. -func (c *wireCodec) verifyIDSignature(nonce, ephkey, sig []byte, n *enode.Node) error { - switch idscheme := n.Record().IdentityScheme(); idscheme { - case "v4": - var pk ecdsa.PublicKey - n.Load((*enode.Secp256k1)(&pk)) // cannot fail because record is valid - if !crypto.VerifySignature(crypto.FromECDSAPub(&pk), c.idNonceHash(nonce, ephkey), sig) { - return errInvalidNonceSig - } - return nil - default: - return fmt.Errorf("can't verify ID nonce signature against scheme %q", idscheme) - } -} - -// decodePacketBody decodes the body of an encrypted discovery packet. -func decodePacketBodyV5(ptype byte, body []byte) (packetV5, error) { - var dec packetV5 - switch ptype { - case p_pingV5: - dec = new(pingV5) - case p_pongV5: - dec = new(pongV5) - case p_findnodeV5: - dec = new(findnodeV5) - case p_nodesV5: - dec = new(nodesV5) - case p_requestTicketV5: - dec = new(requestTicketV5) - case p_ticketV5: - dec = new(ticketV5) - case p_regtopicV5: - dec = new(regtopicV5) - case p_regconfirmationV5: - dec = new(regconfirmationV5) - case p_topicqueryV5: - dec = new(topicqueryV5) - default: - return nil, fmt.Errorf("unknown packet type %d", ptype) - } - if err := rlp.DecodeBytes(body, dec); err != nil { - return nil, err - } - return dec, nil -} - -// sha256reset returns the shared hash instance. -func (c *wireCodec) sha256reset() hash.Hash { - c.sha256.Reset() - return c.sha256 -} - -// sha256sum computes sha256 on the concatenation of inputs. -func (c *wireCodec) sha256sum(inputs ...[]byte) []byte { - c.sha256.Reset() - for _, b := range inputs { - c.sha256.Write(b) - } - return c.sha256.Sum(nil) -} - -func xorTag(a []byte, b enode.ID) enode.ID { - var r enode.ID - for i := range r { - r[i] = a[i] ^ b[i] - } - return r -} - -// ecdh creates a shared secret. -func ecdh(privkey *ecdsa.PrivateKey, pubkey *ecdsa.PublicKey) []byte { - secX, secY := pubkey.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) - if secX == nil { - return nil - } - sec := make([]byte, 33) - sec[0] = 0x02 | byte(secY.Bit(0)) - math.ReadBits(secX, sec[1:]) - return sec -} - -// encryptGCM encrypts pt using AES-GCM with the given key and nonce. -func encryptGCM(dest, key, nonce, pt, authData []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - panic(fmt.Errorf("can't create block cipher: %v", err)) - } - aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize) - if err != nil { - panic(fmt.Errorf("can't create GCM: %v", err)) - } - return aesgcm.Seal(dest, nonce, pt, authData), nil -} - -// decryptGCM decrypts ct using AES-GCM with the given key and nonce. -func decryptGCM(key, nonce, ct, authData []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, fmt.Errorf("can't create block cipher: %v", err) - } - if len(nonce) != gcmNonceSize { - return nil, fmt.Errorf("invalid GCM nonce size: %d", len(nonce)) - } - aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize) - if err != nil { - return nil, fmt.Errorf("can't create GCM: %v", err) - } - pt := make([]byte, 0, len(ct)) - return aesgcm.Open(pt, nonce, ct, authData) -} diff --git a/p2p/discover/v5_encoding_test.go b/p2p/discover/v5_encoding_test.go deleted file mode 100644 index 77e6bae6ae..0000000000 --- a/p2p/discover/v5_encoding_test.go +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package discover - -import ( - "bytes" - "crypto/ecdsa" - "encoding/hex" - "fmt" - "net" - "reflect" - "testing" - - "github.com/davecgh/go-spew/spew" - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/p2p/enode" -) - -var ( - testKeyA, _ = crypto.HexToECDSA("eef77acb6c6a6eebc5b363a475ac583ec7eccdb42b6481424c60f59aa326547f") - testKeyB, _ = crypto.HexToECDSA("66fb62bfbd66b9177a138c1e5cddbe4f7c30c343e94e68df8769459cb1cde628") - testIDnonce = [32]byte{5, 6, 7, 8, 9, 10, 11, 12} -) - -func TestDeriveKeysV5(t *testing.T) { - t.Parallel() - - var ( - n1 = enode.ID{1} - n2 = enode.ID{2} - challenge = &whoareyouV5{} - db, _ = enode.OpenDB("") - ln = enode.NewLocalNode(db, testKeyA) - c = newWireCodec(ln, testKeyA, mclock.System{}) - ) - defer db.Close() - - sec1 := c.deriveKeys(n1, n2, testKeyA, &testKeyB.PublicKey, challenge) - sec2 := c.deriveKeys(n1, n2, testKeyB, &testKeyA.PublicKey, challenge) - if sec1 == nil || sec2 == nil { - t.Fatal("key agreement failed") - } - if !reflect.DeepEqual(sec1, sec2) { - t.Fatalf("keys not equal:\n %+v\n %+v", sec1, sec2) - } -} - -// This test checks the basic handshake flow where A talks to B and A has no secrets. -func TestHandshakeV5(t *testing.T) { - t.Parallel() - net := newHandshakeTest() - defer net.close() - - // A -> B RANDOM PACKET - packet, _ := net.nodeA.encode(t, net.nodeB, &findnodeV5{}) - resp := net.nodeB.expectDecode(t, p_unknownV5, packet) - - // A <- B WHOAREYOU - challenge := &whoareyouV5{ - AuthTag: resp.(*unknownV5).AuthTag, - IDNonce: testIDnonce, - RecordSeq: 0, - } - whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) - net.nodeA.expectDecode(t, p_whoareyouV5, whoareyou) - - // A -> B FINDNODE - findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &findnodeV5{}) - net.nodeB.expectDecode(t, p_findnodeV5, findnode) - if len(net.nodeB.c.sc.handshakes) > 0 { - t.Fatalf("node B didn't remove handshake from challenge map") - } - - // A <- B NODES - nodes, _ := net.nodeB.encode(t, net.nodeA, &nodesV5{Total: 1}) - net.nodeA.expectDecode(t, p_nodesV5, nodes) -} - -// This test checks that handshake attempts are removed within the timeout. -func TestHandshakeV5_timeout(t *testing.T) { - t.Parallel() - net := newHandshakeTest() - defer net.close() - - // A -> B RANDOM PACKET - packet, _ := net.nodeA.encode(t, net.nodeB, &findnodeV5{}) - resp := net.nodeB.expectDecode(t, p_unknownV5, packet) - - // A <- B WHOAREYOU - challenge := &whoareyouV5{ - AuthTag: resp.(*unknownV5).AuthTag, - IDNonce: testIDnonce, - RecordSeq: 0, - } - whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) - net.nodeA.expectDecode(t, p_whoareyouV5, whoareyou) - - // A -> B FINDNODE after timeout - net.clock.Run(handshakeTimeout + 1) - findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &findnodeV5{}) - net.nodeB.expectDecodeErr(t, errUnexpectedHandshake, findnode) -} - -// This test checks handshake behavior when no record is sent in the auth response. -func TestHandshakeV5_norecord(t *testing.T) { - t.Parallel() - net := newHandshakeTest() - defer net.close() - - // A -> B RANDOM PACKET - packet, _ := net.nodeA.encode(t, net.nodeB, &findnodeV5{}) - resp := net.nodeB.expectDecode(t, p_unknownV5, packet) - - // A <- B WHOAREYOU - nodeA := net.nodeA.n() - if nodeA.Seq() == 0 { - t.Fatal("need non-zero sequence number") - } - challenge := &whoareyouV5{ - AuthTag: resp.(*unknownV5).AuthTag, - IDNonce: testIDnonce, - RecordSeq: nodeA.Seq(), - node: nodeA, - } - whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) - net.nodeA.expectDecode(t, p_whoareyouV5, whoareyou) - - // A -> B FINDNODE - findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &findnodeV5{}) - net.nodeB.expectDecode(t, p_findnodeV5, findnode) - - // A <- B NODES - nodes, _ := net.nodeB.encode(t, net.nodeA, &nodesV5{Total: 1}) - net.nodeA.expectDecode(t, p_nodesV5, nodes) -} - -// In this test, A tries to send FINDNODE with existing secrets but B doesn't know -// anything about A. -func TestHandshakeV5_rekey(t *testing.T) { - t.Parallel() - net := newHandshakeTest() - defer net.close() - - initKeys := &handshakeSecrets{ - readKey: []byte("BBBBBBBBBBBBBBBB"), - writeKey: []byte("AAAAAAAAAAAAAAAA"), - } - net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), initKeys.readKey, initKeys.writeKey) - - // A -> B FINDNODE (encrypted with zero keys) - findnode, authTag := net.nodeA.encode(t, net.nodeB, &findnodeV5{}) - net.nodeB.expectDecode(t, p_unknownV5, findnode) - - // A <- B WHOAREYOU - challenge := &whoareyouV5{AuthTag: authTag, IDNonce: testIDnonce} - whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) - net.nodeA.expectDecode(t, p_whoareyouV5, whoareyou) - - // Check that new keys haven't been stored yet. - if s := net.nodeA.c.sc.session(net.nodeB.id(), net.nodeB.addr()); !bytes.Equal(s.writeKey, initKeys.writeKey) || !bytes.Equal(s.readKey, initKeys.readKey) { - t.Fatal("node A stored keys too early") - } - if s := net.nodeB.c.sc.session(net.nodeA.id(), net.nodeA.addr()); s != nil { - t.Fatal("node B stored keys too early") - } - - // A -> B FINDNODE encrypted with new keys - findnode, _ = net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &findnodeV5{}) - net.nodeB.expectDecode(t, p_findnodeV5, findnode) - - // A <- B NODES - nodes, _ := net.nodeB.encode(t, net.nodeA, &nodesV5{Total: 1}) - net.nodeA.expectDecode(t, p_nodesV5, nodes) -} - -// In this test A and B have different keys before the handshake. -func TestHandshakeV5_rekey2(t *testing.T) { - t.Parallel() - net := newHandshakeTest() - defer net.close() - - initKeysA := &handshakeSecrets{ - readKey: []byte("BBBBBBBBBBBBBBBB"), - writeKey: []byte("AAAAAAAAAAAAAAAA"), - } - initKeysB := &handshakeSecrets{ - readKey: []byte("CCCCCCCCCCCCCCCC"), - writeKey: []byte("DDDDDDDDDDDDDDDD"), - } - net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), initKeysA.readKey, initKeysA.writeKey) - net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), initKeysB.readKey, initKeysA.writeKey) - - // A -> B FINDNODE encrypted with initKeysA - findnode, authTag := net.nodeA.encode(t, net.nodeB, &findnodeV5{Distance: 3}) - net.nodeB.expectDecode(t, p_unknownV5, findnode) - - // A <- B WHOAREYOU - challenge := &whoareyouV5{AuthTag: authTag, IDNonce: testIDnonce} - whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) - net.nodeA.expectDecode(t, p_whoareyouV5, whoareyou) - - // A -> B FINDNODE encrypted with new keys - findnode, _ = net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &findnodeV5{}) - net.nodeB.expectDecode(t, p_findnodeV5, findnode) - - // A <- B NODES - nodes, _ := net.nodeB.encode(t, net.nodeA, &nodesV5{Total: 1}) - net.nodeA.expectDecode(t, p_nodesV5, nodes) -} - -// This test checks some malformed packets. -func TestDecodeErrorsV5(t *testing.T) { - t.Parallel() - net := newHandshakeTest() - defer net.close() - - net.nodeA.expectDecodeErr(t, errTooShort, []byte{}) - // TODO some more tests would be nice :) -} - -// This benchmark checks performance of authHeader decoding, verification and key derivation. -func BenchmarkV5_DecodeAuthSecp256k1(b *testing.B) { - net := newHandshakeTest() - defer net.close() - - var ( - idA = net.nodeA.id() - addrA = net.nodeA.addr() - challenge = &whoareyouV5{AuthTag: []byte("authresp"), RecordSeq: 0, node: net.nodeB.n()} - nonce = make([]byte, gcmNonceSize) - ) - header, _, _ := net.nodeA.c.makeAuthHeader(nonce, challenge) - challenge.node = nil // force ENR signature verification in decoder - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, err := net.nodeB.c.decodeAuthResp(idA, addrA, header, challenge) - if err != nil { - b.Fatal(err) - } - } -} - -// This benchmark checks how long it takes to decode an encrypted ping packet. -func BenchmarkV5_DecodePing(b *testing.B) { - net := newHandshakeTest() - defer net.close() - - r := []byte{233, 203, 93, 195, 86, 47, 177, 186, 227, 43, 2, 141, 244, 230, 120, 17} - w := []byte{79, 145, 252, 171, 167, 216, 252, 161, 208, 190, 176, 106, 214, 39, 178, 134} - net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), r, w) - net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), w, r) - addrB := net.nodeA.addr() - ping := &pingV5{ReqID: []byte("reqid"), ENRSeq: 5} - enc, _, err := net.nodeA.c.encode(net.nodeB.id(), addrB, ping, nil) - if err != nil { - b.Fatalf("can't encode: %v", err) - } - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, _, p, _ := net.nodeB.c.decode(enc, addrB) - if _, ok := p.(*pingV5); !ok { - b.Fatalf("wrong packet type %T", p) - } - } -} - -var pp = spew.NewDefaultConfig() - -type handshakeTest struct { - nodeA, nodeB handshakeTestNode - clock mclock.Simulated -} - -type handshakeTestNode struct { - ln *enode.LocalNode - c *wireCodec -} - -func newHandshakeTest() *handshakeTest { - t := new(handshakeTest) - t.nodeA.init(testKeyA, net.IP{127, 0, 0, 1}, &t.clock) - t.nodeB.init(testKeyB, net.IP{127, 0, 0, 1}, &t.clock) - return t -} - -func (t *handshakeTest) close() { - t.nodeA.ln.Database().Close() - t.nodeB.ln.Database().Close() -} - -func (n *handshakeTestNode) init(key *ecdsa.PrivateKey, ip net.IP, clock mclock.Clock) { - db, _ := enode.OpenDB("") - n.ln = enode.NewLocalNode(db, key) - n.ln.SetStaticIP(ip) - n.c = newWireCodec(n.ln, key, clock) -} - -func (n *handshakeTestNode) encode(t testing.TB, to handshakeTestNode, p packetV5) ([]byte, []byte) { - t.Helper() - return n.encodeWithChallenge(t, to, nil, p) -} - -func (n *handshakeTestNode) encodeWithChallenge(t testing.TB, to handshakeTestNode, c *whoareyouV5, p packetV5) ([]byte, []byte) { - t.Helper() - // Copy challenge and add destination node. This avoids sharing 'c' among the two codecs. - var challenge *whoareyouV5 - if c != nil { - challengeCopy := *c - challenge = &challengeCopy - challenge.node = to.n() - } - // Encode to destination. - enc, authTag, err := n.c.encode(to.id(), to.addr(), p, challenge) - if err != nil { - t.Fatal(fmt.Errorf("(%s) %v", n.ln.ID().TerminalString(), err)) - } - t.Logf("(%s) -> (%s) %s\n%s", n.ln.ID().TerminalString(), to.id().TerminalString(), p.name(), hex.Dump(enc)) - return enc, authTag -} - -func (n *handshakeTestNode) expectDecode(t *testing.T, ptype byte, p []byte) packetV5 { - t.Helper() - dec, err := n.decode(p) - if err != nil { - t.Fatal(fmt.Errorf("(%s) %v", n.ln.ID().TerminalString(), err)) - } - t.Logf("(%s) %#v", n.ln.ID().TerminalString(), pp.NewFormatter(dec)) - if dec.kind() != ptype { - t.Fatalf("expected packet type %d, got %d", ptype, dec.kind()) - } - return dec -} - -func (n *handshakeTestNode) expectDecodeErr(t *testing.T, wantErr error, p []byte) { - t.Helper() - if _, err := n.decode(p); !reflect.DeepEqual(err, wantErr) { - t.Fatal(fmt.Errorf("(%s) got err %q, want %q", n.ln.ID().TerminalString(), err, wantErr)) - } -} - -func (n *handshakeTestNode) decode(input []byte) (packetV5, error) { - _, _, p, err := n.c.decode(input, "127.0.0.1") - return p, err -} - -func (n *handshakeTestNode) n() *enode.Node { - return n.ln.Node() -} - -func (n *handshakeTestNode) addr() string { - return n.ln.Node().IP().String() -} - -func (n *handshakeTestNode) id() enode.ID { - return n.ln.ID() -} diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index d53375b48b..c95317a005 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/discover/v5wire" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/netutil" @@ -38,36 +39,24 @@ import ( const ( lookupRequestLimit = 3 // max requests against a single node during lookup - findnodeResultLimit = 15 // applies in FINDNODE handler + findnodeResultLimit = 16 // applies in FINDNODE handler totalNodesResponseLimit = 5 // applies in waitForNodes nodesResponseItemLimit = 3 // applies in sendNodes respTimeoutV5 = 700 * time.Millisecond ) -// codecV5 is implemented by wireCodec (and testCodec). +// codecV5 is implemented by v5wire.Codec (and testCodec). // // The UDPv5 transport is split into two objects: the codec object deals with // encoding/decoding and with the handshake; the UDPv5 object handles higher-level concerns. type codecV5 interface { - // encode encodes a packet. The 'challenge' parameter is non-nil for calls which got a - // WHOAREYOU response. - encode(fromID enode.ID, fromAddr string, p packetV5, challenge *whoareyouV5) (enc []byte, authTag []byte, err error) + // Encode encodes a packet. + Encode(enode.ID, string, v5wire.Packet, *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error) - // decode decodes a packet. It returns an *unknownV5 packet if decryption fails. - // The fromNode return value is non-nil when the input contains a handshake response. - decode(input []byte, fromAddr string) (fromID enode.ID, fromNode *enode.Node, p packetV5, err error) -} - -// packetV5 is implemented by all discv5 packet type structs. -type packetV5 interface { - // These methods provide information and set the request ID. - name() string - kind() byte - setreqid([]byte) - // handle should perform the appropriate action to handle the packet, i.e. this is the - // place to send the response. - handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) + // decode decodes a packet. It returns a *v5wire.Unknown packet if decryption fails. + // The *enode.Node return value is non-nil when the input contains a handshake response. + Decode([]byte, string) (enode.ID, *enode.Node, v5wire.Packet, error) } // UDPv5 is the implementation of protocol version 5. @@ -83,6 +72,10 @@ type UDPv5 struct { clock mclock.Clock validSchemes enr.IdentityScheme + // talkreq handler registry + trlock sync.Mutex + trhandlers map[string]func([]byte) []byte + // channels into dispatch packetInCh chan ReadPacket readNextCh chan struct{} @@ -93,7 +86,7 @@ type UDPv5 struct { // state of dispatch codec codecV5 activeCallByNode map[enode.ID]*callV5 - activeCallByAuth map[string]*callV5 + activeCallByAuth map[v5wire.Nonce]*callV5 callQueue map[enode.ID][]*callV5 // shutdown stuff @@ -106,16 +99,16 @@ type UDPv5 struct { // callV5 represents a remote procedure call against another node. type callV5 struct { node *enode.Node - packet packetV5 + packet v5wire.Packet responseType byte // expected packet type of response reqid []byte - ch chan packetV5 // responses sent here - err chan error // errors sent here + ch chan v5wire.Packet // responses sent here + err chan error // errors sent here // Valid for active calls only: - authTag []byte // authTag of request packet - handshakeCount int // # times we attempted handshake for this call - challenge *whoareyouV5 // last sent handshake challenge + nonce v5wire.Nonce // nonce of request packet + handshakeCount int // # times we attempted handshake for this call + challenge *v5wire.Whoareyou // last sent handshake challenge timeout mclock.Timer } @@ -152,6 +145,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) { log: cfg.Log, validSchemes: cfg.ValidSchemes, clock: cfg.Clock, + trhandlers: make(map[string]func([]byte) []byte), // channels into dispatch packetInCh: make(chan ReadPacket, 1), readNextCh: make(chan struct{}, 1), @@ -159,9 +153,9 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) { callDoneCh: make(chan *callV5), respTimeoutCh: make(chan *callTimeout), // state of dispatch - codec: newWireCodec(ln, cfg.PrivateKey, cfg.Clock), + codec: v5wire.NewCodec(ln, cfg.PrivateKey, cfg.Clock), activeCallByNode: make(map[enode.ID]*callV5), - activeCallByAuth: make(map[string]*callV5), + activeCallByAuth: make(map[v5wire.Nonce]*callV5), callQueue: make(map[enode.ID][]*callV5), // shutdown closeCtx: closeCtx, @@ -236,6 +230,29 @@ func (t *UDPv5) LocalNode() *enode.LocalNode { return t.localNode } +// RegisterTalkHandler adds a handler for 'talk requests'. The handler function is called +// whenever a request for the given protocol is received and should return the response +// data or nil. +func (t *UDPv5) RegisterTalkHandler(protocol string, handler func([]byte) []byte) { + t.trlock.Lock() + defer t.trlock.Unlock() + t.trhandlers[protocol] = handler +} + +// TalkRequest sends a talk request to n and waits for a response. +func (t *UDPv5) TalkRequest(n *enode.Node, protocol string, request []byte) ([]byte, error) { + req := &v5wire.TalkRequest{Protocol: protocol, Message: request} + resp := t.call(n, v5wire.TalkResponseMsg, req) + defer t.callDone(resp) + select { + case respMsg := <-resp.ch: + return respMsg.(*v5wire.TalkResponse).Message, nil + case err := <-resp.err: + return nil, err + } +} + +// RandomNodes returns an iterator that finds random nodes in the DHT. func (t *UDPv5) RandomNodes() enode.Iterator { if t.tab.len() == 0 { // All nodes were dropped, refresh. The very first query will hit this @@ -283,16 +300,14 @@ func (t *UDPv5) lookupWorker(destNode *node, target enode.ID) ([]*node, error) { nodes = nodesByDistance{target: target} err error ) - for i := 0; i < lookupRequestLimit && len(nodes.entries) < findnodeResultLimit; i++ { - var r []*enode.Node - r, err = t.findnode(unwrapNode(destNode), dists[i]) - if err == errClosed { - return nil, err - } - for _, n := range r { - if n.ID() != t.Self().ID() { - nodes.push(wrapNode(n), findnodeResultLimit) - } + var r []*enode.Node + r, err = t.findnode(unwrapNode(destNode), dists) + if err == errClosed { + return nil, err + } + for _, n := range r { + if n.ID() != t.Self().ID() { + nodes.push(wrapNode(n), findnodeResultLimit) } } return nodes.entries, err @@ -301,15 +316,15 @@ func (t *UDPv5) lookupWorker(destNode *node, target enode.ID) ([]*node, error) { // lookupDistances computes the distance parameter for FINDNODE calls to dest. // It chooses distances adjacent to logdist(target, dest), e.g. for a target // with logdist(target, dest) = 255 the result is [255, 256, 254]. -func lookupDistances(target, dest enode.ID) (dists []int) { +func lookupDistances(target, dest enode.ID) (dists []uint) { td := enode.LogDist(target, dest) - dists = append(dists, td) + dists = append(dists, uint(td)) for i := 1; len(dists) < lookupRequestLimit; i++ { if td+i < 256 { - dists = append(dists, td+i) + dists = append(dists, uint(td+i)) } if td-i > 0 { - dists = append(dists, td-i) + dists = append(dists, uint(td-i)) } } return dists @@ -317,11 +332,13 @@ func lookupDistances(target, dest enode.ID) (dists []int) { // ping calls PING on a node and waits for a PONG response. func (t *UDPv5) ping(n *enode.Node) (uint64, error) { - resp := t.call(n, p_pongV5, &pingV5{ENRSeq: t.localNode.Node().Seq()}) + req := &v5wire.Ping{ENRSeq: t.localNode.Node().Seq()} + resp := t.call(n, v5wire.PongMsg, req) defer t.callDone(resp) + select { case pong := <-resp.ch: - return pong.(*pongV5).ENRSeq, nil + return pong.(*v5wire.Pong).ENRSeq, nil case err := <-resp.err: return 0, err } @@ -329,7 +346,7 @@ func (t *UDPv5) ping(n *enode.Node) (uint64, error) { // requestENR requests n's record. func (t *UDPv5) RequestENR(n *enode.Node) (*enode.Node, error) { - nodes, err := t.findnode(n, 0) + nodes, err := t.findnode(n, []uint{0}) if err != nil { return nil, err } @@ -339,26 +356,14 @@ func (t *UDPv5) RequestENR(n *enode.Node) (*enode.Node, error) { return nodes[0], nil } -// requestTicket calls REQUESTTICKET on a node and waits for a TICKET response. -func (t *UDPv5) requestTicket(n *enode.Node) ([]byte, error) { - resp := t.call(n, p_ticketV5, &pingV5{}) - defer t.callDone(resp) - select { - case response := <-resp.ch: - return response.(*ticketV5).Ticket, nil - case err := <-resp.err: - return nil, err - } -} - // findnode calls FINDNODE on a node and waits for responses. -func (t *UDPv5) findnode(n *enode.Node, distance int) ([]*enode.Node, error) { - resp := t.call(n, p_nodesV5, &findnodeV5{Distance: uint(distance)}) - return t.waitForNodes(resp, distance) +func (t *UDPv5) findnode(n *enode.Node, distances []uint) ([]*enode.Node, error) { + resp := t.call(n, v5wire.NodesMsg, &v5wire.Findnode{Distances: distances}) + return t.waitForNodes(resp, distances) } // waitForNodes waits for NODES responses to the given call. -func (t *UDPv5) waitForNodes(c *callV5, distance int) ([]*enode.Node, error) { +func (t *UDPv5) waitForNodes(c *callV5, distances []uint) ([]*enode.Node, error) { defer t.callDone(c) var ( @@ -369,11 +374,11 @@ func (t *UDPv5) waitForNodes(c *callV5, distance int) ([]*enode.Node, error) { for { select { case responseP := <-c.ch: - response := responseP.(*nodesV5) + response := responseP.(*v5wire.Nodes) for _, record := range response.Nodes { - node, err := t.verifyResponseNode(c, record, distance, seen) + node, err := t.verifyResponseNode(c, record, distances, seen) if err != nil { - t.log.Debug("Invalid record in "+response.name(), "id", c.node.ID(), "err", err) + t.log.Debug("Invalid record in "+response.Name(), "id", c.node.ID(), "err", err) continue } nodes = append(nodes, node) @@ -391,7 +396,7 @@ func (t *UDPv5) waitForNodes(c *callV5, distance int) ([]*enode.Node, error) { } // verifyResponseNode checks validity of a record in a NODES response. -func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distance int, seen map[enode.ID]struct{}) (*enode.Node, error) { +func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, seen map[enode.ID]struct{}) (*enode.Node, error) { node, err := enode.New(t.validSchemes, r) if err != nil { return nil, err @@ -402,9 +407,10 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distance int, seen if c.node.UDP() <= 1024 { return nil, errLowPort } - if distance != -1 { - if d := enode.LogDist(c.node.ID(), node.ID()); d != distance { - return nil, fmt.Errorf("wrong distance %d", d) + if distances != nil { + nd := enode.LogDist(c.node.ID(), node.ID()) + if !containsUint(uint(nd), distances) { + return nil, errors.New("does not match any requested distance") } } if _, ok := seen[node.ID()]; ok { @@ -414,20 +420,29 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distance int, seen return node, nil } -// call sends the given call and sets up a handler for response packets (of type c.responseType). -// Responses are dispatched to the call's response channel. -func (t *UDPv5) call(node *enode.Node, responseType byte, packet packetV5) *callV5 { +func containsUint(x uint, xs []uint) bool { + for _, v := range xs { + if x == v { + return true + } + } + return false +} + +// call sends the given call and sets up a handler for response packets (of message type +// responseType). Responses are dispatched to the call's response channel. +func (t *UDPv5) call(node *enode.Node, responseType byte, packet v5wire.Packet) *callV5 { c := &callV5{ node: node, packet: packet, responseType: responseType, reqid: make([]byte, 8), - ch: make(chan packetV5, 1), + ch: make(chan v5wire.Packet, 1), err: make(chan error, 1), } // Assign request ID. crand.Read(c.reqid) - packet.setreqid(c.reqid) + packet.SetRequestID(c.reqid) // Send call to dispatch. select { case t.callCh <- c: @@ -482,7 +497,7 @@ func (t *UDPv5) dispatch() { panic("BUG: callDone for inactive call") } c.timeout.Stop() - delete(t.activeCallByAuth, string(c.authTag)) + delete(t.activeCallByAuth, c.nonce) delete(t.activeCallByNode, id) t.sendNextCall(id) @@ -502,7 +517,7 @@ func (t *UDPv5) dispatch() { for id, c := range t.activeCallByNode { c.err <- errClosed delete(t.activeCallByNode, id) - delete(t.activeCallByAuth, string(c.authTag)) + delete(t.activeCallByAuth, c.nonce) } return } @@ -548,38 +563,37 @@ func (t *UDPv5) sendNextCall(id enode.ID) { // sendCall encodes and sends a request packet to the call's recipient node. // This performs a handshake if needed. func (t *UDPv5) sendCall(c *callV5) { - if len(c.authTag) > 0 { - // The call already has an authTag from a previous handshake attempt. Remove the - // entry for the authTag because we're about to generate a new authTag for this - // call. - delete(t.activeCallByAuth, string(c.authTag)) + // The call might have a nonce from a previous handshake attempt. Remove the entry for + // the old nonce because we're about to generate a new nonce for this call. + if c.nonce != (v5wire.Nonce{}) { + delete(t.activeCallByAuth, c.nonce) } addr := &net.UDPAddr{IP: c.node.IP(), Port: c.node.UDP()} - newTag, _ := t.send(c.node.ID(), addr, c.packet, c.challenge) - c.authTag = newTag - t.activeCallByAuth[string(c.authTag)] = c + newNonce, _ := t.send(c.node.ID(), addr, c.packet, c.challenge) + c.nonce = newNonce + t.activeCallByAuth[newNonce] = c t.startResponseTimeout(c) } // sendResponse sends a response packet to the given node. // This doesn't trigger a handshake even if no keys are available. -func (t *UDPv5) sendResponse(toID enode.ID, toAddr *net.UDPAddr, packet packetV5) error { +func (t *UDPv5) sendResponse(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet) error { _, err := t.send(toID, toAddr, packet, nil) return err } // send sends a packet to the given node. -func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet packetV5, c *whoareyouV5) ([]byte, error) { +func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) { addr := toAddr.String() - enc, authTag, err := t.codec.encode(toID, addr, packet, c) + enc, nonce, err := t.codec.Encode(toID, addr, packet, c) if err != nil { - t.log.Warn(">> "+packet.name(), "id", toID, "addr", addr, "err", err) - return authTag, err + t.log.Warn(">> "+packet.Name(), "id", toID, "addr", addr, "err", err) + return nonce, err } _, err = t.conn.WriteToUDP(enc, toAddr) - t.log.Trace(">> "+packet.name(), "id", toID, "addr", addr) - return authTag, err + t.log.Trace(">> "+packet.Name(), "id", toID, "addr", addr) + return nonce, err } // readLoop runs in its own goroutine and reads packets from the network. @@ -617,7 +631,7 @@ func (t *UDPv5) dispatchReadPacket(from *net.UDPAddr, content []byte) bool { // handlePacket decodes and processes an incoming packet from the network. func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error { addr := fromAddr.String() - fromID, fromNode, packet, err := t.codec.decode(rawpacket, addr) + fromID, fromNode, packet, err := t.codec.Decode(rawpacket, addr) if err != nil { t.log.Debug("Bad discv5 packet", "id", fromID, "addr", addr, "err", err) return err @@ -626,31 +640,32 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error { // Handshake succeeded, add to table. t.tab.addSeenNode(wrapNode(fromNode)) } - if packet.kind() != p_whoareyouV5 { - // WHOAREYOU logged separately to report the sender ID. - t.log.Trace("<< "+packet.name(), "id", fromID, "addr", addr) + if packet.Kind() != v5wire.WhoareyouPacket { + // WHOAREYOU logged separately to report errors. + t.log.Trace("<< "+packet.Name(), "id", fromID, "addr", addr) } - packet.handle(t, fromID, fromAddr) + t.handle(packet, fromID, fromAddr) return nil } // handleCallResponse dispatches a response packet to the call waiting for it. -func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr *net.UDPAddr, reqid []byte, p packetV5) { +func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr *net.UDPAddr, p v5wire.Packet) bool { ac := t.activeCallByNode[fromID] - if ac == nil || !bytes.Equal(reqid, ac.reqid) { - t.log.Debug(fmt.Sprintf("Unsolicited/late %s response", p.name()), "id", fromID, "addr", fromAddr) - return + if ac == nil || !bytes.Equal(p.RequestID(), ac.reqid) { + t.log.Debug(fmt.Sprintf("Unsolicited/late %s response", p.Name()), "id", fromID, "addr", fromAddr) + return false } if !fromAddr.IP.Equal(ac.node.IP()) || fromAddr.Port != ac.node.UDP() { - t.log.Debug(fmt.Sprintf("%s from wrong endpoint", p.name()), "id", fromID, "addr", fromAddr) - return + t.log.Debug(fmt.Sprintf("%s from wrong endpoint", p.Name()), "id", fromID, "addr", fromAddr) + return false } - if p.kind() != ac.responseType { - t.log.Debug(fmt.Sprintf("Wrong disv5 response type %s", p.name()), "id", fromID, "addr", fromAddr) - return + if p.Kind() != ac.responseType { + t.log.Debug(fmt.Sprintf("Wrong discv5 response type %s", p.Name()), "id", fromID, "addr", fromAddr) + return false } t.startResponseTimeout(ac) ac.ch <- p + return true } // getNode looks for a node record in table and database. @@ -664,50 +679,65 @@ func (t *UDPv5) getNode(id enode.ID) *enode.Node { return nil } -// UNKNOWN - -func (p *unknownV5) name() string { return "UNKNOWN/v5" } -func (p *unknownV5) kind() byte { return p_unknownV5 } -func (p *unknownV5) setreqid(id []byte) {} +// handle processes incoming packets according to their message type. +func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr) { + switch p := p.(type) { + case *v5wire.Unknown: + t.handleUnknown(p, fromID, fromAddr) + case *v5wire.Whoareyou: + t.handleWhoareyou(p, fromID, fromAddr) + case *v5wire.Ping: + t.handlePing(p, fromID, fromAddr) + case *v5wire.Pong: + if t.handleCallResponse(fromID, fromAddr, p) { + t.localNode.UDPEndpointStatement(fromAddr, &net.UDPAddr{IP: p.ToIP, Port: int(p.ToPort)}) + } + case *v5wire.Findnode: + t.handleFindnode(p, fromID, fromAddr) + case *v5wire.Nodes: + t.handleCallResponse(fromID, fromAddr, p) + case *v5wire.TalkRequest: + t.handleTalkRequest(p, fromID, fromAddr) + case *v5wire.TalkResponse: + t.handleCallResponse(fromID, fromAddr, p) + } +} -func (p *unknownV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - challenge := &whoareyouV5{AuthTag: p.AuthTag} +// handleUnknown initiates a handshake by responding with WHOAREYOU. +func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr *net.UDPAddr) { + challenge := &v5wire.Whoareyou{Nonce: p.Nonce} crand.Read(challenge.IDNonce[:]) if n := t.getNode(fromID); n != nil { - challenge.node = n + challenge.Node = n challenge.RecordSeq = n.Seq() } t.sendResponse(fromID, fromAddr, challenge) } -// WHOAREYOU - -func (p *whoareyouV5) name() string { return "WHOAREYOU/v5" } -func (p *whoareyouV5) kind() byte { return p_whoareyouV5 } -func (p *whoareyouV5) setreqid(id []byte) {} +var ( + errChallengeNoCall = errors.New("no matching call") + errChallengeTwice = errors.New("second handshake") +) -func (p *whoareyouV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - c, err := p.matchWithCall(t, p.AuthTag) +// handleWhoareyou resends the active call as a handshake packet. +func (t *UDPv5) handleWhoareyou(p *v5wire.Whoareyou, fromID enode.ID, fromAddr *net.UDPAddr) { + c, err := t.matchWithCall(fromID, p.Nonce) if err != nil { - t.log.Debug("Invalid WHOAREYOU/v5", "addr", fromAddr, "err", err) + t.log.Debug("Invalid "+p.Name(), "addr", fromAddr, "err", err) return } + // Resend the call that was answered by WHOAREYOU. - t.log.Trace("<< "+p.name(), "id", c.node.ID(), "addr", fromAddr) + t.log.Trace("<< "+p.Name(), "id", c.node.ID(), "addr", fromAddr) c.handshakeCount++ c.challenge = p - p.node = c.node + p.Node = c.node t.sendCall(c) } -var ( - errChallengeNoCall = errors.New("no matching call") - errChallengeTwice = errors.New("second handshake") -) - -// matchWithCall checks whether the handshake attempt matches the active call. -func (p *whoareyouV5) matchWithCall(t *UDPv5, authTag []byte) (*callV5, error) { - c := t.activeCallByAuth[string(authTag)] +// matchWithCall checks whether a handshake attempt matches the active call. +func (t *UDPv5) matchWithCall(fromID enode.ID, nonce v5wire.Nonce) (*callV5, error) { + c := t.activeCallByAuth[nonce] if c == nil { return nil, errChallengeNoCall } @@ -717,14 +747,9 @@ func (p *whoareyouV5) matchWithCall(t *UDPv5, authTag []byte) (*callV5, error) { return c, nil } -// PING - -func (p *pingV5) name() string { return "PING/v5" } -func (p *pingV5) kind() byte { return p_pingV5 } -func (p *pingV5) setreqid(id []byte) { p.ReqID = id } - -func (p *pingV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - t.sendResponse(fromID, fromAddr, &pongV5{ +// handlePing sends a PONG response. +func (t *UDPv5) handlePing(p *v5wire.Ping, fromID enode.ID, fromAddr *net.UDPAddr) { + t.sendResponse(fromID, fromAddr, &v5wire.Pong{ ReqID: p.ReqID, ToIP: fromAddr.IP, ToPort: uint16(fromAddr.Port), @@ -732,121 +757,81 @@ func (p *pingV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { }) } -// PONG - -func (p *pongV5) name() string { return "PONG/v5" } -func (p *pongV5) kind() byte { return p_pongV5 } -func (p *pongV5) setreqid(id []byte) { p.ReqID = id } - -func (p *pongV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - t.localNode.UDPEndpointStatement(fromAddr, &net.UDPAddr{IP: p.ToIP, Port: int(p.ToPort)}) - t.handleCallResponse(fromID, fromAddr, p.ReqID, p) +// handleFindnode returns nodes to the requester. +func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr *net.UDPAddr) { + nodes := t.collectTableNodes(fromAddr.IP, p.Distances, findnodeResultLimit) + for _, resp := range packNodes(p.ReqID, nodes) { + t.sendResponse(fromID, fromAddr, resp) + } } -// FINDNODE +// collectTableNodes creates a FINDNODE result set for the given distances. +func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*enode.Node { + var nodes []*enode.Node + var processed = make(map[uint]struct{}) + for _, dist := range distances { + // Reject duplicate / invalid distances. + _, seen := processed[dist] + if seen || dist > 256 { + continue + } -func (p *findnodeV5) name() string { return "FINDNODE/v5" } -func (p *findnodeV5) kind() byte { return p_findnodeV5 } -func (p *findnodeV5) setreqid(id []byte) { p.ReqID = id } + // Get the nodes. + var bn []*enode.Node + if dist == 0 { + bn = []*enode.Node{t.Self()} + } else if dist <= 256 { + t.tab.mutex.Lock() + bn = unwrapNodes(t.tab.bucketAtDistance(int(dist)).entries) + t.tab.mutex.Unlock() + } + processed[dist] = struct{}{} -func (p *findnodeV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - if p.Distance == 0 { - t.sendNodes(fromID, fromAddr, p.ReqID, []*enode.Node{t.Self()}) - return - } - if p.Distance > 256 { - p.Distance = 256 - } - // Get bucket entries. - t.tab.mutex.Lock() - nodes := unwrapNodes(t.tab.bucketAtDistance(int(p.Distance)).entries) - t.tab.mutex.Unlock() - if len(nodes) > findnodeResultLimit { - nodes = nodes[:findnodeResultLimit] + // Apply some pre-checks to avoid sending invalid nodes. + for _, n := range bn { + // TODO livenessChecks > 1 + if netutil.CheckRelayIP(rip, n.IP()) != nil { + continue + } + nodes = append(nodes, n) + if len(nodes) >= limit { + return nodes + } + } } - t.sendNodes(fromID, fromAddr, p.ReqID, nodes) + return nodes } -// sendNodes sends the given records in one or more NODES packets. -func (t *UDPv5) sendNodes(toID enode.ID, toAddr *net.UDPAddr, reqid []byte, nodes []*enode.Node) { - // TODO livenessChecks > 1 - // TODO CheckRelayIP +// packNodes creates NODES response packets for the given node list. +func packNodes(reqid []byte, nodes []*enode.Node) []*v5wire.Nodes { + if len(nodes) == 0 { + return []*v5wire.Nodes{{ReqID: reqid, Total: 1}} + } + total := uint8(math.Ceil(float64(len(nodes)) / 3)) - resp := &nodesV5{ReqID: reqid, Total: total, Nodes: make([]*enr.Record, 3)} - sent := false + var resp []*v5wire.Nodes for len(nodes) > 0 { + p := &v5wire.Nodes{ReqID: reqid, Total: total} items := min(nodesResponseItemLimit, len(nodes)) - resp.Nodes = resp.Nodes[:items] for i := 0; i < items; i++ { - resp.Nodes[i] = nodes[i].Record() + p.Nodes = append(p.Nodes, nodes[i].Record()) } - t.sendResponse(toID, toAddr, resp) nodes = nodes[items:] - sent = true - } - // Ensure at least one response is sent. - if !sent { - resp.Total = 1 - resp.Nodes = nil - t.sendResponse(toID, toAddr, resp) + resp = append(resp, p) } + return resp } -// NODES +// handleTalkRequest runs the talk request handler of the requested protocol. +func (t *UDPv5) handleTalkRequest(p *v5wire.TalkRequest, fromID enode.ID, fromAddr *net.UDPAddr) { + t.trlock.Lock() + handler := t.trhandlers[p.Protocol] + t.trlock.Unlock() -func (p *nodesV5) name() string { return "NODES/v5" } -func (p *nodesV5) kind() byte { return p_nodesV5 } -func (p *nodesV5) setreqid(id []byte) { p.ReqID = id } - -func (p *nodesV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - t.handleCallResponse(fromID, fromAddr, p.ReqID, p) -} - -// REQUESTTICKET - -func (p *requestTicketV5) name() string { return "REQUESTTICKET/v5" } -func (p *requestTicketV5) kind() byte { return p_requestTicketV5 } -func (p *requestTicketV5) setreqid(id []byte) { p.ReqID = id } - -func (p *requestTicketV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - t.sendResponse(fromID, fromAddr, &ticketV5{ReqID: p.ReqID}) -} - -// TICKET - -func (p *ticketV5) name() string { return "TICKET/v5" } -func (p *ticketV5) kind() byte { return p_ticketV5 } -func (p *ticketV5) setreqid(id []byte) { p.ReqID = id } - -func (p *ticketV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - t.handleCallResponse(fromID, fromAddr, p.ReqID, p) -} - -// REGTOPIC - -func (p *regtopicV5) name() string { return "REGTOPIC/v5" } -func (p *regtopicV5) kind() byte { return p_regtopicV5 } -func (p *regtopicV5) setreqid(id []byte) { p.ReqID = id } - -func (p *regtopicV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - t.sendResponse(fromID, fromAddr, ®confirmationV5{ReqID: p.ReqID, Registered: false}) -} - -// REGCONFIRMATION - -func (p *regconfirmationV5) name() string { return "REGCONFIRMATION/v5" } -func (p *regconfirmationV5) kind() byte { return p_regconfirmationV5 } -func (p *regconfirmationV5) setreqid(id []byte) { p.ReqID = id } - -func (p *regconfirmationV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { - t.handleCallResponse(fromID, fromAddr, p.ReqID, p) -} - -// TOPICQUERY - -func (p *topicqueryV5) name() string { return "TOPICQUERY/v5" } -func (p *topicqueryV5) kind() byte { return p_topicqueryV5 } -func (p *topicqueryV5) setreqid(id []byte) { p.ReqID = id } - -func (p *topicqueryV5) handle(t *UDPv5, fromID enode.ID, fromAddr *net.UDPAddr) { + var response []byte + if handler != nil { + response = handler(p.Message) + } + resp := &v5wire.TalkResponse{ReqID: p.ReqID, Message: response} + t.sendResponse(fromID, fromAddr, resp) } diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 7d3915e2dc..d91a2097db 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -24,22 +24,25 @@ import ( "math/rand" "net" "reflect" + "sort" "testing" "time" "github.com/ethereum/go-ethereum/internal/testlog" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/discover/v5wire" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" ) // Real sockets, real crypto: this test checks end-to-end connectivity for UDPv5. -func TestEndToEndV5(t *testing.T) { +func TestUDPv5_lookupE2E(t *testing.T) { t.Parallel() + const N = 5 var nodes []*UDPv5 - for i := 0; i < 5; i++ { + for i := 0; i < N; i++ { var cfg Config if len(nodes) > 0 { bn := nodes[0].Self() @@ -49,12 +52,22 @@ func TestEndToEndV5(t *testing.T) { nodes = append(nodes, node) defer node.Close() } + last := nodes[N-1] + target := nodes[rand.Intn(N-2)].Self() - last := nodes[len(nodes)-1] - target := nodes[rand.Intn(len(nodes)-2)].Self() + // It is expected that all nodes can be found. + expectedResult := make([]*enode.Node, len(nodes)) + for i := range nodes { + expectedResult[i] = nodes[i].Self() + } + sort.Slice(expectedResult, func(i, j int) bool { + return enode.DistCmp(target.ID(), expectedResult[i].ID(), expectedResult[j].ID()) < 0 + }) + + // Do the lookup. results := last.Lookup(target.ID()) - if len(results) == 0 || results[0].ID() != target.ID() { - t.Fatalf("lookup returned wrong results: %v", results) + if err := checkNodesEqual(results, expectedResult); err != nil { + t.Fatalf("lookup returned wrong results: %v", err) } } @@ -93,8 +106,8 @@ func TestUDPv5_pingHandling(t *testing.T) { test := newUDPV5Test(t) defer test.close() - test.packetIn(&pingV5{ReqID: []byte("foo")}) - test.waitPacketOut(func(p *pongV5, addr *net.UDPAddr, authTag []byte) { + test.packetIn(&v5wire.Ping{ReqID: []byte("foo")}) + test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) { if !bytes.Equal(p.ReqID, []byte("foo")) { t.Error("wrong request ID in response:", p.ReqID) } @@ -110,13 +123,13 @@ func TestUDPv5_unknownPacket(t *testing.T) { test := newUDPV5Test(t) defer test.close() - authTag := [12]byte{1, 2, 3} - check := func(p *whoareyouV5, wantSeq uint64) { + nonce := v5wire.Nonce{1, 2, 3} + check := func(p *v5wire.Whoareyou, wantSeq uint64) { t.Helper() - if !bytes.Equal(p.AuthTag, authTag[:]) { - t.Error("wrong token in WHOAREYOU:", p.AuthTag, authTag[:]) + if p.Nonce != nonce { + t.Error("wrong nonce in WHOAREYOU:", p.Nonce, nonce) } - if p.IDNonce == ([32]byte{}) { + if p.IDNonce == ([16]byte{}) { t.Error("all zero ID nonce") } if p.RecordSeq != wantSeq { @@ -125,8 +138,8 @@ func TestUDPv5_unknownPacket(t *testing.T) { } // Unknown packet from unknown node. - test.packetIn(&unknownV5{AuthTag: authTag[:]}) - test.waitPacketOut(func(p *whoareyouV5, addr *net.UDPAddr, _ []byte) { + test.packetIn(&v5wire.Unknown{Nonce: nonce}) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { check(p, 0) }) @@ -134,8 +147,8 @@ func TestUDPv5_unknownPacket(t *testing.T) { n := test.getNode(test.remotekey, test.remoteaddr).Node() test.table.addSeenNode(wrapNode(n)) - test.packetIn(&unknownV5{AuthTag: authTag[:]}) - test.waitPacketOut(func(p *whoareyouV5, addr *net.UDPAddr, _ []byte) { + test.packetIn(&v5wire.Unknown{Nonce: nonce}) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { check(p, n.Seq()) }) } @@ -147,24 +160,40 @@ func TestUDPv5_findnodeHandling(t *testing.T) { defer test.close() // Create test nodes and insert them into the table. - nodes := nodesAtDistance(test.table.self().ID(), 253, 10) - fillTable(test.table, wrapNodes(nodes)) + nodes253 := nodesAtDistance(test.table.self().ID(), 253, 10) + nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4) + nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10) + fillTable(test.table, wrapNodes(nodes253)) + fillTable(test.table, wrapNodes(nodes249)) + fillTable(test.table, wrapNodes(nodes248)) // Requesting with distance zero should return the node's own record. - test.packetIn(&findnodeV5{ReqID: []byte{0}, Distance: 0}) + test.packetIn(&v5wire.Findnode{ReqID: []byte{0}, Distances: []uint{0}}) test.expectNodes([]byte{0}, 1, []*enode.Node{test.udp.Self()}) - // Requesting with distance > 256 caps it at 256. - test.packetIn(&findnodeV5{ReqID: []byte{1}, Distance: 4234098}) + // Requesting with distance > 256 shouldn't crash. + test.packetIn(&v5wire.Findnode{ReqID: []byte{1}, Distances: []uint{4234098}}) test.expectNodes([]byte{1}, 1, nil) - // This request gets no nodes because the corresponding bucket is empty. - test.packetIn(&findnodeV5{ReqID: []byte{2}, Distance: 254}) + // Requesting with empty distance list shouldn't crash either. + test.packetIn(&v5wire.Findnode{ReqID: []byte{2}, Distances: []uint{}}) test.expectNodes([]byte{2}, 1, nil) - // This request gets all test nodes. - test.packetIn(&findnodeV5{ReqID: []byte{3}, Distance: 253}) - test.expectNodes([]byte{3}, 4, nodes) + // This request gets no nodes because the corresponding bucket is empty. + test.packetIn(&v5wire.Findnode{ReqID: []byte{3}, Distances: []uint{254}}) + test.expectNodes([]byte{3}, 1, nil) + + // This request gets all the distance-253 nodes. + test.packetIn(&v5wire.Findnode{ReqID: []byte{4}, Distances: []uint{253}}) + test.expectNodes([]byte{4}, 4, nodes253) + + // This request gets all the distance-249 nodes and some more at 248 because + // the bucket at 249 is not full. + test.packetIn(&v5wire.Findnode{ReqID: []byte{5}, Distances: []uint{249, 248}}) + var nodes []*enode.Node + nodes = append(nodes, nodes249...) + nodes = append(nodes, nodes248[:10]...) + test.expectNodes([]byte{5}, 5, nodes) } func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes []*enode.Node) { @@ -172,16 +201,17 @@ func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes for _, n := range wantNodes { nodeSet[n.ID()] = n.Record() } + for { - test.waitPacketOut(func(p *nodesV5, addr *net.UDPAddr, authTag []byte) { + test.waitPacketOut(func(p *v5wire.Nodes, addr *net.UDPAddr, _ v5wire.Nonce) { + if !bytes.Equal(p.ReqID, wantReqID) { + test.t.Fatalf("wrong request ID %v in response, want %v", p.ReqID, wantReqID) + } if len(p.Nodes) > 3 { test.t.Fatalf("too many nodes in response") } if p.Total != wantTotal { - test.t.Fatalf("wrong total response count %d", p.Total) - } - if !bytes.Equal(p.ReqID, wantReqID) { - test.t.Fatalf("wrong request ID in response: %v", p.ReqID) + test.t.Fatalf("wrong total response count %d, want %d", p.Total, wantTotal) } for _, record := range p.Nodes { n, _ := enode.New(enode.ValidSchemesForTesting, record) @@ -215,7 +245,7 @@ func TestUDPv5_pingCall(t *testing.T) { _, err := test.udp.ping(remote) done <- err }() - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) {}) + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {}) if err := <-done; err != errTimeout { t.Fatalf("want errTimeout, got %q", err) } @@ -225,8 +255,8 @@ func TestUDPv5_pingCall(t *testing.T) { _, err := test.udp.ping(remote) done <- err }() - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) { - test.packetInFrom(test.remotekey, test.remoteaddr, &pongV5{ReqID: p.ReqID}) + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { + test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.Pong{ReqID: p.ReqID}) }) if err := <-done; err != nil { t.Fatal(err) @@ -237,9 +267,9 @@ func TestUDPv5_pingCall(t *testing.T) { _, err := test.udp.ping(remote) done <- err }() - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) { + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 55, 22}, Port: 10101} - test.packetInFrom(test.remotekey, wrongAddr, &pongV5{ReqID: p.ReqID}) + test.packetInFrom(test.remotekey, wrongAddr, &v5wire.Pong{ReqID: p.ReqID}) }) if err := <-done; err != errTimeout { t.Fatalf("want errTimeout for reply from wrong IP, got %q", err) @@ -255,29 +285,29 @@ func TestUDPv5_findnodeCall(t *testing.T) { // Launch the request: var ( - distance = 230 - remote = test.getNode(test.remotekey, test.remoteaddr).Node() - nodes = nodesAtDistance(remote.ID(), distance, 8) - done = make(chan error, 1) - response []*enode.Node + distances = []uint{230} + remote = test.getNode(test.remotekey, test.remoteaddr).Node() + nodes = nodesAtDistance(remote.ID(), int(distances[0]), 8) + done = make(chan error, 1) + response []*enode.Node ) go func() { var err error - response, err = test.udp.findnode(remote, distance) + response, err = test.udp.findnode(remote, distances) done <- err }() // Serve the responses: - test.waitPacketOut(func(p *findnodeV5, addr *net.UDPAddr, authTag []byte) { - if p.Distance != uint(distance) { - t.Fatalf("wrong bucket: %d", p.Distance) + test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { + if !reflect.DeepEqual(p.Distances, distances) { + t.Fatalf("wrong distances in request: %v", p.Distances) } - test.packetIn(&nodesV5{ + test.packetIn(&v5wire.Nodes{ ReqID: p.ReqID, Total: 2, Nodes: nodesToRecords(nodes[:4]), }) - test.packetIn(&nodesV5{ + test.packetIn(&v5wire.Nodes{ ReqID: p.ReqID, Total: 2, Nodes: nodesToRecords(nodes[4:]), @@ -314,16 +344,16 @@ func TestUDPv5_callResend(t *testing.T) { }() // Ping answered by WHOAREYOU. - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) { - test.packetIn(&whoareyouV5{AuthTag: authTag}) + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { + test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) }) // Ping should be re-sent. - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) { - test.packetIn(&pongV5{ReqID: p.ReqID}) + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { + test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) }) // Answer the other ping. - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) { - test.packetIn(&pongV5{ReqID: p.ReqID}) + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { + test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) }) if err := <-done; err != nil { t.Fatalf("unexpected ping error: %v", err) @@ -347,12 +377,12 @@ func TestUDPv5_multipleHandshakeRounds(t *testing.T) { }() // Ping answered by WHOAREYOU. - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) { - test.packetIn(&whoareyouV5{AuthTag: authTag}) + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { + test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) }) // Ping answered by WHOAREYOU again. - test.waitPacketOut(func(p *pingV5, addr *net.UDPAddr, authTag []byte) { - test.packetIn(&whoareyouV5{AuthTag: authTag}) + test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { + test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) }) if err := <-done; err != errTimeout { t.Fatalf("unexpected ping error: %q", err) @@ -367,27 +397,27 @@ func TestUDPv5_callTimeoutReset(t *testing.T) { // Launch the request: var ( - distance = 230 + distance = uint(230) remote = test.getNode(test.remotekey, test.remoteaddr).Node() - nodes = nodesAtDistance(remote.ID(), distance, 8) + nodes = nodesAtDistance(remote.ID(), int(distance), 8) done = make(chan error, 1) ) go func() { - _, err := test.udp.findnode(remote, distance) + _, err := test.udp.findnode(remote, []uint{distance}) done <- err }() // Serve two responses, slowly. - test.waitPacketOut(func(p *findnodeV5, addr *net.UDPAddr, authTag []byte) { + test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { time.Sleep(respTimeout - 50*time.Millisecond) - test.packetIn(&nodesV5{ + test.packetIn(&v5wire.Nodes{ ReqID: p.ReqID, Total: 2, Nodes: nodesToRecords(nodes[:4]), }) time.Sleep(respTimeout - 50*time.Millisecond) - test.packetIn(&nodesV5{ + test.packetIn(&v5wire.Nodes{ ReqID: p.ReqID, Total: 2, Nodes: nodesToRecords(nodes[4:]), @@ -398,6 +428,97 @@ func TestUDPv5_callTimeoutReset(t *testing.T) { } } +// This test checks that TALKREQ calls the registered handler function. +func TestUDPv5_talkHandling(t *testing.T) { + t.Parallel() + test := newUDPV5Test(t) + defer test.close() + + var recvMessage []byte + test.udp.RegisterTalkHandler("test", func(message []byte) []byte { + recvMessage = message + return []byte("test response") + }) + + // Successful case: + test.packetIn(&v5wire.TalkRequest{ + ReqID: []byte("foo"), + Protocol: "test", + Message: []byte("test request"), + }) + test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { + if !bytes.Equal(p.ReqID, []byte("foo")) { + t.Error("wrong request ID in response:", p.ReqID) + } + if string(p.Message) != "test response" { + t.Errorf("wrong talk response message: %q", p.Message) + } + if string(recvMessage) != "test request" { + t.Errorf("wrong message received in handler: %q", recvMessage) + } + }) + + // Check that empty response is returned for unregistered protocols. + recvMessage = nil + test.packetIn(&v5wire.TalkRequest{ + ReqID: []byte("2"), + Protocol: "wrong", + Message: []byte("test request"), + }) + test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { + if !bytes.Equal(p.ReqID, []byte("2")) { + t.Error("wrong request ID in response:", p.ReqID) + } + if string(p.Message) != "" { + t.Errorf("wrong talk response message: %q", p.Message) + } + if recvMessage != nil { + t.Errorf("handler was called for wrong protocol: %q", recvMessage) + } + }) +} + +// This test checks that outgoing TALKREQ calls work. +func TestUDPv5_talkRequest(t *testing.T) { + t.Parallel() + test := newUDPV5Test(t) + defer test.close() + + remote := test.getNode(test.remotekey, test.remoteaddr).Node() + done := make(chan error, 1) + + // This request times out. + go func() { + _, err := test.udp.TalkRequest(remote, "test", []byte("test request")) + done <- err + }() + test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) {}) + if err := <-done; err != errTimeout { + t.Fatalf("want errTimeout, got %q", err) + } + + // This request works. + go func() { + _, err := test.udp.TalkRequest(remote, "test", []byte("test request")) + done <- err + }() + test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) { + if p.Protocol != "test" { + t.Errorf("wrong protocol ID in talk request: %q", p.Protocol) + } + if string(p.Message) != "test request" { + t.Errorf("wrong message talk request: %q", p.Message) + } + test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.TalkResponse{ + ReqID: p.ReqID, + Message: []byte("test response"), + }) + }) + if err := <-done; err != nil { + t.Fatal(err) + } +} + // This test checks that lookup works. func TestUDPv5_lookup(t *testing.T) { t.Parallel() @@ -417,7 +538,8 @@ func TestUDPv5_lookup(t *testing.T) { } // Seed table with initial node. - fillTable(test.table, []*node{wrapNode(lookupTestnet.node(256, 0))}) + initialNode := lookupTestnet.node(256, 0) + fillTable(test.table, []*node{wrapNode(initialNode)}) // Start the lookup. resultC := make(chan []*enode.Node, 1) @@ -427,22 +549,30 @@ func TestUDPv5_lookup(t *testing.T) { }() // Answer lookup packets. + asked := make(map[enode.ID]bool) for done := false; !done; { - done = test.waitPacketOut(func(p packetV5, to *net.UDPAddr, authTag []byte) { + done = test.waitPacketOut(func(p v5wire.Packet, to *net.UDPAddr, _ v5wire.Nonce) { recipient, key := lookupTestnet.nodeByAddr(to) switch p := p.(type) { - case *pingV5: - test.packetInFrom(key, to, &pongV5{ReqID: p.ReqID}) - case *findnodeV5: - nodes := lookupTestnet.neighborsAtDistance(recipient, p.Distance, 3) - response := &nodesV5{ReqID: p.ReqID, Total: 1, Nodes: nodesToRecords(nodes)} - test.packetInFrom(key, to, response) + case *v5wire.Ping: + test.packetInFrom(key, to, &v5wire.Pong{ReqID: p.ReqID}) + case *v5wire.Findnode: + if asked[recipient.ID()] { + t.Error("Asked node", recipient.ID(), "twice") + } + asked[recipient.ID()] = true + nodes := lookupTestnet.neighborsAtDistances(recipient, p.Distances, 16) + t.Logf("Got FINDNODE for %v, returning %d nodes", p.Distances, len(nodes)) + for _, resp := range packNodes(p.ReqID, nodes) { + test.packetInFrom(key, to, resp) + } } }) } // Verify result nodes. - checkLookupResults(t, lookupTestnet, <-resultC) + results := <-resultC + checkLookupResults(t, lookupTestnet, results) } // This test checks the local node can be utilised to set key-values. @@ -481,6 +611,7 @@ type udpV5Test struct { nodesByIP map[string]*enode.LocalNode } +// testCodec is the packet encoding used by protocol tests. This codec does not perform encryption. type testCodec struct { test *udpV5Test id enode.ID @@ -489,46 +620,44 @@ type testCodec struct { type testCodecFrame struct { NodeID enode.ID - AuthTag []byte + AuthTag v5wire.Nonce Ptype byte Packet rlp.RawValue } -func (c *testCodec) encode(toID enode.ID, addr string, p packetV5, _ *whoareyouV5) ([]byte, []byte, error) { +func (c *testCodec) Encode(toID enode.ID, addr string, p v5wire.Packet, _ *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error) { c.ctr++ - authTag := make([]byte, 8) - binary.BigEndian.PutUint64(authTag, c.ctr) + var authTag v5wire.Nonce + binary.BigEndian.PutUint64(authTag[:], c.ctr) + penc, _ := rlp.EncodeToBytes(p) - frame, err := rlp.EncodeToBytes(testCodecFrame{c.id, authTag, p.kind(), penc}) + frame, err := rlp.EncodeToBytes(testCodecFrame{c.id, authTag, p.Kind(), penc}) return frame, authTag, err } -func (c *testCodec) decode(input []byte, addr string) (enode.ID, *enode.Node, packetV5, error) { +func (c *testCodec) Decode(input []byte, addr string) (enode.ID, *enode.Node, v5wire.Packet, error) { frame, p, err := c.decodeFrame(input) if err != nil { return enode.ID{}, nil, nil, err } - if p.kind() == p_whoareyouV5 { - frame.NodeID = enode.ID{} // match wireCodec behavior - } return frame.NodeID, nil, p, nil } -func (c *testCodec) decodeFrame(input []byte) (frame testCodecFrame, p packetV5, err error) { +func (c *testCodec) decodeFrame(input []byte) (frame testCodecFrame, p v5wire.Packet, err error) { if err = rlp.DecodeBytes(input, &frame); err != nil { return frame, nil, fmt.Errorf("invalid frame: %v", err) } switch frame.Ptype { - case p_unknownV5: - dec := new(unknownV5) + case v5wire.UnknownPacket: + dec := new(v5wire.Unknown) err = rlp.DecodeBytes(frame.Packet, &dec) p = dec - case p_whoareyouV5: - dec := new(whoareyouV5) + case v5wire.WhoareyouPacket: + dec := new(v5wire.Whoareyou) err = rlp.DecodeBytes(frame.Packet, &dec) p = dec default: - p, err = decodePacketBodyV5(frame.Ptype, frame.Packet) + p, err = v5wire.DecodeMessage(frame.Ptype, frame.Packet) } return frame, p, err } @@ -561,20 +690,20 @@ func newUDPV5Test(t *testing.T) *udpV5Test { } // handles a packet as if it had been sent to the transport. -func (test *udpV5Test) packetIn(packet packetV5) { +func (test *udpV5Test) packetIn(packet v5wire.Packet) { test.t.Helper() test.packetInFrom(test.remotekey, test.remoteaddr, packet) } // handles a packet as if it had been sent to the transport by the key/endpoint. -func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, packet packetV5) { +func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, packet v5wire.Packet) { test.t.Helper() ln := test.getNode(key, addr) codec := &testCodec{test: test, id: ln.ID()} - enc, _, err := codec.encode(test.udp.Self().ID(), addr.String(), packet, nil) + enc, _, err := codec.Encode(test.udp.Self().ID(), addr.String(), packet, nil) if err != nil { - test.t.Errorf("%s encode error: %v", packet.name(), err) + test.t.Errorf("%s encode error: %v", packet.Name(), err) } if test.udp.dispatchReadPacket(addr, enc) { <-test.udp.readNextCh // unblock UDPv5.dispatch @@ -596,8 +725,12 @@ func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr *net.UDPAddr) *enode. return ln } +// waitPacketOut waits for the next output packet and handles it using the given 'validate' +// function. The function must be of type func (X, *net.UDPAddr, v5wire.Nonce) where X is +// assignable to packetV5. func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) { test.t.Helper() + fn := reflect.ValueOf(validate) exptype := fn.Type().In(0) diff --git a/p2p/discover/v5wire/crypto.go b/p2p/discover/v5wire/crypto.go new file mode 100644 index 0000000000..fc0a0edef5 --- /dev/null +++ b/p2p/discover/v5wire/crypto.go @@ -0,0 +1,180 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package v5wire + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/elliptic" + "errors" + "fmt" + "hash" + + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/enode" + "golang.org/x/crypto/hkdf" +) + +const ( + // Encryption/authentication parameters. + aesKeySize = 16 + gcmNonceSize = 12 +) + +// Nonce represents a nonce used for AES/GCM. +type Nonce [gcmNonceSize]byte + +// EncodePubkey encodes a public key. +func EncodePubkey(key *ecdsa.PublicKey) []byte { + switch key.Curve { + case crypto.S256(): + return crypto.CompressPubkey(key) + default: + panic("unsupported curve " + key.Curve.Params().Name + " in EncodePubkey") + } +} + +// DecodePubkey decodes a public key in compressed format. +func DecodePubkey(curve elliptic.Curve, e []byte) (*ecdsa.PublicKey, error) { + switch curve { + case crypto.S256(): + if len(e) != 33 { + return nil, errors.New("wrong size public key data") + } + return crypto.DecompressPubkey(e) + default: + return nil, fmt.Errorf("unsupported curve %s in DecodePubkey", curve.Params().Name) + } +} + +// idNonceHash computes the ID signature hash used in the handshake. +func idNonceHash(h hash.Hash, challenge, ephkey []byte, destID enode.ID) []byte { + h.Reset() + h.Write([]byte("discovery v5 identity proof")) + h.Write(challenge) + h.Write(ephkey) + h.Write(destID[:]) + return h.Sum(nil) +} + +// makeIDSignature creates the ID nonce signature. +func makeIDSignature(hash hash.Hash, key *ecdsa.PrivateKey, challenge, ephkey []byte, destID enode.ID) ([]byte, error) { + input := idNonceHash(hash, challenge, ephkey, destID) + switch key.Curve { + case crypto.S256(): + idsig, err := crypto.Sign(input, key) + if err != nil { + return nil, err + } + return idsig[:len(idsig)-1], nil // remove recovery ID + default: + return nil, fmt.Errorf("unsupported curve %s", key.Curve.Params().Name) + } +} + +// s256raw is an unparsed secp256k1 public key ENR entry. +type s256raw []byte + +func (s256raw) ENRKey() string { return "secp256k1" } + +// verifyIDSignature checks that signature over idnonce was made by the given node. +func verifyIDSignature(hash hash.Hash, sig []byte, n *enode.Node, challenge, ephkey []byte, destID enode.ID) error { + switch idscheme := n.Record().IdentityScheme(); idscheme { + case "v4": + var pubkey s256raw + if n.Load(&pubkey) != nil { + return errors.New("no secp256k1 public key in record") + } + input := idNonceHash(hash, challenge, ephkey, destID) + if !crypto.VerifySignature(pubkey, input, sig) { + return errInvalidNonceSig + } + return nil + default: + return fmt.Errorf("can't verify ID nonce signature against scheme %q", idscheme) + } +} + +type hashFn func() hash.Hash + +// deriveKeys creates the session keys. +func deriveKeys(hash hashFn, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, n1, n2 enode.ID, challenge []byte) *session { + const text = "discovery v5 key agreement" + var info = make([]byte, 0, len(text)+len(n1)+len(n2)) + info = append(info, text...) + info = append(info, n1[:]...) + info = append(info, n2[:]...) + + eph := ecdh(priv, pub) + if eph == nil { + return nil + } + kdf := hkdf.New(hash, eph, challenge, info) + sec := session{writeKey: make([]byte, aesKeySize), readKey: make([]byte, aesKeySize)} + kdf.Read(sec.writeKey) + kdf.Read(sec.readKey) + for i := range eph { + eph[i] = 0 + } + return &sec +} + +// ecdh creates a shared secret. +func ecdh(privkey *ecdsa.PrivateKey, pubkey *ecdsa.PublicKey) []byte { + secX, secY := pubkey.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) + if secX == nil { + return nil + } + sec := make([]byte, 33) + sec[0] = 0x02 | byte(secY.Bit(0)) + math.ReadBits(secX, sec[1:]) + return sec +} + +// encryptGCM encrypts pt using AES-GCM with the given key and nonce. The ciphertext is +// appended to dest, which must not overlap with plaintext. The resulting ciphertext is 16 +// bytes longer than plaintext because it contains an authentication tag. +func encryptGCM(dest, key, nonce, plaintext, authData []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + panic(fmt.Errorf("can't create block cipher: %v", err)) + } + aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize) + if err != nil { + panic(fmt.Errorf("can't create GCM: %v", err)) + } + return aesgcm.Seal(dest, nonce, plaintext, authData), nil +} + +// decryptGCM decrypts ct using AES-GCM with the given key and nonce. +func decryptGCM(key, nonce, ct, authData []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("can't create block cipher: %v", err) + } + if len(nonce) != gcmNonceSize { + return nil, fmt.Errorf("invalid GCM nonce size: %d", len(nonce)) + } + aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize) + if err != nil { + return nil, fmt.Errorf("can't create GCM: %v", err) + } + pt := make([]byte, 0, len(ct)) + return aesgcm.Open(pt, nonce, ct, authData) +} diff --git a/p2p/discover/v5wire/crypto_test.go b/p2p/discover/v5wire/crypto_test.go new file mode 100644 index 0000000000..72169b4314 --- /dev/null +++ b/p2p/discover/v5wire/crypto_test.go @@ -0,0 +1,124 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package v5wire + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "reflect" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +func TestVector_ECDH(t *testing.T) { + var ( + staticKey = hexPrivkey("0xfb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") + publicKey = hexPubkey(crypto.S256(), "0x039961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231") + want = hexutil.MustDecode("0x033b11a2a1f214567e1537ce5e509ffd9b21373247f2a3ff6841f4976f53165e7e") + ) + result := ecdh(staticKey, publicKey) + check(t, "shared-secret", result, want) +} + +func TestVector_KDF(t *testing.T) { + var ( + ephKey = hexPrivkey("0xfb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") + cdata = hexutil.MustDecode("0x000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000") + net = newHandshakeTest() + ) + defer net.close() + + destKey := &testKeyB.PublicKey + s := deriveKeys(sha256.New, ephKey, destKey, net.nodeA.id(), net.nodeB.id(), cdata) + t.Logf("ephemeral-key = %#x", ephKey.D) + t.Logf("dest-pubkey = %#x", EncodePubkey(destKey)) + t.Logf("node-id-a = %#x", net.nodeA.id().Bytes()) + t.Logf("node-id-b = %#x", net.nodeB.id().Bytes()) + t.Logf("challenge-data = %#x", cdata) + check(t, "initiator-key", s.writeKey, hexutil.MustDecode("0xdccc82d81bd610f4f76d3ebe97a40571")) + check(t, "recipient-key", s.readKey, hexutil.MustDecode("0xac74bb8773749920b0d3a8881c173ec5")) +} + +func TestVector_IDSignature(t *testing.T) { + var ( + key = hexPrivkey("0xfb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") + destID = enode.HexID("0xbbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9") + ephkey = hexutil.MustDecode("0x039961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231") + cdata = hexutil.MustDecode("0x000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000") + ) + + sig, err := makeIDSignature(sha256.New(), key, cdata, ephkey, destID) + if err != nil { + t.Fatal(err) + } + t.Logf("static-key = %#x", key.D) + t.Logf("challenge-data = %#x", cdata) + t.Logf("ephemeral-pubkey = %#x", ephkey) + t.Logf("node-id-B = %#x", destID.Bytes()) + expected := "0x94852a1e2318c4e5e9d422c98eaf19d1d90d876b29cd06ca7cb7546d0fff7b484fe86c09a064fe72bdbef73ba8e9c34df0cd2b53e9d65528c2c7f336d5dfc6e6" + check(t, "id-signature", sig, hexutil.MustDecode(expected)) +} + +func TestDeriveKeys(t *testing.T) { + t.Parallel() + + var ( + n1 = enode.ID{1} + n2 = enode.ID{2} + cdata = []byte{1, 2, 3, 4} + ) + sec1 := deriveKeys(sha256.New, testKeyA, &testKeyB.PublicKey, n1, n2, cdata) + sec2 := deriveKeys(sha256.New, testKeyB, &testKeyA.PublicKey, n1, n2, cdata) + if sec1 == nil || sec2 == nil { + t.Fatal("key agreement failed") + } + if !reflect.DeepEqual(sec1, sec2) { + t.Fatalf("keys not equal:\n %+v\n %+v", sec1, sec2) + } +} + +func check(t *testing.T, what string, x, y []byte) { + t.Helper() + + if !bytes.Equal(x, y) { + t.Errorf("wrong %s: %#x != %#x", what, x, y) + } else { + t.Logf("%s = %#x", what, x) + } +} + +func hexPrivkey(input string) *ecdsa.PrivateKey { + key, err := crypto.HexToECDSA(strings.TrimPrefix(input, "0x")) + if err != nil { + panic(err) + } + return key +} + +func hexPubkey(curve elliptic.Curve, input string) *ecdsa.PublicKey { + key, err := DecodePubkey(curve, hexutil.MustDecode(input)) + if err != nil { + panic(err) + } + return key +} diff --git a/p2p/discover/v5wire/encoding.go b/p2p/discover/v5wire/encoding.go new file mode 100644 index 0000000000..f502339e1e --- /dev/null +++ b/p2p/discover/v5wire/encoding.go @@ -0,0 +1,648 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package v5wire + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + crand "crypto/rand" + "crypto/sha256" + "encoding/binary" + "errors" + "fmt" + "hash" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/rlp" +) + +// TODO concurrent WHOAREYOU tie-breaker +// TODO rehandshake after X packets + +// Header represents a packet header. +type Header struct { + IV [sizeofMaskingIV]byte + StaticHeader + AuthData []byte + + src enode.ID // used by decoder +} + +// StaticHeader contains the static fields of a packet header. +type StaticHeader struct { + ProtocolID [6]byte + Version uint16 + Flag byte + Nonce Nonce + AuthSize uint16 +} + +// Authdata layouts. +type ( + whoareyouAuthData struct { + IDNonce [16]byte // ID proof data + RecordSeq uint64 // highest known ENR sequence of requester + } + + handshakeAuthData struct { + h struct { + SrcID enode.ID + SigSize byte // ignature data + PubkeySize byte // offset of + } + // Trailing variable-size data. + signature, pubkey, record []byte + } + + messageAuthData struct { + SrcID enode.ID + } +) + +// Packet header flag values. +const ( + flagMessage = iota + flagWhoareyou + flagHandshake +) + +// Protocol constants. +const ( + version = 1 + minVersion = 1 + sizeofMaskingIV = 16 + + minMessageSize = 48 // this refers to data after static headers + randomPacketMsgSize = 20 +) + +var protocolID = [6]byte{'d', 'i', 's', 'c', 'v', '5'} + +// Errors. +var ( + errTooShort = errors.New("packet too short") + errInvalidHeader = errors.New("invalid packet header") + errInvalidFlag = errors.New("invalid flag value in header") + errMinVersion = errors.New("version of packet header below minimum") + errMsgTooShort = errors.New("message/handshake packet below minimum size") + errAuthSize = errors.New("declared auth size is beyond packet length") + errUnexpectedHandshake = errors.New("unexpected auth response, not in handshake") + errInvalidAuthKey = errors.New("invalid ephemeral pubkey") + errNoRecord = errors.New("expected ENR in handshake but none sent") + errInvalidNonceSig = errors.New("invalid ID nonce signature") + errMessageTooShort = errors.New("message contains no data") + errMessageDecrypt = errors.New("cannot decrypt message") +) + +// Public errors. +var ( + ErrInvalidReqID = errors.New("request ID larger than 8 bytes") +) + +// Packet sizes. +var ( + sizeofStaticHeader = binary.Size(StaticHeader{}) + sizeofWhoareyouAuthData = binary.Size(whoareyouAuthData{}) + sizeofHandshakeAuthData = binary.Size(handshakeAuthData{}.h) + sizeofMessageAuthData = binary.Size(messageAuthData{}) + sizeofStaticPacketData = sizeofMaskingIV + sizeofStaticHeader +) + +// Codec encodes and decodes Discovery v5 packets. +// This type is not safe for concurrent use. +type Codec struct { + sha256 hash.Hash + localnode *enode.LocalNode + privkey *ecdsa.PrivateKey + sc *SessionCache + + // encoder buffers + buf bytes.Buffer // whole packet + headbuf bytes.Buffer // packet header + msgbuf bytes.Buffer // message RLP plaintext + msgctbuf []byte // message data ciphertext + + // decoder buffer + reader bytes.Reader +} + +// NewCodec creates a wire codec. +func NewCodec(ln *enode.LocalNode, key *ecdsa.PrivateKey, clock mclock.Clock) *Codec { + c := &Codec{ + sha256: sha256.New(), + localnode: ln, + privkey: key, + sc: NewSessionCache(1024, clock), + } + return c +} + +// Encode encodes a packet to a node. 'id' and 'addr' specify the destination node. The +// 'challenge' parameter should be the most recently received WHOAREYOU packet from that +// node. +func (c *Codec) Encode(id enode.ID, addr string, packet Packet, challenge *Whoareyou) ([]byte, Nonce, error) { + // Create the packet header. + var ( + head Header + session *session + msgData []byte + err error + ) + switch { + case packet.Kind() == WhoareyouPacket: + head, err = c.encodeWhoareyou(id, packet.(*Whoareyou)) + case challenge != nil: + // We have an unanswered challenge, send handshake. + head, session, err = c.encodeHandshakeHeader(id, addr, challenge) + default: + session = c.sc.session(id, addr) + if session != nil { + // There is a session, use it. + head, err = c.encodeMessageHeader(id, session) + } else { + // No keys, send random data to kick off the handshake. + head, msgData, err = c.encodeRandom(id) + } + } + if err != nil { + return nil, Nonce{}, err + } + + // Generate masking IV. + if err := c.sc.maskingIVGen(head.IV[:]); err != nil { + return nil, Nonce{}, fmt.Errorf("can't generate masking IV: %v", err) + } + + // Encode header data. + c.writeHeaders(&head) + + // Store sent WHOAREYOU challenges. + if challenge, ok := packet.(*Whoareyou); ok { + challenge.ChallengeData = bytesCopy(&c.buf) + c.sc.storeSentHandshake(id, addr, challenge) + } else if msgData == nil { + headerData := c.buf.Bytes() + msgData, err = c.encryptMessage(session, packet, &head, headerData) + if err != nil { + return nil, Nonce{}, err + } + } + + enc, err := c.EncodeRaw(id, head, msgData) + return enc, head.Nonce, err +} + +// EncodeRaw encodes a packet with the given header. +func (c *Codec) EncodeRaw(id enode.ID, head Header, msgdata []byte) ([]byte, error) { + c.writeHeaders(&head) + + // Apply masking. + masked := c.buf.Bytes()[sizeofMaskingIV:] + mask := head.mask(id) + mask.XORKeyStream(masked[:], masked[:]) + + // Write message data. + c.buf.Write(msgdata) + return c.buf.Bytes(), nil +} + +func (c *Codec) writeHeaders(head *Header) { + c.buf.Reset() + c.buf.Write(head.IV[:]) + binary.Write(&c.buf, binary.BigEndian, &head.StaticHeader) + c.buf.Write(head.AuthData) +} + +// makeHeader creates a packet header. +func (c *Codec) makeHeader(toID enode.ID, flag byte, authsizeExtra int) Header { + var authsize int + switch flag { + case flagMessage: + authsize = sizeofMessageAuthData + case flagWhoareyou: + authsize = sizeofWhoareyouAuthData + case flagHandshake: + authsize = sizeofHandshakeAuthData + default: + panic(fmt.Errorf("BUG: invalid packet header flag %x", flag)) + } + authsize += authsizeExtra + if authsize > int(^uint16(0)) { + panic(fmt.Errorf("BUG: auth size %d overflows uint16", authsize)) + } + return Header{ + StaticHeader: StaticHeader{ + ProtocolID: protocolID, + Version: version, + Flag: flag, + AuthSize: uint16(authsize), + }, + } +} + +// encodeRandom encodes a packet with random content. +func (c *Codec) encodeRandom(toID enode.ID) (Header, []byte, error) { + head := c.makeHeader(toID, flagMessage, 0) + + // Encode auth data. + auth := messageAuthData{SrcID: c.localnode.ID()} + if _, err := crand.Read(head.Nonce[:]); err != nil { + return head, nil, fmt.Errorf("can't get random data: %v", err) + } + c.headbuf.Reset() + binary.Write(&c.headbuf, binary.BigEndian, auth) + head.AuthData = c.headbuf.Bytes() + + // Fill message ciphertext buffer with random bytes. + c.msgctbuf = append(c.msgctbuf[:0], make([]byte, randomPacketMsgSize)...) + crand.Read(c.msgctbuf) + return head, c.msgctbuf, nil +} + +// encodeWhoareyou encodes a WHOAREYOU packet. +func (c *Codec) encodeWhoareyou(toID enode.ID, packet *Whoareyou) (Header, error) { + // Sanity check node field to catch misbehaving callers. + if packet.RecordSeq > 0 && packet.Node == nil { + panic("BUG: missing node in whoareyou with non-zero seq") + } + + // Create header. + head := c.makeHeader(toID, flagWhoareyou, 0) + head.AuthData = bytesCopy(&c.buf) + head.Nonce = packet.Nonce + + // Encode auth data. + auth := &whoareyouAuthData{ + IDNonce: packet.IDNonce, + RecordSeq: packet.RecordSeq, + } + c.headbuf.Reset() + binary.Write(&c.headbuf, binary.BigEndian, auth) + head.AuthData = c.headbuf.Bytes() + return head, nil +} + +// encodeHandshakeMessage encodes the handshake message packet header. +func (c *Codec) encodeHandshakeHeader(toID enode.ID, addr string, challenge *Whoareyou) (Header, *session, error) { + // Ensure calling code sets challenge.node. + if challenge.Node == nil { + panic("BUG: missing challenge.Node in encode") + } + + // Generate new secrets. + auth, session, err := c.makeHandshakeAuth(toID, addr, challenge) + if err != nil { + return Header{}, nil, err + } + + // Generate nonce for message. + nonce, err := c.sc.nextNonce(session) + if err != nil { + return Header{}, nil, fmt.Errorf("can't generate nonce: %v", err) + } + + // TODO: this should happen when the first authenticated message is received + c.sc.storeNewSession(toID, addr, session) + + // Encode the auth header. + var ( + authsizeExtra = len(auth.pubkey) + len(auth.signature) + len(auth.record) + head = c.makeHeader(toID, flagHandshake, authsizeExtra) + ) + c.headbuf.Reset() + binary.Write(&c.headbuf, binary.BigEndian, &auth.h) + c.headbuf.Write(auth.signature) + c.headbuf.Write(auth.pubkey) + c.headbuf.Write(auth.record) + head.AuthData = c.headbuf.Bytes() + head.Nonce = nonce + return head, session, err +} + +// encodeAuthHeader creates the auth header on a request packet following WHOAREYOU. +func (c *Codec) makeHandshakeAuth(toID enode.ID, addr string, challenge *Whoareyou) (*handshakeAuthData, *session, error) { + auth := new(handshakeAuthData) + auth.h.SrcID = c.localnode.ID() + + // Create the ephemeral key. This needs to be first because the + // key is part of the ID nonce signature. + var remotePubkey = new(ecdsa.PublicKey) + if err := challenge.Node.Load((*enode.Secp256k1)(remotePubkey)); err != nil { + return nil, nil, fmt.Errorf("can't find secp256k1 key for recipient") + } + ephkey, err := c.sc.ephemeralKeyGen() + if err != nil { + return nil, nil, fmt.Errorf("can't generate ephemeral key") + } + ephpubkey := EncodePubkey(&ephkey.PublicKey) + auth.pubkey = ephpubkey[:] + auth.h.PubkeySize = byte(len(auth.pubkey)) + + // Add ID nonce signature to response. + cdata := challenge.ChallengeData + idsig, err := makeIDSignature(c.sha256, c.privkey, cdata, ephpubkey[:], toID) + if err != nil { + return nil, nil, fmt.Errorf("can't sign: %v", err) + } + auth.signature = idsig + auth.h.SigSize = byte(len(auth.signature)) + + // Add our record to response if it's newer than what remote side has. + ln := c.localnode.Node() + if challenge.RecordSeq < ln.Seq() { + auth.record, _ = rlp.EncodeToBytes(ln.Record()) + } + + // Create session keys. + sec := deriveKeys(sha256.New, ephkey, remotePubkey, c.localnode.ID(), challenge.Node.ID(), cdata) + if sec == nil { + return nil, nil, fmt.Errorf("key derivation failed") + } + return auth, sec, err +} + +// encodeMessage encodes an encrypted message packet. +func (c *Codec) encodeMessageHeader(toID enode.ID, s *session) (Header, error) { + head := c.makeHeader(toID, flagMessage, 0) + + // Create the header. + nonce, err := c.sc.nextNonce(s) + if err != nil { + return Header{}, fmt.Errorf("can't generate nonce: %v", err) + } + auth := messageAuthData{SrcID: c.localnode.ID()} + c.buf.Reset() + binary.Write(&c.buf, binary.BigEndian, &auth) + head.AuthData = bytesCopy(&c.buf) + head.Nonce = nonce + return head, err +} + +func (c *Codec) encryptMessage(s *session, p Packet, head *Header, headerData []byte) ([]byte, error) { + // Encode message plaintext. + c.msgbuf.Reset() + c.msgbuf.WriteByte(p.Kind()) + if err := rlp.Encode(&c.msgbuf, p); err != nil { + return nil, err + } + messagePT := c.msgbuf.Bytes() + + // Encrypt into message ciphertext buffer. + messageCT, err := encryptGCM(c.msgctbuf[:0], s.writeKey, head.Nonce[:], messagePT, headerData) + if err == nil { + c.msgctbuf = messageCT + } + return messageCT, err +} + +// Decode decodes a discovery packet. +func (c *Codec) Decode(input []byte, addr string) (src enode.ID, n *enode.Node, p Packet, err error) { + // Unmask the static header. + if len(input) < sizeofStaticPacketData { + return enode.ID{}, nil, nil, errTooShort + } + var head Header + copy(head.IV[:], input[:sizeofMaskingIV]) + mask := head.mask(c.localnode.ID()) + staticHeader := input[sizeofMaskingIV:sizeofStaticPacketData] + mask.XORKeyStream(staticHeader, staticHeader) + + // Decode and verify the static header. + c.reader.Reset(staticHeader) + binary.Read(&c.reader, binary.BigEndian, &head.StaticHeader) + remainingInput := len(input) - sizeofStaticPacketData + if err := head.checkValid(remainingInput); err != nil { + return enode.ID{}, nil, nil, err + } + + // Unmask auth data. + authDataEnd := sizeofStaticPacketData + int(head.AuthSize) + authData := input[sizeofStaticPacketData:authDataEnd] + mask.XORKeyStream(authData, authData) + head.AuthData = authData + + // Delete timed-out handshakes. This must happen before decoding to avoid + // processing the same handshake twice. + c.sc.handshakeGC() + + // Decode auth part and message. + headerData := input[:authDataEnd] + msgData := input[authDataEnd:] + switch head.Flag { + case flagWhoareyou: + p, err = c.decodeWhoareyou(&head, headerData) + case flagHandshake: + n, p, err = c.decodeHandshakeMessage(addr, &head, headerData, msgData) + case flagMessage: + p, err = c.decodeMessage(addr, &head, headerData, msgData) + default: + err = errInvalidFlag + } + return head.src, n, p, err +} + +// decodeWhoareyou reads packet data after the header as a WHOAREYOU packet. +func (c *Codec) decodeWhoareyou(head *Header, headerData []byte) (Packet, error) { + if len(head.AuthData) != sizeofWhoareyouAuthData { + return nil, fmt.Errorf("invalid auth size %d for WHOAREYOU", len(head.AuthData)) + } + var auth whoareyouAuthData + c.reader.Reset(head.AuthData) + binary.Read(&c.reader, binary.BigEndian, &auth) + p := &Whoareyou{ + Nonce: head.Nonce, + IDNonce: auth.IDNonce, + RecordSeq: auth.RecordSeq, + ChallengeData: make([]byte, len(headerData)), + } + copy(p.ChallengeData, headerData) + return p, nil +} + +func (c *Codec) decodeHandshakeMessage(fromAddr string, head *Header, headerData, msgData []byte) (n *enode.Node, p Packet, err error) { + node, auth, session, err := c.decodeHandshake(fromAddr, head) + if err != nil { + c.sc.deleteHandshake(auth.h.SrcID, fromAddr) + return nil, nil, err + } + + // Decrypt the message using the new session keys. + msg, err := c.decryptMessage(msgData, head.Nonce[:], headerData, session.readKey) + if err != nil { + c.sc.deleteHandshake(auth.h.SrcID, fromAddr) + return node, msg, err + } + + // Handshake OK, drop the challenge and store the new session keys. + c.sc.storeNewSession(auth.h.SrcID, fromAddr, session) + c.sc.deleteHandshake(auth.h.SrcID, fromAddr) + return node, msg, nil +} + +func (c *Codec) decodeHandshake(fromAddr string, head *Header) (n *enode.Node, auth handshakeAuthData, s *session, err error) { + if auth, err = c.decodeHandshakeAuthData(head); err != nil { + return nil, auth, nil, err + } + + // Verify against our last WHOAREYOU. + challenge := c.sc.getHandshake(auth.h.SrcID, fromAddr) + if challenge == nil { + return nil, auth, nil, errUnexpectedHandshake + } + // Get node record. + n, err = c.decodeHandshakeRecord(challenge.Node, auth.h.SrcID, auth.record) + if err != nil { + return nil, auth, nil, err + } + // Verify ID nonce signature. + sig := auth.signature + cdata := challenge.ChallengeData + err = verifyIDSignature(c.sha256, sig, n, cdata, auth.pubkey, c.localnode.ID()) + if err != nil { + return nil, auth, nil, err + } + // Verify ephemeral key is on curve. + ephkey, err := DecodePubkey(c.privkey.Curve, auth.pubkey) + if err != nil { + return nil, auth, nil, errInvalidAuthKey + } + // Derive sesssion keys. + session := deriveKeys(sha256.New, c.privkey, ephkey, auth.h.SrcID, c.localnode.ID(), cdata) + session = session.keysFlipped() + return n, auth, session, nil +} + +// decodeHandshakeAuthData reads the authdata section of a handshake packet. +func (c *Codec) decodeHandshakeAuthData(head *Header) (auth handshakeAuthData, err error) { + // Decode fixed size part. + if len(head.AuthData) < sizeofHandshakeAuthData { + return auth, fmt.Errorf("header authsize %d too low for handshake", head.AuthSize) + } + c.reader.Reset(head.AuthData) + binary.Read(&c.reader, binary.BigEndian, &auth.h) + head.src = auth.h.SrcID + + // Decode variable-size part. + var ( + vardata = head.AuthData[sizeofHandshakeAuthData:] + sigAndKeySize = int(auth.h.SigSize) + int(auth.h.PubkeySize) + keyOffset = int(auth.h.SigSize) + recOffset = keyOffset + int(auth.h.PubkeySize) + ) + if len(vardata) < sigAndKeySize { + return auth, errTooShort + } + auth.signature = vardata[:keyOffset] + auth.pubkey = vardata[keyOffset:recOffset] + auth.record = vardata[recOffset:] + return auth, nil +} + +// decodeHandshakeRecord verifies the node record contained in a handshake packet. The +// remote node should include the record if we don't have one or if ours is older than the +// latest sequence number. +func (c *Codec) decodeHandshakeRecord(local *enode.Node, wantID enode.ID, remote []byte) (*enode.Node, error) { + node := local + if len(remote) > 0 { + var record enr.Record + if err := rlp.DecodeBytes(remote, &record); err != nil { + return nil, err + } + if local == nil || local.Seq() < record.Seq() { + n, err := enode.New(enode.ValidSchemes, &record) + if err != nil { + return nil, fmt.Errorf("invalid node record: %v", err) + } + if n.ID() != wantID { + return nil, fmt.Errorf("record in handshake has wrong ID: %v", n.ID()) + } + node = n + } + } + if node == nil { + return nil, errNoRecord + } + return node, nil +} + +// decodeMessage reads packet data following the header as an ordinary message packet. +func (c *Codec) decodeMessage(fromAddr string, head *Header, headerData, msgData []byte) (Packet, error) { + if len(head.AuthData) != sizeofMessageAuthData { + return nil, fmt.Errorf("invalid auth size %d for message packet", len(head.AuthData)) + } + var auth messageAuthData + c.reader.Reset(head.AuthData) + binary.Read(&c.reader, binary.BigEndian, &auth) + head.src = auth.SrcID + + // Try decrypting the message. + key := c.sc.readKey(auth.SrcID, fromAddr) + msg, err := c.decryptMessage(msgData, head.Nonce[:], headerData, key) + if err == errMessageDecrypt { + // It didn't work. Start the handshake since this is an ordinary message packet. + return &Unknown{Nonce: head.Nonce}, nil + } + return msg, err +} + +func (c *Codec) decryptMessage(input, nonce, headerData, readKey []byte) (Packet, error) { + msgdata, err := decryptGCM(readKey, nonce, input, headerData) + if err != nil { + return nil, errMessageDecrypt + } + if len(msgdata) == 0 { + return nil, errMessageTooShort + } + return DecodeMessage(msgdata[0], msgdata[1:]) +} + +// checkValid performs some basic validity checks on the header. +// The packetLen here is the length remaining after the static header. +func (h *StaticHeader) checkValid(packetLen int) error { + if h.ProtocolID != protocolID { + return errInvalidHeader + } + if h.Version < minVersion { + return errMinVersion + } + if h.Flag != flagWhoareyou && packetLen < minMessageSize { + return errMsgTooShort + } + if int(h.AuthSize) > packetLen { + return errAuthSize + } + return nil +} + +// headerMask returns a cipher for 'masking' / 'unmasking' packet headers. +func (h *Header) mask(destID enode.ID) cipher.Stream { + block, err := aes.NewCipher(destID[:16]) + if err != nil { + panic("can't create cipher") + } + return cipher.NewCTR(block, h.IV[:]) +} + +func bytesCopy(r *bytes.Buffer) []byte { + b := make([]byte, r.Len()) + copy(b, r.Bytes()) + return b +} diff --git a/p2p/discover/v5wire/encoding_test.go b/p2p/discover/v5wire/encoding_test.go new file mode 100644 index 0000000000..d9c807e0a8 --- /dev/null +++ b/p2p/discover/v5wire/encoding_test.go @@ -0,0 +1,636 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package v5wire + +import ( + "bytes" + "crypto/ecdsa" + "encoding/hex" + "flag" + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// To regenerate discv5 test vectors, run +// +// go test -run TestVectors -write-test-vectors +// +var writeTestVectorsFlag = flag.Bool("write-test-vectors", false, "Overwrite discv5 test vectors in testdata/") + +var ( + testKeyA, _ = crypto.HexToECDSA("eef77acb6c6a6eebc5b363a475ac583ec7eccdb42b6481424c60f59aa326547f") + testKeyB, _ = crypto.HexToECDSA("66fb62bfbd66b9177a138c1e5cddbe4f7c30c343e94e68df8769459cb1cde628") + testEphKey, _ = crypto.HexToECDSA("0288ef00023598499cb6c940146d050d2b1fb914198c327f76aad590bead68b6") + testIDnonce = [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} +) + +// This test checks that the minPacketSize and randomPacketMsgSize constants are well-defined. +func TestMinSizes(t *testing.T) { + var ( + gcmTagSize = 16 + emptyMsg = sizeofMessageAuthData + gcmTagSize + ) + t.Log("static header size", sizeofStaticPacketData) + t.Log("whoareyou size", sizeofStaticPacketData+sizeofWhoareyouAuthData) + t.Log("empty msg size", sizeofStaticPacketData+emptyMsg) + if want := emptyMsg; minMessageSize != want { + t.Fatalf("wrong minMessageSize %d, want %d", minMessageSize, want) + } + if sizeofMessageAuthData+randomPacketMsgSize < minMessageSize { + t.Fatalf("randomPacketMsgSize %d too small", randomPacketMsgSize) + } +} + +// This test checks the basic handshake flow where A talks to B and A has no secrets. +func TestHandshake(t *testing.T) { + t.Parallel() + net := newHandshakeTest() + defer net.close() + + // A -> B RANDOM PACKET + packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{}) + resp := net.nodeB.expectDecode(t, UnknownPacket, packet) + + // A <- B WHOAREYOU + challenge := &Whoareyou{ + Nonce: resp.(*Unknown).Nonce, + IDNonce: testIDnonce, + RecordSeq: 0, + } + whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) + net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou) + + // A -> B FINDNODE (handshake packet) + findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{}) + net.nodeB.expectDecode(t, FindnodeMsg, findnode) + if len(net.nodeB.c.sc.handshakes) > 0 { + t.Fatalf("node B didn't remove handshake from challenge map") + } + + // A <- B NODES + nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1}) + net.nodeA.expectDecode(t, NodesMsg, nodes) +} + +// This test checks that handshake attempts are removed within the timeout. +func TestHandshake_timeout(t *testing.T) { + t.Parallel() + net := newHandshakeTest() + defer net.close() + + // A -> B RANDOM PACKET + packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{}) + resp := net.nodeB.expectDecode(t, UnknownPacket, packet) + + // A <- B WHOAREYOU + challenge := &Whoareyou{ + Nonce: resp.(*Unknown).Nonce, + IDNonce: testIDnonce, + RecordSeq: 0, + } + whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) + net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou) + + // A -> B FINDNODE (handshake packet) after timeout + net.clock.Run(handshakeTimeout + 1) + findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{}) + net.nodeB.expectDecodeErr(t, errUnexpectedHandshake, findnode) +} + +// This test checks handshake behavior when no record is sent in the auth response. +func TestHandshake_norecord(t *testing.T) { + t.Parallel() + net := newHandshakeTest() + defer net.close() + + // A -> B RANDOM PACKET + packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{}) + resp := net.nodeB.expectDecode(t, UnknownPacket, packet) + + // A <- B WHOAREYOU + nodeA := net.nodeA.n() + if nodeA.Seq() == 0 { + t.Fatal("need non-zero sequence number") + } + challenge := &Whoareyou{ + Nonce: resp.(*Unknown).Nonce, + IDNonce: testIDnonce, + RecordSeq: nodeA.Seq(), + Node: nodeA, + } + whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) + net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou) + + // A -> B FINDNODE + findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{}) + net.nodeB.expectDecode(t, FindnodeMsg, findnode) + + // A <- B NODES + nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1}) + net.nodeA.expectDecode(t, NodesMsg, nodes) +} + +// In this test, A tries to send FINDNODE with existing secrets but B doesn't know +// anything about A. +func TestHandshake_rekey(t *testing.T) { + t.Parallel() + net := newHandshakeTest() + defer net.close() + + session := &session{ + readKey: []byte("BBBBBBBBBBBBBBBB"), + writeKey: []byte("AAAAAAAAAAAAAAAA"), + } + net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session) + + // A -> B FINDNODE (encrypted with zero keys) + findnode, authTag := net.nodeA.encode(t, net.nodeB, &Findnode{}) + net.nodeB.expectDecode(t, UnknownPacket, findnode) + + // A <- B WHOAREYOU + challenge := &Whoareyou{Nonce: authTag, IDNonce: testIDnonce} + whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) + net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou) + + // Check that new keys haven't been stored yet. + sa := net.nodeA.c.sc.session(net.nodeB.id(), net.nodeB.addr()) + if !bytes.Equal(sa.writeKey, session.writeKey) || !bytes.Equal(sa.readKey, session.readKey) { + t.Fatal("node A stored keys too early") + } + if s := net.nodeB.c.sc.session(net.nodeA.id(), net.nodeA.addr()); s != nil { + t.Fatal("node B stored keys too early") + } + + // A -> B FINDNODE encrypted with new keys + findnode, _ = net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{}) + net.nodeB.expectDecode(t, FindnodeMsg, findnode) + + // A <- B NODES + nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1}) + net.nodeA.expectDecode(t, NodesMsg, nodes) +} + +// In this test A and B have different keys before the handshake. +func TestHandshake_rekey2(t *testing.T) { + t.Parallel() + net := newHandshakeTest() + defer net.close() + + initKeysA := &session{ + readKey: []byte("BBBBBBBBBBBBBBBB"), + writeKey: []byte("AAAAAAAAAAAAAAAA"), + } + initKeysB := &session{ + readKey: []byte("CCCCCCCCCCCCCCCC"), + writeKey: []byte("DDDDDDDDDDDDDDDD"), + } + net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), initKeysA) + net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), initKeysB) + + // A -> B FINDNODE encrypted with initKeysA + findnode, authTag := net.nodeA.encode(t, net.nodeB, &Findnode{Distances: []uint{3}}) + net.nodeB.expectDecode(t, UnknownPacket, findnode) + + // A <- B WHOAREYOU + challenge := &Whoareyou{Nonce: authTag, IDNonce: testIDnonce} + whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) + net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou) + + // A -> B FINDNODE (handshake packet) + findnode, _ = net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{}) + net.nodeB.expectDecode(t, FindnodeMsg, findnode) + + // A <- B NODES + nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{Total: 1}) + net.nodeA.expectDecode(t, NodesMsg, nodes) +} + +func TestHandshake_BadHandshakeAttack(t *testing.T) { + t.Parallel() + net := newHandshakeTest() + defer net.close() + + // A -> B RANDOM PACKET + packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{}) + resp := net.nodeB.expectDecode(t, UnknownPacket, packet) + + // A <- B WHOAREYOU + challenge := &Whoareyou{ + Nonce: resp.(*Unknown).Nonce, + IDNonce: testIDnonce, + RecordSeq: 0, + } + whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge) + net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou) + + // A -> B FINDNODE + incorrect_challenge := &Whoareyou{ + IDNonce: [16]byte{5, 6, 7, 8, 9, 6, 11, 12}, + RecordSeq: challenge.RecordSeq, + Node: challenge.Node, + sent: challenge.sent, + } + incorrect_findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, incorrect_challenge, &Findnode{}) + incorrect_findnode2 := make([]byte, len(incorrect_findnode)) + copy(incorrect_findnode2, incorrect_findnode) + + net.nodeB.expectDecodeErr(t, errInvalidNonceSig, incorrect_findnode) + + // Reject new findnode as previous handshake is now deleted. + net.nodeB.expectDecodeErr(t, errUnexpectedHandshake, incorrect_findnode2) + + // The findnode packet is again rejected even with a valid challenge this time. + findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{}) + net.nodeB.expectDecodeErr(t, errUnexpectedHandshake, findnode) +} + +// This test checks some malformed packets. +func TestDecodeErrorsV5(t *testing.T) { + t.Parallel() + net := newHandshakeTest() + defer net.close() + + net.nodeA.expectDecodeErr(t, errTooShort, []byte{}) + // TODO some more tests would be nice :) + // - check invalid authdata sizes + // - check invalid handshake data sizes +} + +// This test checks that all test vectors can be decoded. +func TestTestVectorsV5(t *testing.T) { + var ( + idA = enode.PubkeyToIDV4(&testKeyA.PublicKey) + idB = enode.PubkeyToIDV4(&testKeyB.PublicKey) + addr = "127.0.0.1" + session = &session{ + writeKey: hexutil.MustDecode("0x00000000000000000000000000000000"), + readKey: hexutil.MustDecode("0x01010101010101010101010101010101"), + } + challenge0A, challenge1A, challenge0B Whoareyou + ) + + // Create challenge packets. + c := Whoareyou{ + Nonce: Nonce{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + IDNonce: testIDnonce, + } + challenge0A, challenge1A, challenge0B = c, c, c + challenge1A.RecordSeq = 1 + net := newHandshakeTest() + challenge0A.Node = net.nodeA.n() + challenge0B.Node = net.nodeB.n() + challenge1A.Node = net.nodeA.n() + net.close() + + type testVectorTest struct { + name string // test vector name + packet Packet // the packet to be encoded + challenge *Whoareyou // handshake challenge passed to encoder + prep func(*handshakeTest) // called before encode/decode + } + tests := []testVectorTest{ + { + name: "v5.1-whoareyou", + packet: &challenge0B, + }, + { + name: "v5.1-ping-message", + packet: &Ping{ + ReqID: []byte{0, 0, 0, 1}, + ENRSeq: 2, + }, + prep: func(net *handshakeTest) { + net.nodeA.c.sc.storeNewSession(idB, addr, session) + net.nodeB.c.sc.storeNewSession(idA, addr, session.keysFlipped()) + }, + }, + { + name: "v5.1-ping-handshake-enr", + packet: &Ping{ + ReqID: []byte{0, 0, 0, 1}, + ENRSeq: 1, + }, + challenge: &challenge0A, + prep: func(net *handshakeTest) { + // Update challenge.Header.AuthData. + net.nodeA.c.Encode(idB, "", &challenge0A, nil) + net.nodeB.c.sc.storeSentHandshake(idA, addr, &challenge0A) + }, + }, + { + name: "v5.1-ping-handshake", + packet: &Ping{ + ReqID: []byte{0, 0, 0, 1}, + ENRSeq: 1, + }, + challenge: &challenge1A, + prep: func(net *handshakeTest) { + // Update challenge data. + net.nodeA.c.Encode(idB, "", &challenge1A, nil) + net.nodeB.c.sc.storeSentHandshake(idA, addr, &challenge1A) + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + net := newHandshakeTest() + defer net.close() + + // Override all random inputs. + net.nodeA.c.sc.nonceGen = func(counter uint32) (Nonce, error) { + return Nonce{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, nil + } + net.nodeA.c.sc.maskingIVGen = func(buf []byte) error { + return nil // all zero + } + net.nodeA.c.sc.ephemeralKeyGen = func() (*ecdsa.PrivateKey, error) { + return testEphKey, nil + } + + // Prime the codec for encoding/decoding. + if test.prep != nil { + test.prep(net) + } + + file := filepath.Join("testdata", test.name+".txt") + if *writeTestVectorsFlag { + // Encode the packet. + d, nonce := net.nodeA.encodeWithChallenge(t, net.nodeB, test.challenge, test.packet) + comment := testVectorComment(net, test.packet, test.challenge, nonce) + writeTestVector(file, comment, d) + } + enc := hexFile(file) + net.nodeB.expectDecode(t, test.packet.Kind(), enc) + }) + } +} + +// testVectorComment creates the commentary for discv5 test vector files. +func testVectorComment(net *handshakeTest, p Packet, challenge *Whoareyou, nonce Nonce) string { + o := new(strings.Builder) + printWhoareyou := func(p *Whoareyou) { + fmt.Fprintf(o, "whoareyou.challenge-data = %#x\n", p.ChallengeData) + fmt.Fprintf(o, "whoareyou.request-nonce = %#x\n", p.Nonce[:]) + fmt.Fprintf(o, "whoareyou.id-nonce = %#x\n", p.IDNonce[:]) + fmt.Fprintf(o, "whoareyou.enr-seq = %d\n", p.RecordSeq) + } + + fmt.Fprintf(o, "src-node-id = %#x\n", net.nodeA.id().Bytes()) + fmt.Fprintf(o, "dest-node-id = %#x\n", net.nodeB.id().Bytes()) + switch p := p.(type) { + case *Whoareyou: + // WHOAREYOU packet. + printWhoareyou(p) + case *Ping: + fmt.Fprintf(o, "nonce = %#x\n", nonce[:]) + fmt.Fprintf(o, "read-key = %#x\n", net.nodeA.c.sc.session(net.nodeB.id(), net.nodeB.addr()).writeKey) + fmt.Fprintf(o, "ping.req-id = %#x\n", p.ReqID) + fmt.Fprintf(o, "ping.enr-seq = %d\n", p.ENRSeq) + if challenge != nil { + // Handshake message packet. + fmt.Fprint(o, "\nhandshake inputs:\n\n") + printWhoareyou(challenge) + fmt.Fprintf(o, "ephemeral-key = %#x\n", testEphKey.D.Bytes()) + fmt.Fprintf(o, "ephemeral-pubkey = %#x\n", crypto.CompressPubkey(&testEphKey.PublicKey)) + } + default: + panic(fmt.Errorf("unhandled packet type %T", p)) + } + return o.String() +} + +// This benchmark checks performance of handshake packet decoding. +func BenchmarkV5_DecodeHandshakePingSecp256k1(b *testing.B) { + net := newHandshakeTest() + defer net.close() + + var ( + idA = net.nodeA.id() + challenge = &Whoareyou{Node: net.nodeB.n()} + message = &Ping{ReqID: []byte("reqid")} + ) + enc, _, err := net.nodeA.c.Encode(net.nodeB.id(), "", message, challenge) + if err != nil { + b.Fatal("can't encode handshake packet") + } + challenge.Node = nil // force ENR signature verification in decoder + b.ResetTimer() + + input := make([]byte, len(enc)) + for i := 0; i < b.N; i++ { + copy(input, enc) + net.nodeB.c.sc.storeSentHandshake(idA, "", challenge) + _, _, _, err := net.nodeB.c.Decode(input, "") + if err != nil { + b.Fatal(err) + } + } +} + +// This benchmark checks how long it takes to decode an encrypted ping packet. +func BenchmarkV5_DecodePing(b *testing.B) { + net := newHandshakeTest() + defer net.close() + + session := &session{ + readKey: []byte{233, 203, 93, 195, 86, 47, 177, 186, 227, 43, 2, 141, 244, 230, 120, 17}, + writeKey: []byte{79, 145, 252, 171, 167, 216, 252, 161, 208, 190, 176, 106, 214, 39, 178, 134}, + } + net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session) + net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), session.keysFlipped()) + addrB := net.nodeA.addr() + ping := &Ping{ReqID: []byte("reqid"), ENRSeq: 5} + enc, _, err := net.nodeA.c.Encode(net.nodeB.id(), addrB, ping, nil) + if err != nil { + b.Fatalf("can't encode: %v", err) + } + b.ResetTimer() + + input := make([]byte, len(enc)) + for i := 0; i < b.N; i++ { + copy(input, enc) + _, _, packet, _ := net.nodeB.c.Decode(input, addrB) + if _, ok := packet.(*Ping); !ok { + b.Fatalf("wrong packet type %T", packet) + } + } +} + +var pp = spew.NewDefaultConfig() + +type handshakeTest struct { + nodeA, nodeB handshakeTestNode + clock mclock.Simulated +} + +type handshakeTestNode struct { + ln *enode.LocalNode + c *Codec +} + +func newHandshakeTest() *handshakeTest { + t := new(handshakeTest) + t.nodeA.init(testKeyA, net.IP{127, 0, 0, 1}, &t.clock) + t.nodeB.init(testKeyB, net.IP{127, 0, 0, 1}, &t.clock) + return t +} + +func (t *handshakeTest) close() { + t.nodeA.ln.Database().Close() + t.nodeB.ln.Database().Close() +} + +func (n *handshakeTestNode) init(key *ecdsa.PrivateKey, ip net.IP, clock mclock.Clock) { + db, _ := enode.OpenDB("") + n.ln = enode.NewLocalNode(db, key) + n.ln.SetStaticIP(ip) + if n.ln.Node().Seq() != 1 { + panic(fmt.Errorf("unexpected seq %d", n.ln.Node().Seq())) + } + n.c = NewCodec(n.ln, key, clock) +} + +func (n *handshakeTestNode) encode(t testing.TB, to handshakeTestNode, p Packet) ([]byte, Nonce) { + t.Helper() + return n.encodeWithChallenge(t, to, nil, p) +} + +func (n *handshakeTestNode) encodeWithChallenge(t testing.TB, to handshakeTestNode, c *Whoareyou, p Packet) ([]byte, Nonce) { + t.Helper() + + // Copy challenge and add destination node. This avoids sharing 'c' among the two codecs. + var challenge *Whoareyou + if c != nil { + challengeCopy := *c + challenge = &challengeCopy + challenge.Node = to.n() + } + // Encode to destination. + enc, nonce, err := n.c.Encode(to.id(), to.addr(), p, challenge) + if err != nil { + t.Fatal(fmt.Errorf("(%s) %v", n.ln.ID().TerminalString(), err)) + } + t.Logf("(%s) -> (%s) %s\n%s", n.ln.ID().TerminalString(), to.id().TerminalString(), p.Name(), hex.Dump(enc)) + return enc, nonce +} + +func (n *handshakeTestNode) expectDecode(t *testing.T, ptype byte, p []byte) Packet { + t.Helper() + + dec, err := n.decode(p) + if err != nil { + t.Fatal(fmt.Errorf("(%s) %v", n.ln.ID().TerminalString(), err)) + } + t.Logf("(%s) %#v", n.ln.ID().TerminalString(), pp.NewFormatter(dec)) + if dec.Kind() != ptype { + t.Fatalf("expected packet type %d, got %d", ptype, dec.Kind()) + } + return dec +} + +func (n *handshakeTestNode) expectDecodeErr(t *testing.T, wantErr error, p []byte) { + t.Helper() + if _, err := n.decode(p); !reflect.DeepEqual(err, wantErr) { + t.Fatal(fmt.Errorf("(%s) got err %q, want %q", n.ln.ID().TerminalString(), err, wantErr)) + } +} + +func (n *handshakeTestNode) decode(input []byte) (Packet, error) { + _, _, p, err := n.c.Decode(input, "127.0.0.1") + return p, err +} + +func (n *handshakeTestNode) n() *enode.Node { + return n.ln.Node() +} + +func (n *handshakeTestNode) addr() string { + return n.ln.Node().IP().String() +} + +func (n *handshakeTestNode) id() enode.ID { + return n.ln.ID() +} + +// hexFile reads the given file and decodes the hex data contained in it. +// Whitespace and any lines beginning with the # character are ignored. +func hexFile(file string) []byte { + fileContent, err := ioutil.ReadFile(file) + if err != nil { + panic(err) + } + + // Gather hex data, ignore comments. + var text []byte + for _, line := range bytes.Split(fileContent, []byte("\n")) { + line = bytes.TrimSpace(line) + if len(line) > 0 && line[0] == '#' { + continue + } + text = append(text, line...) + } + + // Parse the hex. + if bytes.HasPrefix(text, []byte("0x")) { + text = text[2:] + } + data := make([]byte, hex.DecodedLen(len(text))) + if _, err := hex.Decode(data, text); err != nil { + panic("invalid hex in " + file) + } + return data +} + +// writeTestVector writes a test vector file with the given commentary and binary data. +func writeTestVector(file, comment string, data []byte) { + fd, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + panic(err) + } + defer fd.Close() + + if len(comment) > 0 { + for _, line := range strings.Split(strings.TrimSpace(comment), "\n") { + fmt.Fprintf(fd, "# %s\n", line) + } + fmt.Fprintln(fd) + } + for len(data) > 0 { + var chunk []byte + if len(data) < 32 { + chunk = data + } else { + chunk = data[:32] + } + data = data[len(chunk):] + fmt.Fprintf(fd, "%x\n", chunk) + } +} diff --git a/p2p/discover/v5wire/msg.go b/p2p/discover/v5wire/msg.go new file mode 100644 index 0000000000..7c3686111b --- /dev/null +++ b/p2p/discover/v5wire/msg.go @@ -0,0 +1,249 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package v5wire + +import ( + "fmt" + "net" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/rlp" +) + +// Packet is implemented by all message types. +type Packet interface { + Name() string // Name returns a string corresponding to the message type. + Kind() byte // Kind returns the message type. + RequestID() []byte // Returns the request ID. + SetRequestID([]byte) // Sets the request ID. +} + +// Message types. +const ( + PingMsg byte = iota + 1 + PongMsg + FindnodeMsg + NodesMsg + TalkRequestMsg + TalkResponseMsg + RequestTicketMsg + TicketMsg + RegtopicMsg + RegconfirmationMsg + TopicQueryMsg + + UnknownPacket = byte(255) // any non-decryptable packet + WhoareyouPacket = byte(254) // the WHOAREYOU packet +) + +// Protocol messages. +type ( + // Unknown represents any packet that can't be decrypted. + Unknown struct { + Nonce Nonce + } + + // WHOAREYOU contains the handshake challenge. + Whoareyou struct { + ChallengeData []byte // Encoded challenge + Nonce Nonce // Nonce of request packet + IDNonce [16]byte // Identity proof data + RecordSeq uint64 // ENR sequence number of recipient + + // Node is the locally known node record of recipient. + // This must be set by the caller of Encode. + Node *enode.Node + + sent mclock.AbsTime // for handshake GC. + } + + // PING is sent during liveness checks. + Ping struct { + ReqID []byte + ENRSeq uint64 + } + + // PONG is the reply to PING. + Pong struct { + ReqID []byte + ENRSeq uint64 + ToIP net.IP // These fields should mirror the UDP envelope address of the ping + ToPort uint16 // packet, which provides a way to discover the the external address (after NAT). + } + + // FINDNODE is a query for nodes in the given bucket. + Findnode struct { + ReqID []byte + Distances []uint + } + + // NODES is the reply to FINDNODE and TOPICQUERY. + Nodes struct { + ReqID []byte + Total uint8 + Nodes []*enr.Record + } + + // TALKREQ is an application-level request. + TalkRequest struct { + ReqID []byte + Protocol string + Message []byte + } + + // TALKRESP is the reply to TALKREQ. + TalkResponse struct { + ReqID []byte + Message []byte + } + + // REQUESTTICKET requests a ticket for a topic queue. + RequestTicket struct { + ReqID []byte + Topic []byte + } + + // TICKET is the response to REQUESTTICKET. + Ticket struct { + ReqID []byte + Ticket []byte + } + + // REGTOPIC registers the sender in a topic queue using a ticket. + Regtopic struct { + ReqID []byte + Ticket []byte + ENR *enr.Record + } + + // REGCONFIRMATION is the reply to REGTOPIC. + Regconfirmation struct { + ReqID []byte + Registered bool + } + + // TOPICQUERY asks for nodes with the given topic. + TopicQuery struct { + ReqID []byte + Topic []byte + } +) + +// DecodeMessage decodes the message body of a packet. +func DecodeMessage(ptype byte, body []byte) (Packet, error) { + var dec Packet + switch ptype { + case PingMsg: + dec = new(Ping) + case PongMsg: + dec = new(Pong) + case FindnodeMsg: + dec = new(Findnode) + case NodesMsg: + dec = new(Nodes) + case TalkRequestMsg: + dec = new(TalkRequest) + case TalkResponseMsg: + dec = new(TalkResponse) + case RequestTicketMsg: + dec = new(RequestTicket) + case TicketMsg: + dec = new(Ticket) + case RegtopicMsg: + dec = new(Regtopic) + case RegconfirmationMsg: + dec = new(Regconfirmation) + case TopicQueryMsg: + dec = new(TopicQuery) + default: + return nil, fmt.Errorf("unknown packet type %d", ptype) + } + if err := rlp.DecodeBytes(body, dec); err != nil { + return nil, err + } + if dec.RequestID() != nil && len(dec.RequestID()) > 8 { + return nil, ErrInvalidReqID + } + return dec, nil +} + +func (*Whoareyou) Name() string { return "WHOAREYOU/v5" } +func (*Whoareyou) Kind() byte { return WhoareyouPacket } +func (*Whoareyou) RequestID() []byte { return nil } +func (*Whoareyou) SetRequestID([]byte) {} + +func (*Unknown) Name() string { return "UNKNOWN/v5" } +func (*Unknown) Kind() byte { return UnknownPacket } +func (*Unknown) RequestID() []byte { return nil } +func (*Unknown) SetRequestID([]byte) {} + +func (*Ping) Name() string { return "PING/v5" } +func (*Ping) Kind() byte { return PingMsg } +func (p *Ping) RequestID() []byte { return p.ReqID } +func (p *Ping) SetRequestID(id []byte) { p.ReqID = id } + +func (*Pong) Name() string { return "PONG/v5" } +func (*Pong) Kind() byte { return PongMsg } +func (p *Pong) RequestID() []byte { return p.ReqID } +func (p *Pong) SetRequestID(id []byte) { p.ReqID = id } + +func (*Findnode) Name() string { return "FINDNODE/v5" } +func (*Findnode) Kind() byte { return FindnodeMsg } +func (p *Findnode) RequestID() []byte { return p.ReqID } +func (p *Findnode) SetRequestID(id []byte) { p.ReqID = id } + +func (*Nodes) Name() string { return "NODES/v5" } +func (*Nodes) Kind() byte { return NodesMsg } +func (p *Nodes) RequestID() []byte { return p.ReqID } +func (p *Nodes) SetRequestID(id []byte) { p.ReqID = id } + +func (*TalkRequest) Name() string { return "TALKREQ/v5" } +func (*TalkRequest) Kind() byte { return TalkRequestMsg } +func (p *TalkRequest) RequestID() []byte { return p.ReqID } +func (p *TalkRequest) SetRequestID(id []byte) { p.ReqID = id } + +func (*TalkResponse) Name() string { return "TALKRESP/v5" } +func (*TalkResponse) Kind() byte { return TalkResponseMsg } +func (p *TalkResponse) RequestID() []byte { return p.ReqID } +func (p *TalkResponse) SetRequestID(id []byte) { p.ReqID = id } + +func (*RequestTicket) Name() string { return "REQTICKET/v5" } +func (*RequestTicket) Kind() byte { return RequestTicketMsg } +func (p *RequestTicket) RequestID() []byte { return p.ReqID } +func (p *RequestTicket) SetRequestID(id []byte) { p.ReqID = id } + +func (*Regtopic) Name() string { return "REGTOPIC/v5" } +func (*Regtopic) Kind() byte { return RegtopicMsg } +func (p *Regtopic) RequestID() []byte { return p.ReqID } +func (p *Regtopic) SetRequestID(id []byte) { p.ReqID = id } + +func (*Ticket) Name() string { return "TICKET/v5" } +func (*Ticket) Kind() byte { return TicketMsg } +func (p *Ticket) RequestID() []byte { return p.ReqID } +func (p *Ticket) SetRequestID(id []byte) { p.ReqID = id } + +func (*Regconfirmation) Name() string { return "REGCONFIRMATION/v5" } +func (*Regconfirmation) Kind() byte { return RegconfirmationMsg } +func (p *Regconfirmation) RequestID() []byte { return p.ReqID } +func (p *Regconfirmation) SetRequestID(id []byte) { p.ReqID = id } + +func (*TopicQuery) Name() string { return "TOPICQUERY/v5" } +func (*TopicQuery) Kind() byte { return TopicQueryMsg } +func (p *TopicQuery) RequestID() []byte { return p.ReqID } +func (p *TopicQuery) SetRequestID(id []byte) { p.ReqID = id } diff --git a/p2p/discover/v5_session.go b/p2p/discover/v5wire/session.go similarity index 58% rename from p2p/discover/v5_session.go rename to p2p/discover/v5wire/session.go index 8a0eeb6977..d52b5c1181 100644 --- a/p2p/discover/v5_session.go +++ b/p2p/discover/v5wire/session.go @@ -14,22 +14,33 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package discover +package v5wire import ( + "crypto/ecdsa" crand "crypto/rand" + "encoding/binary" + "time" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/hashicorp/golang-lru/simplelru" ) -// The sessionCache keeps negotiated encryption keys and +const handshakeTimeout = time.Second + +// The SessionCache keeps negotiated encryption keys and // state for in-progress handshakes in the Discovery v5 wire protocol. -type sessionCache struct { +type SessionCache struct { sessions *simplelru.LRU - handshakes map[sessionID]*whoareyouV5 + handshakes map[sessionID]*Whoareyou clock mclock.Clock + + // hooks for overriding randomness. + nonceGen func(uint32) (Nonce, error) + maskingIVGen func([]byte) error + ephemeralKeyGen func() (*ecdsa.PrivateKey, error) } // sessionID identifies a session or handshake. @@ -45,27 +56,45 @@ type session struct { nonceCounter uint32 } -func newSessionCache(maxItems int, clock mclock.Clock) *sessionCache { +// keysFlipped returns a copy of s with the read and write keys flipped. +func (s *session) keysFlipped() *session { + return &session{s.readKey, s.writeKey, s.nonceCounter} +} + +func NewSessionCache(maxItems int, clock mclock.Clock) *SessionCache { cache, err := simplelru.NewLRU(maxItems, nil) if err != nil { panic("can't create session cache") } - return &sessionCache{ - sessions: cache, - handshakes: make(map[sessionID]*whoareyouV5), - clock: clock, + return &SessionCache{ + sessions: cache, + handshakes: make(map[sessionID]*Whoareyou), + clock: clock, + nonceGen: generateNonce, + maskingIVGen: generateMaskingIV, + ephemeralKeyGen: crypto.GenerateKey, } } +func generateNonce(counter uint32) (n Nonce, err error) { + binary.BigEndian.PutUint32(n[:4], counter) + _, err = crand.Read(n[4:]) + return n, err +} + +func generateMaskingIV(buf []byte) error { + _, err := crand.Read(buf) + return err +} + // nextNonce creates a nonce for encrypting a message to the given session. -func (sc *sessionCache) nextNonce(id enode.ID, addr string) []byte { - n := make([]byte, gcmNonceSize) - crand.Read(n) - return n +func (sc *SessionCache) nextNonce(s *session) (Nonce, error) { + s.nonceCounter++ + return sc.nonceGen(s.nonceCounter) } // session returns the current session for the given node, if any. -func (sc *sessionCache) session(id enode.ID, addr string) *session { +func (sc *SessionCache) session(id enode.ID, addr string) *session { item, ok := sc.sessions.Get(sessionID{id, addr}) if !ok { return nil @@ -74,46 +103,36 @@ func (sc *sessionCache) session(id enode.ID, addr string) *session { } // readKey returns the current read key for the given node. -func (sc *sessionCache) readKey(id enode.ID, addr string) []byte { +func (sc *SessionCache) readKey(id enode.ID, addr string) []byte { if s := sc.session(id, addr); s != nil { return s.readKey } return nil } -// writeKey returns the current read key for the given node. -func (sc *sessionCache) writeKey(id enode.ID, addr string) []byte { - if s := sc.session(id, addr); s != nil { - return s.writeKey - } - return nil -} - // storeNewSession stores new encryption keys in the cache. -func (sc *sessionCache) storeNewSession(id enode.ID, addr string, r, w []byte) { - sc.sessions.Add(sessionID{id, addr}, &session{ - readKey: r, writeKey: w, - }) +func (sc *SessionCache) storeNewSession(id enode.ID, addr string, s *session) { + sc.sessions.Add(sessionID{id, addr}, s) } // getHandshake gets the handshake challenge we previously sent to the given remote node. -func (sc *sessionCache) getHandshake(id enode.ID, addr string) *whoareyouV5 { +func (sc *SessionCache) getHandshake(id enode.ID, addr string) *Whoareyou { return sc.handshakes[sessionID{id, addr}] } // storeSentHandshake stores the handshake challenge sent to the given remote node. -func (sc *sessionCache) storeSentHandshake(id enode.ID, addr string, challenge *whoareyouV5) { +func (sc *SessionCache) storeSentHandshake(id enode.ID, addr string, challenge *Whoareyou) { challenge.sent = sc.clock.Now() sc.handshakes[sessionID{id, addr}] = challenge } // deleteHandshake deletes handshake data for the given node. -func (sc *sessionCache) deleteHandshake(id enode.ID, addr string) { +func (sc *SessionCache) deleteHandshake(id enode.ID, addr string) { delete(sc.handshakes, sessionID{id, addr}) } // handshakeGC deletes timed-out handshakes. -func (sc *sessionCache) handshakeGC() { +func (sc *SessionCache) handshakeGC() { deadline := sc.clock.Now().Add(-handshakeTimeout) for key, challenge := range sc.handshakes { if challenge.sent < deadline { diff --git a/p2p/discover/v5wire/testdata/v5.1-ping-handshake-enr.txt b/p2p/discover/v5wire/testdata/v5.1-ping-handshake-enr.txt new file mode 100644 index 0000000000..477f9e15a8 --- /dev/null +++ b/p2p/discover/v5wire/testdata/v5.1-ping-handshake-enr.txt @@ -0,0 +1,27 @@ +# src-node-id = 0xaaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb +# dest-node-id = 0xbbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9 +# nonce = 0xffffffffffffffffffffffff +# read-key = 0x53b1c075f41876423154e157470c2f48 +# ping.req-id = 0x00000001 +# ping.enr-seq = 1 +# +# handshake inputs: +# +# whoareyou.challenge-data = 0x000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000 +# whoareyou.request-nonce = 0x0102030405060708090a0b0c +# whoareyou.id-nonce = 0x0102030405060708090a0b0c0d0e0f10 +# whoareyou.enr-seq = 0 +# ephemeral-key = 0x0288ef00023598499cb6c940146d050d2b1fb914198c327f76aad590bead68b6 +# ephemeral-pubkey = 0x039a003ba6517b473fa0cd74aefe99dadfdb34627f90fec6362df85803908f53a5 + +00000000000000000000000000000000088b3d4342774649305f313964a39e55 +ea96c005ad539c8c7560413a7008f16c9e6d2f43bbea8814a546b7409ce783d3 +4c4f53245d08da4bb23698868350aaad22e3ab8dd034f548a1c43cd246be9856 +2fafa0a1fa86d8e7a3b95ae78cc2b988ded6a5b59eb83ad58097252188b902b2 +1481e30e5e285f19735796706adff216ab862a9186875f9494150c4ae06fa4d1 +f0396c93f215fa4ef524e0ed04c3c21e39b1868e1ca8105e585ec17315e755e6 +cfc4dd6cb7fd8e1a1f55e49b4b5eb024221482105346f3c82b15fdaae36a3bb1 +2a494683b4a3c7f2ae41306252fed84785e2bbff3b022812d0882f06978df84a +80d443972213342d04b9048fc3b1d5fcb1df0f822152eced6da4d3f6df27e70e +4539717307a0208cd208d65093ccab5aa596a34d7511401987662d8cf62b1394 +71 diff --git a/p2p/discover/v5wire/testdata/v5.1-ping-handshake.txt b/p2p/discover/v5wire/testdata/v5.1-ping-handshake.txt new file mode 100644 index 0000000000..b3f304766c --- /dev/null +++ b/p2p/discover/v5wire/testdata/v5.1-ping-handshake.txt @@ -0,0 +1,23 @@ +# src-node-id = 0xaaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb +# dest-node-id = 0xbbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9 +# nonce = 0xffffffffffffffffffffffff +# read-key = 0x4f9fac6de7567d1e3b1241dffe90f662 +# ping.req-id = 0x00000001 +# ping.enr-seq = 1 +# +# handshake inputs: +# +# whoareyou.challenge-data = 0x000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000001 +# whoareyou.request-nonce = 0x0102030405060708090a0b0c +# whoareyou.id-nonce = 0x0102030405060708090a0b0c0d0e0f10 +# whoareyou.enr-seq = 1 +# ephemeral-key = 0x0288ef00023598499cb6c940146d050d2b1fb914198c327f76aad590bead68b6 +# ephemeral-pubkey = 0x039a003ba6517b473fa0cd74aefe99dadfdb34627f90fec6362df85803908f53a5 + +00000000000000000000000000000000088b3d4342774649305f313964a39e55 +ea96c005ad521d8c7560413a7008f16c9e6d2f43bbea8814a546b7409ce783d3 +4c4f53245d08da4bb252012b2cba3f4f374a90a75cff91f142fa9be3e0a5f3ef +268ccb9065aeecfd67a999e7fdc137e062b2ec4a0eb92947f0d9a74bfbf44dfb +a776b21301f8b65efd5796706adff216ab862a9186875f9494150c4ae06fa4d1 +f0396c93f215fa4ef524f1eadf5f0f4126b79336671cbcf7a885b1f8bd2a5d83 +9cf8 diff --git a/p2p/discover/v5wire/testdata/v5.1-ping-message.txt b/p2p/discover/v5wire/testdata/v5.1-ping-message.txt new file mode 100644 index 0000000000..f82b99c3bc --- /dev/null +++ b/p2p/discover/v5wire/testdata/v5.1-ping-message.txt @@ -0,0 +1,10 @@ +# src-node-id = 0xaaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb +# dest-node-id = 0xbbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9 +# nonce = 0xffffffffffffffffffffffff +# read-key = 0x00000000000000000000000000000000 +# ping.req-id = 0x00000001 +# ping.enr-seq = 2 + +00000000000000000000000000000000088b3d4342774649325f313964a39e55 +ea96c005ad52be8c7560413a7008f16c9e6d2f43bbea8814a546b7409ce783d3 +4c4f53245d08dab84102ed931f66d1492acb308fa1c6715b9d139b81acbdcc diff --git a/p2p/discover/v5wire/testdata/v5.1-whoareyou.txt b/p2p/discover/v5wire/testdata/v5.1-whoareyou.txt new file mode 100644 index 0000000000..1a75f525ee --- /dev/null +++ b/p2p/discover/v5wire/testdata/v5.1-whoareyou.txt @@ -0,0 +1,9 @@ +# src-node-id = 0xaaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb +# dest-node-id = 0xbbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9 +# whoareyou.challenge-data = 0x000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000 +# whoareyou.request-nonce = 0x0102030405060708090a0b0c +# whoareyou.id-nonce = 0x0102030405060708090a0b0c0d0e0f10 +# whoareyou.enr-seq = 0 + +00000000000000000000000000000000088b3d434277464933a1ccc59f5967ad +1d6035f15e528627dde75cd68292f9e6c27d6b66c8100a873fcbaed4e16b8d diff --git a/p2p/enode/node.go b/p2p/enode/node.go index 5380f12a9b..c557e68e70 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -23,7 +23,6 @@ import ( "errors" "fmt" "math/bits" - "math/rand" "net" "strings" @@ -367,23 +366,3 @@ func LogDist(a, b ID) int { } return len(a)*8 - lz } - -// RandomID returns a random ID b such that logdist(a, b) == n. -func RandomID(a ID, n int) (b ID) { - if n == 0 { - return a - } - // flip bit at position n, fill the rest with random bits - b = a - pos := len(a) - n/8 - 1 - bit := byte(0x01) << (byte(n%8) - 1) - if bit == 0 { - pos++ - bit = 0x80 - } - b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits - for i := pos + 1; i < len(a); i++ { - b[i] = byte(rand.Intn(255)) - } - return b -} diff --git a/p2p/netutil/error.go b/p2p/netutil/error.go index cb21b9cd4c..5d3d9bfd65 100644 --- a/p2p/netutil/error.go +++ b/p2p/netutil/error.go @@ -23,3 +23,11 @@ func IsTemporaryError(err error) bool { }) return ok && tempErr.Temporary() || isPacketTooBig(err) } + +// IsTimeout checks whether the given error is a timeout. +func IsTimeout(err error) bool { + timeoutErr, ok := err.(interface { + Timeout() bool + }) + return ok && timeoutErr.Timeout() +} diff --git a/p2p/peer.go b/p2p/peer.go index c6f8fec14f..00d44fd228 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -142,8 +142,17 @@ func (p *Peer) Node() *enode.Node { return p.rw.node } -// Name returns the node name that the remote node advertised. +// Name returns an abbreviated form of the name func (p *Peer) Name() string { + s := p.rw.name + if len(s) > 20 { + return s[:20] + "..." + } + return s +} + +// Fullname returns the node name that the remote node advertised. +func (p *Peer) Fullname() string { return p.rw.name } @@ -478,7 +487,7 @@ func (p *Peer) Info() *PeerInfo { info := &PeerInfo{ Enode: p.Node().URLv4(), ID: p.ID().String(), - Name: p.Name(), + Name: p.Fullname(), Caps: caps, Protocols: make(map[string]interface{}), } diff --git a/p2p/server.go b/p2p/server.go index 8c8e95987b..1067a12520 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -767,7 +767,7 @@ running: // The handshakes are done and it passed all checks. p := srv.launchPeer(c) peers[c.node.ID()] = p - srv.log.Debug("Adding p2p peer", "peercount", len(peers), "id", p.ID(), "conn", c.flags, "addr", p.RemoteAddr(), "name", truncateName(c.name)) + srv.log.Debug("Adding p2p peer", "peercount", len(peers), "id", p.ID(), "conn", c.flags, "addr", p.RemoteAddr(), "name", p.Name()) srv.dialsched.peerAdded(c) if p.Inbound() { inboundCount++ @@ -1053,13 +1053,6 @@ func nodeFromConn(pubkey *ecdsa.PublicKey, conn net.Conn) *enode.Node { return enode.NewV4(pubkey, ip, port, port) } -func truncateName(s string) string { - if len(s) > 20 { - return s[:20] + "..." - } - return s -} - // checkpoint sends the conn to run, which performs the // post-handshake checks for the stage (posthandshake, addpeer). func (srv *Server) checkpoint(c *conn, stage chan<- *conn) error { diff --git a/params/bootnodes.go b/params/bootnodes.go index c8736b8ae8..42a6e2ec7c 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -56,11 +56,15 @@ var GoerliBootnodes = []string{ "enode://011f758e6552d105183b1761c5e2dea0111bc20fd5f6422bc7f91e0fabbec9a6595caf6239b37feb773dddd3f87240d99d859431891e4a642cf2a0a9e6cbb98a@51.141.78.53:30303", "enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303", "enode://46add44b9f13965f7b9875ac6b85f016f341012d84f975377573800a863526f4da19ae2c620ec73d11591fa9510e992ecc03ad0751f53cc02f7c7ed6d55c7291@94.237.54.114:30313", - "enode://c1f8b7c2ac4453271fa07d8e9ecf9a2e8285aa0bd0c07df0131f47153306b0736fd3db8924e7a9bf0bed6b1d8d4f87362a71b033dc7c64547728d953e43e59b2@52.64.155.147:30303", - "enode://f4a9c6ee28586009fb5a96c8af13a58ed6d8315a9eee4772212c1d4d9cebe5a8b8a78ea4434f318726317d04a3f531a1ef0420cf9752605a562cfe858c46e263@213.186.16.82:30303", + "enode://b5948a2d3e9d486c4d75bf32713221c2bd6cf86463302339299bd227dc2e276cd5a1c7ca4f43a0e9122fe9af884efed563bd2a1fd28661f3b5f5ad7bf1de5949@18.218.250.66:30303", // Ethereum Foundation bootnode "enode://a61215641fb8714a373c80edbfa0ea8878243193f57c96eeb44d0bc019ef295abd4e044fd619bfc4c59731a73fb79afe84e9ab6da0c743ceb479cbb6d263fa91@3.11.147.67:30303", + + // Goerli Initiative bootnodes + "enode://a869b02cec167211fb4815a82941db2e7ed2936fd90e78619c53eb17753fcf0207463e3419c264e2a1dd8786de0df7e68cf99571ab8aeb7c4e51367ef186b1dd@51.15.116.226:30303", + "enode://807b37ee4816ecf407e9112224494b74dd5933625f655962d892f2f0f02d7fbbb3e2a94cf87a96609526f30c998fd71e93e2f53015c558ffc8b03eceaf30ee33@51.15.119.157:30303", + "enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.15.119.157:40303", } // YoloV1Bootnodes are the enode URLs of the P2P bootstrap nodes running on the diff --git a/params/config.go b/params/config.go index 9b5612f465..2e22bc3008 100644 --- a/params/config.go +++ b/params/config.go @@ -74,10 +74,10 @@ var ( // MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network. MainnetTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 333, - SectionHead: common.HexToHash("0xb80784cbe88077e5911b446765edc814dd67ca3f6bdd33b6ec72d66058df4a11"), - CHTRoot: common.HexToHash("0x4da9cde840dd3de39916620f7a97674c5747a89a9359e6b918e134d199a8dd45"), - BloomRoot: common.HexToHash("0xdd0f4fef7fa2a5cc05d49568e38f15dab24098ffc7677a2e35d1a8d67f5458af"), + SectionIndex: 336, + SectionHead: common.HexToHash("0xd42b78902b6527a80337bf1bc372a3ccc3db97e9cc7cf421ca047ae9076c716b"), + CHTRoot: common.HexToHash("0xd97f3b30f7e0cb958e4c67c53ec27745e5a165e33e56821b86523dfee62b783a"), + BloomRoot: common.HexToHash("0xf3cbfd070fababfe2adc9b23fc02c731f6ca2cce6646b3ede4ef2db06092ccce"), } // MainnetCheckpointOracle contains a set of configs for the main network oracle. @@ -113,10 +113,10 @@ var ( // RopstenTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network. RopstenTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 262, - SectionHead: common.HexToHash("0x12b068f285789b966a983b632266484f1bc93803df6c78773538a5777f57a236"), - CHTRoot: common.HexToHash("0x14000a1407e866f174f3a20fe9f271acd704bcf929b5205d83b70a1bba8c82c2"), - BloomRoot: common.HexToHash("0x2f4f4a34a55e35d0691c79a79e39b6f661259345080fb880da5195c11c2413be"), + SectionIndex: 269, + SectionHead: common.HexToHash("0x290a9eb65e65c64601d1b05522533ed502098a246736b348502a170818a33d64"), + CHTRoot: common.HexToHash("0x530ebac02264227277d0a16b0819ef96a2011a6e1e66523ebff8040f4a3437ca"), + BloomRoot: common.HexToHash("0x480cd5b3198a0767022902130546854a2e8867cce573c1cf0ce54e67a7bf5efb"), } // RopstenCheckpointOracle contains a set of configs for the Ropsten test network oracle. @@ -155,10 +155,10 @@ var ( // RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network. RinkebyTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 220, - SectionHead: common.HexToHash("0x9513befa126a83c96a6408ee8b34502699094a49b2bf1064b2de31b010a03798"), - CHTRoot: common.HexToHash("0x490a17d3bfbfc9bca9de087c5ee9c9f69dc2359cad9c1fe68cab639fdbcfccee"), - BloomRoot: common.HexToHash("0x56bf5fda940ca4ca8346e42ef86f9092c82268c304c03c4093b21c1aa07190fc"), + SectionIndex: 223, + SectionHead: common.HexToHash("0x03ca0d5e3a931c77cd7a97bbaa2d9e4edc4549c621dc1d223a29f10c86a4a16a"), + CHTRoot: common.HexToHash("0x6573dbdd91b2958b446bd04d67c23e5f14b2510ac96e8df1b6a894dc49e37c6c"), + BloomRoot: common.HexToHash("0x28a35042a4e88efbac55fe566faf7fce000dc436f17fd4cb4b081c9cd793e1a7"), } // RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle. @@ -195,10 +195,10 @@ var ( // GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network. GoerliTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 105, - SectionHead: common.HexToHash("0x695f5b67d1985fb13d177c56d20ded0622d7f63a1623959fb4b5c5e38dc6bbee"), - CHTRoot: common.HexToHash("0x4c281ef1ca63e6f9bb4ce8e46e80e478787c91da95c3727550ee418886dd6415"), - BloomRoot: common.HexToHash("0xa02463cc6ee54f12990e9adb019e34696ad1efe2694cf07187d7ce0802cd653d"), + SectionIndex: 107, + SectionHead: common.HexToHash("0xff3ae39199fa191894de419e7f673c8627aa8cc7af924b90f36635b6add375f2"), + CHTRoot: common.HexToHash("0x27d59d60c652425b6b593a882f55a4ff57f24e470a810a6e3c8ba71833a20220"), + BloomRoot: common.HexToHash("0x3c14066d8bb3733780c06b8165768dbb9dd23b75f56012fe5f2fb3c2fb70cadb"), } // GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle. diff --git a/params/protocol_params.go b/params/protocol_params.go index 1a9863d317..9f8c225657 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -57,14 +57,10 @@ const ( NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value - SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed - SstoreNoopGasEIP2200 uint64 = 800 // Once per SSTORE operation if the value doesn't change. - SstoreDirtyGasEIP2200 uint64 = 800 // Once per SSTORE operation if a dirty value is changed. - SstoreInitGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero - SstoreInitRefundEIP2200 uint64 = 19200 // Once per SSTORE operation for resetting to the original zero value - SstoreCleanGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else - SstoreCleanRefundEIP2200 uint64 = 4200 // Once per SSTORE operation for resetting to the original non-zero value - SstoreClearRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed + SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero + SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else + SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot JumpdestGas uint64 = 1 // Once per JUMPDEST operation. EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. diff --git a/params/version.go b/params/version.go index 37ee260fef..bce4de426e 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 9 // Minor version component of the current release - VersionPatch = 22 // Patch version component of the current release + VersionPatch = 23 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string QuorumVersionMajor = 21 diff --git a/permission/v1/bind/accounts.go b/permission/v1/bind/accounts.go index 02940eae44..d57a3d5a24 100644 --- a/permission/v1/bind/accounts.go +++ b/permission/v1/bind/accounts.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindAcctManager(address common.Address, caller bind.ContractCaller, transac // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_AcctManager *AcctManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_AcctManager *AcctManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _AcctManager.Contract.AcctManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_AcctManager *AcctManagerRaw) Transact(opts *bind.TransactOpts, method str // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_AcctManager *AcctManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_AcctManager *AcctManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _AcctManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,202 +192,225 @@ func (_AcctManager *AcctManagerTransactorRaw) Transact(opts *bind.TransactOpts, // CheckOrgAdmin is a free data retrieval call binding the contract method 0xe8b42bf4. // -// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) view returns(bool) func (_AcctManager *AcctManagerCaller) CheckOrgAdmin(opts *bind.CallOpts, _account common.Address, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "checkOrgAdmin", _account, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "checkOrgAdmin", _account, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckOrgAdmin is a free data retrieval call binding the contract method 0xe8b42bf4. // -// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) view returns(bool) func (_AcctManager *AcctManagerSession) CheckOrgAdmin(_account common.Address, _orgId string, _ultParent string) (bool, error) { return _AcctManager.Contract.CheckOrgAdmin(&_AcctManager.CallOpts, _account, _orgId, _ultParent) } // CheckOrgAdmin is a free data retrieval call binding the contract method 0xe8b42bf4. // -// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) view returns(bool) func (_AcctManager *AcctManagerCallerSession) CheckOrgAdmin(_account common.Address, _orgId string, _ultParent string) (bool, error) { return _AcctManager.Contract.CheckOrgAdmin(&_AcctManager.CallOpts, _account, _orgId, _ultParent) } // GetAccountDetails is a free data retrieval call binding the contract method 0x2aceb534. // -// Solidity: function getAccountDetails(address _account) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetails(address _account) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCaller) GetAccountDetails(opts *bind.CallOpts, _account common.Address) (common.Address, string, string, *big.Int, bool, error) { - var ( - ret0 = new(common.Address) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(bool) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, - } - err := _AcctManager.contract.Call(opts, out, "getAccountDetails", _account) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountDetails", _account) + + if err != nil { + return *new(common.Address), *new(string), *new(string), *new(*big.Int), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(bool)).(*bool) + + return out0, out1, out2, out3, out4, err + } // GetAccountDetails is a free data retrieval call binding the contract method 0x2aceb534. // -// Solidity: function getAccountDetails(address _account) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetails(address _account) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerSession) GetAccountDetails(_account common.Address) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetails(&_AcctManager.CallOpts, _account) } // GetAccountDetails is a free data retrieval call binding the contract method 0x2aceb534. // -// Solidity: function getAccountDetails(address _account) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetails(address _account) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCallerSession) GetAccountDetails(_account common.Address) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetails(&_AcctManager.CallOpts, _account) } // GetAccountDetailsFromIndex is a free data retrieval call binding the contract method 0xb2018568. // -// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCaller) GetAccountDetailsFromIndex(opts *bind.CallOpts, _aIndex *big.Int) (common.Address, string, string, *big.Int, bool, error) { - var ( - ret0 = new(common.Address) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(bool) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, - } - err := _AcctManager.contract.Call(opts, out, "getAccountDetailsFromIndex", _aIndex) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountDetailsFromIndex", _aIndex) + + if err != nil { + return *new(common.Address), *new(string), *new(string), *new(*big.Int), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(bool)).(*bool) + + return out0, out1, out2, out3, out4, err + } // GetAccountDetailsFromIndex is a free data retrieval call binding the contract method 0xb2018568. // -// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerSession) GetAccountDetailsFromIndex(_aIndex *big.Int) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetailsFromIndex(&_AcctManager.CallOpts, _aIndex) } // GetAccountDetailsFromIndex is a free data retrieval call binding the contract method 0xb2018568. // -// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCallerSession) GetAccountDetailsFromIndex(_aIndex *big.Int) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetailsFromIndex(&_AcctManager.CallOpts, _aIndex) } // GetAccountRole is a free data retrieval call binding the contract method 0x81d66b23. // -// Solidity: function getAccountRole(address _account) constant returns(string) +// Solidity: function getAccountRole(address _account) view returns(string) func (_AcctManager *AcctManagerCaller) GetAccountRole(opts *bind.CallOpts, _account common.Address) (string, error) { - var ( - ret0 = new(string) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "getAccountRole", _account) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountRole", _account) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + } // GetAccountRole is a free data retrieval call binding the contract method 0x81d66b23. // -// Solidity: function getAccountRole(address _account) constant returns(string) +// Solidity: function getAccountRole(address _account) view returns(string) func (_AcctManager *AcctManagerSession) GetAccountRole(_account common.Address) (string, error) { return _AcctManager.Contract.GetAccountRole(&_AcctManager.CallOpts, _account) } // GetAccountRole is a free data retrieval call binding the contract method 0x81d66b23. // -// Solidity: function getAccountRole(address _account) constant returns(string) +// Solidity: function getAccountRole(address _account) view returns(string) func (_AcctManager *AcctManagerCallerSession) GetAccountRole(_account common.Address) (string, error) { return _AcctManager.Contract.GetAccountRole(&_AcctManager.CallOpts, _account) } // GetNumberOfAccounts is a free data retrieval call binding the contract method 0x309e36ef. // -// Solidity: function getNumberOfAccounts() constant returns(uint256) +// Solidity: function getNumberOfAccounts() view returns(uint256) func (_AcctManager *AcctManagerCaller) GetNumberOfAccounts(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "getNumberOfAccounts") - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getNumberOfAccounts") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfAccounts is a free data retrieval call binding the contract method 0x309e36ef. // -// Solidity: function getNumberOfAccounts() constant returns(uint256) +// Solidity: function getNumberOfAccounts() view returns(uint256) func (_AcctManager *AcctManagerSession) GetNumberOfAccounts() (*big.Int, error) { return _AcctManager.Contract.GetNumberOfAccounts(&_AcctManager.CallOpts) } // GetNumberOfAccounts is a free data retrieval call binding the contract method 0x309e36ef. // -// Solidity: function getNumberOfAccounts() constant returns(uint256) +// Solidity: function getNumberOfAccounts() view returns(uint256) func (_AcctManager *AcctManagerCallerSession) GetNumberOfAccounts() (*big.Int, error) { return _AcctManager.Contract.GetNumberOfAccounts(&_AcctManager.CallOpts) } // OrgAdminExists is a free data retrieval call binding the contract method 0x950145cf. // -// Solidity: function orgAdminExists(string _orgId) constant returns(bool) +// Solidity: function orgAdminExists(string _orgId) view returns(bool) func (_AcctManager *AcctManagerCaller) OrgAdminExists(opts *bind.CallOpts, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "orgAdminExists", _orgId) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "orgAdminExists", _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // OrgAdminExists is a free data retrieval call binding the contract method 0x950145cf. // -// Solidity: function orgAdminExists(string _orgId) constant returns(bool) +// Solidity: function orgAdminExists(string _orgId) view returns(bool) func (_AcctManager *AcctManagerSession) OrgAdminExists(_orgId string) (bool, error) { return _AcctManager.Contract.OrgAdminExists(&_AcctManager.CallOpts, _orgId) } // OrgAdminExists is a free data retrieval call binding the contract method 0x950145cf. // -// Solidity: function orgAdminExists(string _orgId) constant returns(bool) +// Solidity: function orgAdminExists(string _orgId) view returns(bool) func (_AcctManager *AcctManagerCallerSession) OrgAdminExists(_orgId string) (bool, error) { return _AcctManager.Contract.OrgAdminExists(&_AcctManager.CallOpts, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_AcctManager *AcctManagerCaller) ValidateAccount(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "validateAccount", _account, _orgId) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "validateAccount", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_AcctManager *AcctManagerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _AcctManager.Contract.ValidateAccount(&_AcctManager.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_AcctManager *AcctManagerCallerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _AcctManager.Contract.ValidateAccount(&_AcctManager.CallOpts, _account, _orgId) } diff --git a/permission/v1/bind/nodes.go b/permission/v1/bind/nodes.go index 11e607eab1..b06861db5c 100644 --- a/permission/v1/bind/nodes.go +++ b/permission/v1/bind/nodes.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindNodeManager(address common.Address, caller bind.ContractCaller, transac // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_NodeManager *NodeManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_NodeManager *NodeManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _NodeManager.Contract.NodeManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_NodeManager *NodeManagerRaw) Transact(opts *bind.TransactOpts, method str // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_NodeManager *NodeManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_NodeManager *NodeManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _NodeManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,25 +192,32 @@ func (_NodeManager *NodeManagerTransactorRaw) Transact(opts *bind.TransactOpts, // GetNodeDetails is a free data retrieval call binding the contract method 0x3f0e0e47. // -// Solidity: function getNodeDetails(string enodeId) constant returns(string _orgId, string _enodeId, uint256 _nodeStatus) +// Solidity: function getNodeDetails(string enodeId) view returns(string _orgId, string _enodeId, uint256 _nodeStatus) func (_NodeManager *NodeManagerCaller) GetNodeDetails(opts *bind.CallOpts, enodeId string) (struct { OrgId string EnodeId string NodeStatus *big.Int }, error) { - ret := new(struct { + var out []interface{} + err := _NodeManager.contract.Call(opts, &out, "getNodeDetails", enodeId) + + outstruct := new(struct { OrgId string EnodeId string NodeStatus *big.Int }) - out := ret - err := _NodeManager.contract.Call(opts, out, "getNodeDetails", enodeId) - return *ret, err + + outstruct.OrgId = out[0].(string) + outstruct.EnodeId = out[1].(string) + outstruct.NodeStatus = out[2].(*big.Int) + + return *outstruct, err + } // GetNodeDetails is a free data retrieval call binding the contract method 0x3f0e0e47. // -// Solidity: function getNodeDetails(string enodeId) constant returns(string _orgId, string _enodeId, uint256 _nodeStatus) +// Solidity: function getNodeDetails(string enodeId) view returns(string _orgId, string _enodeId, uint256 _nodeStatus) func (_NodeManager *NodeManagerSession) GetNodeDetails(enodeId string) (struct { OrgId string EnodeId string @@ -223,7 +228,7 @@ func (_NodeManager *NodeManagerSession) GetNodeDetails(enodeId string) (struct { // GetNodeDetails is a free data retrieval call binding the contract method 0x3f0e0e47. // -// Solidity: function getNodeDetails(string enodeId) constant returns(string _orgId, string _enodeId, uint256 _nodeStatus) +// Solidity: function getNodeDetails(string enodeId) view returns(string _orgId, string _enodeId, uint256 _nodeStatus) func (_NodeManager *NodeManagerCallerSession) GetNodeDetails(enodeId string) (struct { OrgId string EnodeId string @@ -234,25 +239,32 @@ func (_NodeManager *NodeManagerCallerSession) GetNodeDetails(enodeId string) (st // GetNodeDetailsFromIndex is a free data retrieval call binding the contract method 0x97c07a9b. // -// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) constant returns(string _orgId, string _enodeId, uint256 _nodeStatus) +// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) view returns(string _orgId, string _enodeId, uint256 _nodeStatus) func (_NodeManager *NodeManagerCaller) GetNodeDetailsFromIndex(opts *bind.CallOpts, _nodeIndex *big.Int) (struct { OrgId string EnodeId string NodeStatus *big.Int }, error) { - ret := new(struct { + var out []interface{} + err := _NodeManager.contract.Call(opts, &out, "getNodeDetailsFromIndex", _nodeIndex) + + outstruct := new(struct { OrgId string EnodeId string NodeStatus *big.Int }) - out := ret - err := _NodeManager.contract.Call(opts, out, "getNodeDetailsFromIndex", _nodeIndex) - return *ret, err + + outstruct.OrgId = out[0].(string) + outstruct.EnodeId = out[1].(string) + outstruct.NodeStatus = out[2].(*big.Int) + + return *outstruct, err + } // GetNodeDetailsFromIndex is a free data retrieval call binding the contract method 0x97c07a9b. // -// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) constant returns(string _orgId, string _enodeId, uint256 _nodeStatus) +// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) view returns(string _orgId, string _enodeId, uint256 _nodeStatus) func (_NodeManager *NodeManagerSession) GetNodeDetailsFromIndex(_nodeIndex *big.Int) (struct { OrgId string EnodeId string @@ -263,7 +275,7 @@ func (_NodeManager *NodeManagerSession) GetNodeDetailsFromIndex(_nodeIndex *big. // GetNodeDetailsFromIndex is a free data retrieval call binding the contract method 0x97c07a9b. // -// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) constant returns(string _orgId, string _enodeId, uint256 _nodeStatus) +// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) view returns(string _orgId, string _enodeId, uint256 _nodeStatus) func (_NodeManager *NodeManagerCallerSession) GetNodeDetailsFromIndex(_nodeIndex *big.Int) (struct { OrgId string EnodeId string @@ -274,26 +286,31 @@ func (_NodeManager *NodeManagerCallerSession) GetNodeDetailsFromIndex(_nodeIndex // GetNumberOfNodes is a free data retrieval call binding the contract method 0xb81c806a. // -// Solidity: function getNumberOfNodes() constant returns(uint256) +// Solidity: function getNumberOfNodes() view returns(uint256) func (_NodeManager *NodeManagerCaller) GetNumberOfNodes(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _NodeManager.contract.Call(opts, out, "getNumberOfNodes") - return *ret0, err + var out []interface{} + err := _NodeManager.contract.Call(opts, &out, "getNumberOfNodes") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfNodes is a free data retrieval call binding the contract method 0xb81c806a. // -// Solidity: function getNumberOfNodes() constant returns(uint256) +// Solidity: function getNumberOfNodes() view returns(uint256) func (_NodeManager *NodeManagerSession) GetNumberOfNodes() (*big.Int, error) { return _NodeManager.Contract.GetNumberOfNodes(&_NodeManager.CallOpts) } // GetNumberOfNodes is a free data retrieval call binding the contract method 0xb81c806a. // -// Solidity: function getNumberOfNodes() constant returns(uint256) +// Solidity: function getNumberOfNodes() view returns(uint256) func (_NodeManager *NodeManagerCallerSession) GetNumberOfNodes() (*big.Int, error) { return _NodeManager.Contract.GetNumberOfNodes(&_NodeManager.CallOpts) } diff --git a/permission/v1/bind/org.go b/permission/v1/bind/org.go index 3e84b06990..d6d115866a 100644 --- a/permission/v1/bind/org.go +++ b/permission/v1/bind/org.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindOrgManager(address common.Address, caller bind.ContractCaller, transact // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_OrgManager *OrgManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_OrgManager *OrgManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _OrgManager.Contract.OrgManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_OrgManager *OrgManagerRaw) Transact(opts *bind.TransactOpts, method strin // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_OrgManager *OrgManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_OrgManager *OrgManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _OrgManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,202 +192,225 @@ func (_OrgManager *OrgManagerTransactorRaw) Transact(opts *bind.TransactOpts, me // CheckOrgExists is a free data retrieval call binding the contract method 0xffe40d1d. // -// Solidity: function checkOrgExists(string _orgId) constant returns(bool) +// Solidity: function checkOrgExists(string _orgId) view returns(bool) func (_OrgManager *OrgManagerCaller) CheckOrgExists(opts *bind.CallOpts, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "checkOrgExists", _orgId) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "checkOrgExists", _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckOrgExists is a free data retrieval call binding the contract method 0xffe40d1d. // -// Solidity: function checkOrgExists(string _orgId) constant returns(bool) +// Solidity: function checkOrgExists(string _orgId) view returns(bool) func (_OrgManager *OrgManagerSession) CheckOrgExists(_orgId string) (bool, error) { return _OrgManager.Contract.CheckOrgExists(&_OrgManager.CallOpts, _orgId) } // CheckOrgExists is a free data retrieval call binding the contract method 0xffe40d1d. // -// Solidity: function checkOrgExists(string _orgId) constant returns(bool) +// Solidity: function checkOrgExists(string _orgId) view returns(bool) func (_OrgManager *OrgManagerCallerSession) CheckOrgExists(_orgId string) (bool, error) { return _OrgManager.Contract.CheckOrgExists(&_OrgManager.CallOpts, _orgId) } // CheckOrgStatus is a free data retrieval call binding the contract method 0x8c8642df. // -// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) constant returns(bool) +// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) view returns(bool) func (_OrgManager *OrgManagerCaller) CheckOrgStatus(opts *bind.CallOpts, _orgId string, _orgStatus *big.Int) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "checkOrgStatus", _orgId, _orgStatus) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "checkOrgStatus", _orgId, _orgStatus) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckOrgStatus is a free data retrieval call binding the contract method 0x8c8642df. // -// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) constant returns(bool) +// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) view returns(bool) func (_OrgManager *OrgManagerSession) CheckOrgStatus(_orgId string, _orgStatus *big.Int) (bool, error) { return _OrgManager.Contract.CheckOrgStatus(&_OrgManager.CallOpts, _orgId, _orgStatus) } // CheckOrgStatus is a free data retrieval call binding the contract method 0x8c8642df. // -// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) constant returns(bool) +// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) view returns(bool) func (_OrgManager *OrgManagerCallerSession) CheckOrgStatus(_orgId string, _orgStatus *big.Int) (bool, error) { return _OrgManager.Contract.CheckOrgStatus(&_OrgManager.CallOpts, _orgId, _orgStatus) } // GetNumberOfOrgs is a free data retrieval call binding the contract method 0x7755ebdd. // -// Solidity: function getNumberOfOrgs() constant returns(uint256) +// Solidity: function getNumberOfOrgs() view returns(uint256) func (_OrgManager *OrgManagerCaller) GetNumberOfOrgs(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "getNumberOfOrgs") - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getNumberOfOrgs") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfOrgs is a free data retrieval call binding the contract method 0x7755ebdd. // -// Solidity: function getNumberOfOrgs() constant returns(uint256) +// Solidity: function getNumberOfOrgs() view returns(uint256) func (_OrgManager *OrgManagerSession) GetNumberOfOrgs() (*big.Int, error) { return _OrgManager.Contract.GetNumberOfOrgs(&_OrgManager.CallOpts) } // GetNumberOfOrgs is a free data retrieval call binding the contract method 0x7755ebdd. // -// Solidity: function getNumberOfOrgs() constant returns(uint256) +// Solidity: function getNumberOfOrgs() view returns(uint256) func (_OrgManager *OrgManagerCallerSession) GetNumberOfOrgs() (*big.Int, error) { return _OrgManager.Contract.GetNumberOfOrgs(&_OrgManager.CallOpts) } // GetOrgDetails is a free data retrieval call binding the contract method 0xf4d6d9f5. // -// Solidity: function getOrgDetails(string _orgId) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgDetails(string _orgId) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCaller) GetOrgDetails(opts *bind.CallOpts, _orgId string) (string, string, string, *big.Int, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getOrgDetails", _orgId) + + if err != nil { + return *new(string), *new(string), *new(string), *new(*big.Int), *new(*big.Int), err } - err := _OrgManager.contract.Call(opts, out, "getOrgDetails", _orgId) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, out4, err + } // GetOrgDetails is a free data retrieval call binding the contract method 0xf4d6d9f5. // -// Solidity: function getOrgDetails(string _orgId) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgDetails(string _orgId) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerSession) GetOrgDetails(_orgId string) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgDetails(&_OrgManager.CallOpts, _orgId) } // GetOrgDetails is a free data retrieval call binding the contract method 0xf4d6d9f5. // -// Solidity: function getOrgDetails(string _orgId) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgDetails(string _orgId) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCallerSession) GetOrgDetails(_orgId string) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgDetails(&_OrgManager.CallOpts, _orgId) } // GetOrgInfo is a free data retrieval call binding the contract method 0x5c4f32ee. // -// Solidity: function getOrgInfo(uint256 _orgIndex) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgInfo(uint256 _orgIndex) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCaller) GetOrgInfo(opts *bind.CallOpts, _orgIndex *big.Int) (string, string, string, *big.Int, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getOrgInfo", _orgIndex) + + if err != nil { + return *new(string), *new(string), *new(string), *new(*big.Int), *new(*big.Int), err } - err := _OrgManager.contract.Call(opts, out, "getOrgInfo", _orgIndex) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, out4, err + } // GetOrgInfo is a free data retrieval call binding the contract method 0x5c4f32ee. // -// Solidity: function getOrgInfo(uint256 _orgIndex) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgInfo(uint256 _orgIndex) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerSession) GetOrgInfo(_orgIndex *big.Int) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgInfo(&_OrgManager.CallOpts, _orgIndex) } // GetOrgInfo is a free data retrieval call binding the contract method 0x5c4f32ee. // -// Solidity: function getOrgInfo(uint256 _orgIndex) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgInfo(uint256 _orgIndex) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCallerSession) GetOrgInfo(_orgIndex *big.Int) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgInfo(&_OrgManager.CallOpts, _orgIndex) } // GetSubOrgIndexes is a free data retrieval call binding the contract method 0x5e99f6e5. // -// Solidity: function getSubOrgIndexes(string _orgId) constant returns(uint256[]) +// Solidity: function getSubOrgIndexes(string _orgId) view returns(uint256[]) func (_OrgManager *OrgManagerCaller) GetSubOrgIndexes(opts *bind.CallOpts, _orgId string) ([]*big.Int, error) { - var ( - ret0 = new([]*big.Int) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "getSubOrgIndexes", _orgId) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getSubOrgIndexes", _orgId) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + } // GetSubOrgIndexes is a free data retrieval call binding the contract method 0x5e99f6e5. // -// Solidity: function getSubOrgIndexes(string _orgId) constant returns(uint256[]) +// Solidity: function getSubOrgIndexes(string _orgId) view returns(uint256[]) func (_OrgManager *OrgManagerSession) GetSubOrgIndexes(_orgId string) ([]*big.Int, error) { return _OrgManager.Contract.GetSubOrgIndexes(&_OrgManager.CallOpts, _orgId) } // GetSubOrgIndexes is a free data retrieval call binding the contract method 0x5e99f6e5. // -// Solidity: function getSubOrgIndexes(string _orgId) constant returns(uint256[]) +// Solidity: function getSubOrgIndexes(string _orgId) view returns(uint256[]) func (_OrgManager *OrgManagerCallerSession) GetSubOrgIndexes(_orgId string) ([]*big.Int, error) { return _OrgManager.Contract.GetSubOrgIndexes(&_OrgManager.CallOpts, _orgId) } // GetUltimateParent is a free data retrieval call binding the contract method 0x177c8d8a. // -// Solidity: function getUltimateParent(string _orgId) constant returns(string) +// Solidity: function getUltimateParent(string _orgId) view returns(string) func (_OrgManager *OrgManagerCaller) GetUltimateParent(opts *bind.CallOpts, _orgId string) (string, error) { - var ( - ret0 = new(string) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "getUltimateParent", _orgId) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getUltimateParent", _orgId) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + } // GetUltimateParent is a free data retrieval call binding the contract method 0x177c8d8a. // -// Solidity: function getUltimateParent(string _orgId) constant returns(string) +// Solidity: function getUltimateParent(string _orgId) view returns(string) func (_OrgManager *OrgManagerSession) GetUltimateParent(_orgId string) (string, error) { return _OrgManager.Contract.GetUltimateParent(&_OrgManager.CallOpts, _orgId) } // GetUltimateParent is a free data retrieval call binding the contract method 0x177c8d8a. // -// Solidity: function getUltimateParent(string _orgId) constant returns(string) +// Solidity: function getUltimateParent(string _orgId) view returns(string) func (_OrgManager *OrgManagerCallerSession) GetUltimateParent(_orgId string) (string, error) { return _OrgManager.Contract.GetUltimateParent(&_OrgManager.CallOpts, _orgId) } diff --git a/permission/v1/bind/permission_impl.go b/permission/v1/bind/permission_impl.go index 2f488be8f8..53df6517cf 100644 --- a/permission/v1/bind/permission_impl.go +++ b/permission/v1/bind/permission_impl.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindPermImpl(address common.Address, caller bind.ContractCaller, transactor // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermImpl *PermImplRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermImpl *PermImplRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermImpl.Contract.PermImplCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_PermImpl *PermImplRaw) Transact(opts *bind.TransactOpts, method string, p // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermImpl *PermImplCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermImpl *PermImplCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermImpl.Contract.contract.Call(opts, result, method, params...) } @@ -194,172 +192,192 @@ func (_PermImpl *PermImplTransactorRaw) Transact(opts *bind.TransactOpts, method // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermImpl *PermImplCaller) GetNetworkBootStatus(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "getNetworkBootStatus") - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "getNetworkBootStatus") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermImpl *PermImplSession) GetNetworkBootStatus() (bool, error) { return _PermImpl.Contract.GetNetworkBootStatus(&_PermImpl.CallOpts) } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermImpl *PermImplCallerSession) GetNetworkBootStatus() (bool, error) { return _PermImpl.Contract.GetNetworkBootStatus(&_PermImpl.CallOpts) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermImpl *PermImplCaller) GetPendingOp(opts *bind.CallOpts, _orgId string) (string, string, common.Address, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(common.Address) - ret3 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "getPendingOp", _orgId) + + if err != nil { + return *new(string), *new(string), *new(common.Address), *new(*big.Int), err } - err := _PermImpl.contract.Call(opts, out, "getPendingOp", _orgId) - return *ret0, *ret1, *ret2, *ret3, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, err + } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermImpl *PermImplSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermImpl.Contract.GetPendingOp(&_PermImpl.CallOpts, _orgId) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermImpl *PermImplCallerSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermImpl.Contract.GetPendingOp(&_PermImpl.CallOpts, _orgId) } // GetPolicyDetails is a free data retrieval call binding the contract method 0xcc9ba6fa. // -// Solidity: function getPolicyDetails() constant returns(string, string, string, bool) +// Solidity: function getPolicyDetails() view returns(string, string, string, bool) func (_PermImpl *PermImplCaller) GetPolicyDetails(opts *bind.CallOpts) (string, string, string, bool, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(string) - ret3 = new(bool) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "getPolicyDetails") + + if err != nil { + return *new(string), *new(string), *new(string), *new(bool), err } - err := _PermImpl.contract.Call(opts, out, "getPolicyDetails") - return *ret0, *ret1, *ret2, *ret3, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(bool)).(*bool) + + return out0, out1, out2, out3, err + } // GetPolicyDetails is a free data retrieval call binding the contract method 0xcc9ba6fa. // -// Solidity: function getPolicyDetails() constant returns(string, string, string, bool) +// Solidity: function getPolicyDetails() view returns(string, string, string, bool) func (_PermImpl *PermImplSession) GetPolicyDetails() (string, string, string, bool, error) { return _PermImpl.Contract.GetPolicyDetails(&_PermImpl.CallOpts) } // GetPolicyDetails is a free data retrieval call binding the contract method 0xcc9ba6fa. // -// Solidity: function getPolicyDetails() constant returns(string, string, string, bool) +// Solidity: function getPolicyDetails() view returns(string, string, string, bool) func (_PermImpl *PermImplCallerSession) GetPolicyDetails() (string, string, string, bool, error) { return _PermImpl.Contract.GetPolicyDetails(&_PermImpl.CallOpts) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermImpl *PermImplCaller) IsNetworkAdmin(opts *bind.CallOpts, _account common.Address) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "isNetworkAdmin", _account) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "isNetworkAdmin", _account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermImpl *PermImplSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermImpl.Contract.IsNetworkAdmin(&_PermImpl.CallOpts, _account) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermImpl *PermImplCallerSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermImpl.Contract.IsNetworkAdmin(&_PermImpl.CallOpts, _account) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCaller) IsOrgAdmin(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "isOrgAdmin", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "isOrgAdmin", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.IsOrgAdmin(&_PermImpl.CallOpts, _account, _orgId) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCallerSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.IsOrgAdmin(&_PermImpl.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCaller) ValidateAccount(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "validateAccount", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "validateAccount", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.ValidateAccount(&_PermImpl.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCallerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.ValidateAccount(&_PermImpl.CallOpts, _account, _orgId) } diff --git a/permission/v1/bind/permission_interface.go b/permission/v1/bind/permission_interface.go index 39468af8ef..a8da143baa 100644 --- a/permission/v1/bind/permission_interface.go +++ b/permission/v1/bind/permission_interface.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindPermInterface(address common.Address, caller bind.ContractCaller, trans // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermInterface *PermInterfaceRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermInterface *PermInterfaceRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermInterface.Contract.PermInterfaceCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_PermInterface *PermInterfaceRaw) Transact(opts *bind.TransactOpts, method // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermInterface *PermInterfaceCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermInterface *PermInterfaceCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermInterface.Contract.contract.Call(opts, result, method, params...) } @@ -194,164 +192,189 @@ func (_PermInterface *PermInterfaceTransactorRaw) Transact(opts *bind.TransactOp // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermInterface *PermInterfaceCaller) GetNetworkBootStatus(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "getNetworkBootStatus") - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "getNetworkBootStatus") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermInterface *PermInterfaceSession) GetNetworkBootStatus() (bool, error) { return _PermInterface.Contract.GetNetworkBootStatus(&_PermInterface.CallOpts) } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermInterface *PermInterfaceCallerSession) GetNetworkBootStatus() (bool, error) { return _PermInterface.Contract.GetNetworkBootStatus(&_PermInterface.CallOpts) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermInterface *PermInterfaceCaller) GetPendingOp(opts *bind.CallOpts, _orgId string) (string, string, common.Address, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(common.Address) - ret3 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "getPendingOp", _orgId) + + if err != nil { + return *new(string), *new(string), *new(common.Address), *new(*big.Int), err } - err := _PermInterface.contract.Call(opts, out, "getPendingOp", _orgId) - return *ret0, *ret1, *ret2, *ret3, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, err + } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermInterface *PermInterfaceSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermInterface.Contract.GetPendingOp(&_PermInterface.CallOpts, _orgId) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermInterface *PermInterfaceCallerSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermInterface.Contract.GetPendingOp(&_PermInterface.CallOpts, _orgId) } // GetPermissionsImpl is a free data retrieval call binding the contract method 0x03ed6933. // -// Solidity: function getPermissionsImpl() constant returns(address) +// Solidity: function getPermissionsImpl() view returns(address) func (_PermInterface *PermInterfaceCaller) GetPermissionsImpl(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "getPermissionsImpl") - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "getPermissionsImpl") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetPermissionsImpl is a free data retrieval call binding the contract method 0x03ed6933. // -// Solidity: function getPermissionsImpl() constant returns(address) +// Solidity: function getPermissionsImpl() view returns(address) func (_PermInterface *PermInterfaceSession) GetPermissionsImpl() (common.Address, error) { return _PermInterface.Contract.GetPermissionsImpl(&_PermInterface.CallOpts) } // GetPermissionsImpl is a free data retrieval call binding the contract method 0x03ed6933. // -// Solidity: function getPermissionsImpl() constant returns(address) +// Solidity: function getPermissionsImpl() view returns(address) func (_PermInterface *PermInterfaceCallerSession) GetPermissionsImpl() (common.Address, error) { return _PermInterface.Contract.GetPermissionsImpl(&_PermInterface.CallOpts) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermInterface *PermInterfaceCaller) IsNetworkAdmin(opts *bind.CallOpts, _account common.Address) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "isNetworkAdmin", _account) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "isNetworkAdmin", _account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermInterface *PermInterfaceSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermInterface.Contract.IsNetworkAdmin(&_PermInterface.CallOpts, _account) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermInterface.Contract.IsNetworkAdmin(&_PermInterface.CallOpts, _account) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCaller) IsOrgAdmin(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "isOrgAdmin", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "isOrgAdmin", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.IsOrgAdmin(&_PermInterface.CallOpts, _account, _orgId) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.IsOrgAdmin(&_PermInterface.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCaller) ValidateAccount(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "validateAccount", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "validateAccount", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.ValidateAccount(&_PermInterface.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.ValidateAccount(&_PermInterface.CallOpts, _account, _orgId) } diff --git a/permission/v1/bind/permission_upgr.go b/permission/v1/bind/permission_upgr.go index 6f8de04912..f03d2298e5 100644 --- a/permission/v1/bind/permission_upgr.go +++ b/permission/v1/bind/permission_upgr.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -34,7 +32,7 @@ const PermUpgrABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"getPermImpl\", var PermUpgrParsedABI, _ = abi.JSON(strings.NewReader(PermUpgrABI)) // PermUpgrBin is the compiled bytecode used for deploying new contracts. -var PermUpgrBin = "0x608060405234801561001057600080fd5b506040516020806106e78339810180604052602081101561003057600080fd5b5051600080546001600160a01b039092166001600160a01b031990921691909117905560028054600160a01b60ff0219169055610675806100726000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80630e32cf901461005c57806322bcb39a14610080578063a75b87d2146100a8578063e572515c146100b0578063f09a4016146100b8575b600080fd5b6100646100e6565b604080516001600160a01b039092168252519081900360200190f35b6100a66004803603602081101561009657600080fd5b50356001600160a01b03166100f5565b005b61006461030b565b61006461031a565b6100a6600480360360408110156100ce57600080fd5b506001600160a01b0381358116916020013516610329565b6001546001600160a01b031690565b6000546001600160a01b0316331461014b5760408051600160e51b62461bcd02815260206004820152600e6024820152600160911b6d34b73b30b634b21031b0b63632b902604482015290519081900360640190fd5b60608060606000600160009054906101000a90046001600160a01b03166001600160a01b031663cc9ba6fa6040518163ffffffff1660e01b815260040160006040518083038186803b1580156101a057600080fd5b505afa1580156101b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260808110156101dd57600080fd5b8101908080516401000000008111156101f557600080fd5b8201602081018481111561020857600080fd5b815164010000000081118282018710171561022257600080fd5b5050929190602001805164010000000081111561023e57600080fd5b8201602081018481111561025157600080fd5b815164010000000081118282018710171561026b57600080fd5b5050929190602001805164010000000081111561028757600080fd5b8201602081018481111561029a57600080fd5b81516401000000008111828201871017156102b457600080fd5b50506020909101519498509296509194509192506102d9915086905085858585610443565b600180546001600160a01b0319166001600160a01b03878116919091179182905561030491166105e4565b5050505050565b6000546001600160a01b031690565b6002546001600160a01b031690565b6000546001600160a01b0316331461037f5760408051600160e51b62461bcd02815260206004820152600e6024820152600160911b6d34b73b30b634b21031b0b63632b902604482015290519081900360640190fd5b600254600160a01b900460ff16156103e15760408051600160e51b62461bcd02815260206004820152601960248201527f63616e206265206578656375746564206f6e6c79206f6e636500000000000000604482015290519081900360640190fd5b600180546001600160a01b038084166001600160a01b031992831617928390556002805486831693169290921790915561041b91166105e4565b50506002805474ff00000000000000000000000000000000000000001916600160a01b179055565b846001600160a01b031663f5ad584a858585856040518563ffffffff1660e01b81526004018080602001806020018060200185151515158152602001848103845288818151815260200191508051906020019080838360005b838110156104b457818101518382015260200161049c565b50505050905090810190601f1680156104e15780820380516001836020036101000a031916815260200191505b50848103835287518152875160209182019189019080838360005b838110156105145781810151838201526020016104fc565b50505050905090810190601f1680156105415780820380516001836020036101000a031916815260200191505b50848103825286518152865160209182019188019080838360005b8381101561057457818101518382015260200161055c565b50505050905090810190601f1680156105a15780820380516001836020036101000a031916815260200191505b50975050505050505050600060405180830381600087803b1580156105c557600080fd5b505af11580156105d9573d6000803e3d6000fd5b505050505050505050565b60025460408051600160e01b63511bbd9f0281526001600160a01b0384811660048301529151919092169163511bbd9f91602480830192600092919082900301818387803b15801561063557600080fd5b505af1158015610304573d6000803e3d6000fdfea165627a7a72305820baed98682426c4ca5713954d4aec5ce8f78637bbf627bd8f53ff37aac2394a950029" +var PermUpgrBin = "0x608060405234801561001057600080fd5b50604051610bfa380380610bfa8339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260146101000a81548160ff02191690831515021790555050610b4b806100af6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80630e32cf901461005c57806322bcb39a146100a6578063a75b87d2146100ea578063e572515c14610134578063f09a40161461017e575b600080fd5b6100646101e2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e8600480360360208110156100bc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061020c565b005b6100f2610639565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61013c610662565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101e06004803603604081101561019457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061068c565b005b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146102ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f696e76616c69642063616c6c657200000000000000000000000000000000000081525060200191505060405180910390fd5b60608060606000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663cc9ba6fa6040518163ffffffff1660e01b815260040160006040518083038186803b15801561033d57600080fd5b505afa158015610351573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250608081101561037b57600080fd5b810190808051604051939291908464010000000082111561039b57600080fd5b838201915060208201858111156103b157600080fd5b82518660018202830111640100000000821117156103ce57600080fd5b8083526020830192505050908051906020019080838360005b838110156104025780820151818401526020810190506103e7565b50505050905090810190601f16801561042f5780820380516001836020036101000a031916815260200191505b506040526020018051604051939291908464010000000082111561045257600080fd5b8382019150602082018581111561046857600080fd5b825186600182028301116401000000008211171561048557600080fd5b8083526020830192505050908051906020019080838360005b838110156104b957808201518184015260208101905061049e565b50505050905090810190601f1680156104e65780820380516001836020036101000a031916815260200191505b506040526020018051604051939291908464010000000082111561050957600080fd5b8382019150602082018581111561051f57600080fd5b825186600182028301116401000000008211171561053c57600080fd5b8083526020830192505050908051906020019080838360005b83811015610570578082015181840152602081019050610555565b50505050905090810190601f16801561059d5780820380516001836020036101000a031916815260200191505b506040526020018051906020019092919050505093509350935093506105c6858585858561089d565b84600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610632600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610a5a565b5050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461074e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f696e76616c69642063616c6c657200000000000000000000000000000000000081525060200191505060405180910390fd5b600260149054906101000a900460ff16156107d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f63616e206265206578656375746564206f6e6c79206f6e63650000000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061087e600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610a5a565b6001600260146101000a81548160ff0219169083151502179055505050565b8473ffffffffffffffffffffffffffffffffffffffff1663f5ad584a858585856040518563ffffffff1660e01b81526004018080602001806020018060200185151515158152602001848103845288818151815260200191508051906020019080838360005b8381101561091e578082015181840152602081019050610903565b50505050905090810190601f16801561094b5780820380516001836020036101000a031916815260200191505b50848103835287818151815260200191508051906020019080838360005b83811015610984578082015181840152602081019050610969565b50505050905090810190601f1680156109b15780820380516001836020036101000a031916815260200191505b50848103825286818151815260200191508051906020019080838360005b838110156109ea5780820151818401526020810190506109cf565b50505050905090810190601f168015610a175780820380516001836020036101000a031916815260200191505b50975050505050505050600060405180830381600087803b158015610a3b57600080fd5b505af1158015610a4f573d6000803e3d6000fd5b505050505050505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663511bbd9f826040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050600060405180830381600087803b158015610afb57600080fd5b505af1158015610b0f573d6000803e3d6000fd5b505050505056fea265627a7a72315820316b667b04f5e53cd6b4085849e4dd3299ffca85b1af531372c992cf4c25dde464736f6c63430005110032" // DeployPermUpgr deploys a new Ethereum contract, binding an instance of PermUpgr to it. func DeployPermUpgr(auth *bind.TransactOpts, backend bind.ContractBackend, _guardian common.Address) (common.Address, *types.Transaction, *PermUpgr, error) { @@ -158,7 +156,7 @@ func bindPermUpgr(address common.Address, caller bind.ContractCaller, transactor // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermUpgr *PermUpgrRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermUpgr *PermUpgrRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermUpgr.Contract.PermUpgrCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_PermUpgr *PermUpgrRaw) Transact(opts *bind.TransactOpts, method string, p // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermUpgr *PermUpgrCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermUpgr *PermUpgrCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermUpgr.Contract.contract.Call(opts, result, method, params...) } @@ -194,78 +192,93 @@ func (_PermUpgr *PermUpgrTransactorRaw) Transact(opts *bind.TransactOpts, method // GetGuardian is a free data retrieval call binding the contract method 0xa75b87d2. // -// Solidity: function getGuardian() constant returns(address) +// Solidity: function getGuardian() view returns(address) func (_PermUpgr *PermUpgrCaller) GetGuardian(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermUpgr.contract.Call(opts, out, "getGuardian") - return *ret0, err + var out []interface{} + err := _PermUpgr.contract.Call(opts, &out, "getGuardian") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetGuardian is a free data retrieval call binding the contract method 0xa75b87d2. // -// Solidity: function getGuardian() constant returns(address) +// Solidity: function getGuardian() view returns(address) func (_PermUpgr *PermUpgrSession) GetGuardian() (common.Address, error) { return _PermUpgr.Contract.GetGuardian(&_PermUpgr.CallOpts) } // GetGuardian is a free data retrieval call binding the contract method 0xa75b87d2. // -// Solidity: function getGuardian() constant returns(address) +// Solidity: function getGuardian() view returns(address) func (_PermUpgr *PermUpgrCallerSession) GetGuardian() (common.Address, error) { return _PermUpgr.Contract.GetGuardian(&_PermUpgr.CallOpts) } // GetPermImpl is a free data retrieval call binding the contract method 0x0e32cf90. // -// Solidity: function getPermImpl() constant returns(address) +// Solidity: function getPermImpl() view returns(address) func (_PermUpgr *PermUpgrCaller) GetPermImpl(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermUpgr.contract.Call(opts, out, "getPermImpl") - return *ret0, err + var out []interface{} + err := _PermUpgr.contract.Call(opts, &out, "getPermImpl") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetPermImpl is a free data retrieval call binding the contract method 0x0e32cf90. // -// Solidity: function getPermImpl() constant returns(address) +// Solidity: function getPermImpl() view returns(address) func (_PermUpgr *PermUpgrSession) GetPermImpl() (common.Address, error) { return _PermUpgr.Contract.GetPermImpl(&_PermUpgr.CallOpts) } // GetPermImpl is a free data retrieval call binding the contract method 0x0e32cf90. // -// Solidity: function getPermImpl() constant returns(address) +// Solidity: function getPermImpl() view returns(address) func (_PermUpgr *PermUpgrCallerSession) GetPermImpl() (common.Address, error) { return _PermUpgr.Contract.GetPermImpl(&_PermUpgr.CallOpts) } // GetPermInterface is a free data retrieval call binding the contract method 0xe572515c. // -// Solidity: function getPermInterface() constant returns(address) +// Solidity: function getPermInterface() view returns(address) func (_PermUpgr *PermUpgrCaller) GetPermInterface(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermUpgr.contract.Call(opts, out, "getPermInterface") - return *ret0, err + var out []interface{} + err := _PermUpgr.contract.Call(opts, &out, "getPermInterface") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetPermInterface is a free data retrieval call binding the contract method 0xe572515c. // -// Solidity: function getPermInterface() constant returns(address) +// Solidity: function getPermInterface() view returns(address) func (_PermUpgr *PermUpgrSession) GetPermInterface() (common.Address, error) { return _PermUpgr.Contract.GetPermInterface(&_PermUpgr.CallOpts) } // GetPermInterface is a free data retrieval call binding the contract method 0xe572515c. // -// Solidity: function getPermInterface() constant returns(address) +// Solidity: function getPermInterface() view returns(address) func (_PermUpgr *PermUpgrCallerSession) GetPermInterface() (common.Address, error) { return _PermUpgr.Contract.GetPermInterface(&_PermUpgr.CallOpts) } diff --git a/permission/v1/bind/roles.go b/permission/v1/bind/roles.go index 1e05bbe3e7..5e8fa5b647 100644 --- a/permission/v1/bind/roles.go +++ b/permission/v1/bind/roles.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindRoleManager(address common.Address, caller bind.ContractCaller, transac // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_RoleManager *RoleManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_RoleManager *RoleManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _RoleManager.Contract.RoleManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_RoleManager *RoleManagerRaw) Transact(opts *bind.TransactOpts, method str // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_RoleManager *RoleManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_RoleManager *RoleManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _RoleManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,33 +192,38 @@ func (_RoleManager *RoleManagerTransactorRaw) Transact(opts *bind.TransactOpts, // GetNumberOfRoles is a free data retrieval call binding the contract method 0x87f55d31. // -// Solidity: function getNumberOfRoles() constant returns(uint256) +// Solidity: function getNumberOfRoles() view returns(uint256) func (_RoleManager *RoleManagerCaller) GetNumberOfRoles(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "getNumberOfRoles") - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "getNumberOfRoles") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfRoles is a free data retrieval call binding the contract method 0x87f55d31. // -// Solidity: function getNumberOfRoles() constant returns(uint256) +// Solidity: function getNumberOfRoles() view returns(uint256) func (_RoleManager *RoleManagerSession) GetNumberOfRoles() (*big.Int, error) { return _RoleManager.Contract.GetNumberOfRoles(&_RoleManager.CallOpts) } // GetNumberOfRoles is a free data retrieval call binding the contract method 0x87f55d31. // -// Solidity: function getNumberOfRoles() constant returns(uint256) +// Solidity: function getNumberOfRoles() view returns(uint256) func (_RoleManager *RoleManagerCallerSession) GetNumberOfRoles() (*big.Int, error) { return _RoleManager.Contract.GetNumberOfRoles(&_RoleManager.CallOpts) } // GetRoleDetails is a free data retrieval call binding the contract method 0x1870aba3. // -// Solidity: function getRoleDetails(string _roleId, string _orgId) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetails(string _roleId, string _orgId) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCaller) GetRoleDetails(opts *bind.CallOpts, _roleId string, _orgId string) (struct { RoleId string OrgId string @@ -229,7 +232,10 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetails(opts *bind.CallOpts, _role Admin bool Active bool }, error) { - ret := new(struct { + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "getRoleDetails", _roleId, _orgId) + + outstruct := new(struct { RoleId string OrgId string AccessType *big.Int @@ -237,14 +243,21 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetails(opts *bind.CallOpts, _role Admin bool Active bool }) - out := ret - err := _RoleManager.contract.Call(opts, out, "getRoleDetails", _roleId, _orgId) - return *ret, err + + outstruct.RoleId = out[0].(string) + outstruct.OrgId = out[1].(string) + outstruct.AccessType = out[2].(*big.Int) + outstruct.Voter = out[3].(bool) + outstruct.Admin = out[4].(bool) + outstruct.Active = out[5].(bool) + + return *outstruct, err + } // GetRoleDetails is a free data retrieval call binding the contract method 0x1870aba3. // -// Solidity: function getRoleDetails(string _roleId, string _orgId) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetails(string _roleId, string _orgId) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerSession) GetRoleDetails(_roleId string, _orgId string) (struct { RoleId string OrgId string @@ -258,7 +271,7 @@ func (_RoleManager *RoleManagerSession) GetRoleDetails(_roleId string, _orgId st // GetRoleDetails is a free data retrieval call binding the contract method 0x1870aba3. // -// Solidity: function getRoleDetails(string _roleId, string _orgId) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetails(string _roleId, string _orgId) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCallerSession) GetRoleDetails(_roleId string, _orgId string) (struct { RoleId string OrgId string @@ -272,7 +285,7 @@ func (_RoleManager *RoleManagerCallerSession) GetRoleDetails(_roleId string, _or // GetRoleDetailsFromIndex is a free data retrieval call binding the contract method 0xa451d4a8. // -// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCaller) GetRoleDetailsFromIndex(opts *bind.CallOpts, _rIndex *big.Int) (struct { RoleId string OrgId string @@ -281,7 +294,10 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetailsFromIndex(opts *bind.CallOp Admin bool Active bool }, error) { - ret := new(struct { + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "getRoleDetailsFromIndex", _rIndex) + + outstruct := new(struct { RoleId string OrgId string AccessType *big.Int @@ -289,14 +305,21 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetailsFromIndex(opts *bind.CallOp Admin bool Active bool }) - out := ret - err := _RoleManager.contract.Call(opts, out, "getRoleDetailsFromIndex", _rIndex) - return *ret, err + + outstruct.RoleId = out[0].(string) + outstruct.OrgId = out[1].(string) + outstruct.AccessType = out[2].(*big.Int) + outstruct.Voter = out[3].(bool) + outstruct.Admin = out[4].(bool) + outstruct.Active = out[5].(bool) + + return *outstruct, err + } // GetRoleDetailsFromIndex is a free data retrieval call binding the contract method 0xa451d4a8. // -// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerSession) GetRoleDetailsFromIndex(_rIndex *big.Int) (struct { RoleId string OrgId string @@ -310,7 +333,7 @@ func (_RoleManager *RoleManagerSession) GetRoleDetailsFromIndex(_rIndex *big.Int // GetRoleDetailsFromIndex is a free data retrieval call binding the contract method 0xa451d4a8. // -// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCallerSession) GetRoleDetailsFromIndex(_rIndex *big.Int) (struct { RoleId string OrgId string @@ -324,78 +347,93 @@ func (_RoleManager *RoleManagerCallerSession) GetRoleDetailsFromIndex(_rIndex *b // IsAdminRole is a free data retrieval call binding the contract method 0xbe322e54. // -// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCaller) IsAdminRole(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "isAdminRole", _roleId, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "isAdminRole", _roleId, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsAdminRole is a free data retrieval call binding the contract method 0xbe322e54. // -// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerSession) IsAdminRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsAdminRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // IsAdminRole is a free data retrieval call binding the contract method 0xbe322e54. // -// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCallerSession) IsAdminRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsAdminRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // IsVoterRole is a free data retrieval call binding the contract method 0xdeb16ba7. // -// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCaller) IsVoterRole(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "isVoterRole", _roleId, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "isVoterRole", _roleId, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsVoterRole is a free data retrieval call binding the contract method 0xdeb16ba7. // -// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerSession) IsVoterRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsVoterRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // IsVoterRole is a free data retrieval call binding the contract method 0xdeb16ba7. // -// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCallerSession) IsVoterRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsVoterRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // RoleExists is a free data retrieval call binding the contract method 0xabf5739f. // -// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCaller) RoleExists(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "roleExists", _roleId, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "roleExists", _roleId, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // RoleExists is a free data retrieval call binding the contract method 0xabf5739f. // -// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerSession) RoleExists(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.RoleExists(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // RoleExists is a free data retrieval call binding the contract method 0xabf5739f. // -// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCallerSession) RoleExists(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.RoleExists(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } diff --git a/permission/v1/bind/voter.go b/permission/v1/bind/voter.go index 8c5835206d..3c31bfb46b 100644 --- a/permission/v1/bind/voter.go +++ b/permission/v1/bind/voter.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindVoterManager(address common.Address, caller bind.ContractCaller, transa // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_VoterManager *VoterManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_VoterManager *VoterManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _VoterManager.Contract.VoterManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_VoterManager *VoterManagerRaw) Transact(opts *bind.TransactOpts, method s // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_VoterManager *VoterManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_VoterManager *VoterManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _VoterManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,34 +192,34 @@ func (_VoterManager *VoterManagerTransactorRaw) Transact(opts *bind.TransactOpts // GetPendingOpDetails is a free data retrieval call binding the contract method 0x014e6acc. // -// Solidity: function getPendingOpDetails(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOpDetails(string _orgId) view returns(string, string, address, uint256) func (_VoterManager *VoterManagerCaller) GetPendingOpDetails(opts *bind.CallOpts, _orgId string) (string, string, common.Address, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(common.Address) - ret3 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - } - err := _VoterManager.contract.Call(opts, out, "getPendingOpDetails", _orgId) - return *ret0, *ret1, *ret2, *ret3, err + var out []interface{} + err := _VoterManager.contract.Call(opts, &out, "getPendingOpDetails", _orgId) + + if err != nil { + return *new(string), *new(string), *new(common.Address), *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, err + } // GetPendingOpDetails is a free data retrieval call binding the contract method 0x014e6acc. // -// Solidity: function getPendingOpDetails(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOpDetails(string _orgId) view returns(string, string, address, uint256) func (_VoterManager *VoterManagerSession) GetPendingOpDetails(_orgId string) (string, string, common.Address, *big.Int, error) { return _VoterManager.Contract.GetPendingOpDetails(&_VoterManager.CallOpts, _orgId) } // GetPendingOpDetails is a free data retrieval call binding the contract method 0x014e6acc. // -// Solidity: function getPendingOpDetails(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOpDetails(string _orgId) view returns(string, string, address, uint256) func (_VoterManager *VoterManagerCallerSession) GetPendingOpDetails(_orgId string) (string, string, common.Address, *big.Int, error) { return _VoterManager.Contract.GetPendingOpDetails(&_VoterManager.CallOpts, _orgId) } diff --git a/permission/v2/bind/accounts.go b/permission/v2/bind/accounts.go index 8af01882c2..4c572d2d18 100644 --- a/permission/v2/bind/accounts.go +++ b/permission/v2/bind/accounts.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindAcctManager(address common.Address, caller bind.ContractCaller, transac // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_AcctManager *AcctManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_AcctManager *AcctManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _AcctManager.Contract.AcctManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_AcctManager *AcctManagerRaw) Transact(opts *bind.TransactOpts, method str // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_AcctManager *AcctManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_AcctManager *AcctManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _AcctManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,258 +192,288 @@ func (_AcctManager *AcctManagerTransactorRaw) Transact(opts *bind.TransactOpts, // CheckOrgAdmin is a free data retrieval call binding the contract method 0xe8b42bf4. // -// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) view returns(bool) func (_AcctManager *AcctManagerCaller) CheckOrgAdmin(opts *bind.CallOpts, _account common.Address, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "checkOrgAdmin", _account, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "checkOrgAdmin", _account, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckOrgAdmin is a free data retrieval call binding the contract method 0xe8b42bf4. // -// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) view returns(bool) func (_AcctManager *AcctManagerSession) CheckOrgAdmin(_account common.Address, _orgId string, _ultParent string) (bool, error) { return _AcctManager.Contract.CheckOrgAdmin(&_AcctManager.CallOpts, _account, _orgId, _ultParent) } // CheckOrgAdmin is a free data retrieval call binding the contract method 0xe8b42bf4. // -// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function checkOrgAdmin(address _account, string _orgId, string _ultParent) view returns(bool) func (_AcctManager *AcctManagerCallerSession) CheckOrgAdmin(_account common.Address, _orgId string, _ultParent string) (bool, error) { return _AcctManager.Contract.CheckOrgAdmin(&_AcctManager.CallOpts, _account, _orgId, _ultParent) } // GetAccountDetails is a free data retrieval call binding the contract method 0x2aceb534. // -// Solidity: function getAccountDetails(address _account) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetails(address _account) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCaller) GetAccountDetails(opts *bind.CallOpts, _account common.Address) (common.Address, string, string, *big.Int, bool, error) { - var ( - ret0 = new(common.Address) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(bool) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, - } - err := _AcctManager.contract.Call(opts, out, "getAccountDetails", _account) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountDetails", _account) + + if err != nil { + return *new(common.Address), *new(string), *new(string), *new(*big.Int), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(bool)).(*bool) + + return out0, out1, out2, out3, out4, err + } // GetAccountDetails is a free data retrieval call binding the contract method 0x2aceb534. // -// Solidity: function getAccountDetails(address _account) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetails(address _account) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerSession) GetAccountDetails(_account common.Address) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetails(&_AcctManager.CallOpts, _account) } // GetAccountDetails is a free data retrieval call binding the contract method 0x2aceb534. // -// Solidity: function getAccountDetails(address _account) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetails(address _account) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCallerSession) GetAccountDetails(_account common.Address) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetails(&_AcctManager.CallOpts, _account) } // GetAccountDetailsFromIndex is a free data retrieval call binding the contract method 0xb2018568. // -// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCaller) GetAccountDetailsFromIndex(opts *bind.CallOpts, _aIndex *big.Int) (common.Address, string, string, *big.Int, bool, error) { - var ( - ret0 = new(common.Address) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(bool) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, - } - err := _AcctManager.contract.Call(opts, out, "getAccountDetailsFromIndex", _aIndex) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountDetailsFromIndex", _aIndex) + + if err != nil { + return *new(common.Address), *new(string), *new(string), *new(*big.Int), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(bool)).(*bool) + + return out0, out1, out2, out3, out4, err + } // GetAccountDetailsFromIndex is a free data retrieval call binding the contract method 0xb2018568. // -// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerSession) GetAccountDetailsFromIndex(_aIndex *big.Int) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetailsFromIndex(&_AcctManager.CallOpts, _aIndex) } // GetAccountDetailsFromIndex is a free data retrieval call binding the contract method 0xb2018568. // -// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) constant returns(address, string, string, uint256, bool) +// Solidity: function getAccountDetailsFromIndex(uint256 _aIndex) view returns(address, string, string, uint256, bool) func (_AcctManager *AcctManagerCallerSession) GetAccountDetailsFromIndex(_aIndex *big.Int) (common.Address, string, string, *big.Int, bool, error) { return _AcctManager.Contract.GetAccountDetailsFromIndex(&_AcctManager.CallOpts, _aIndex) } // GetAccountOrgRole is a free data retrieval call binding the contract method 0x6acee5fd. // -// Solidity: function getAccountOrgRole(address _account) constant returns(string, string) +// Solidity: function getAccountOrgRole(address _account) view returns(string, string) func (_AcctManager *AcctManagerCaller) GetAccountOrgRole(opts *bind.CallOpts, _account common.Address) (string, string, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ) - out := &[]interface{}{ - ret0, - ret1, + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountOrgRole", _account) + + if err != nil { + return *new(string), *new(string), err } - err := _AcctManager.contract.Call(opts, out, "getAccountOrgRole", _account) - return *ret0, *ret1, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + + return out0, out1, err + } // GetAccountOrgRole is a free data retrieval call binding the contract method 0x6acee5fd. // -// Solidity: function getAccountOrgRole(address _account) constant returns(string, string) +// Solidity: function getAccountOrgRole(address _account) view returns(string, string) func (_AcctManager *AcctManagerSession) GetAccountOrgRole(_account common.Address) (string, string, error) { return _AcctManager.Contract.GetAccountOrgRole(&_AcctManager.CallOpts, _account) } // GetAccountOrgRole is a free data retrieval call binding the contract method 0x6acee5fd. // -// Solidity: function getAccountOrgRole(address _account) constant returns(string, string) +// Solidity: function getAccountOrgRole(address _account) view returns(string, string) func (_AcctManager *AcctManagerCallerSession) GetAccountOrgRole(_account common.Address) (string, string, error) { return _AcctManager.Contract.GetAccountOrgRole(&_AcctManager.CallOpts, _account) } // GetAccountRole is a free data retrieval call binding the contract method 0x81d66b23. // -// Solidity: function getAccountRole(address _account) constant returns(string) +// Solidity: function getAccountRole(address _account) view returns(string) func (_AcctManager *AcctManagerCaller) GetAccountRole(opts *bind.CallOpts, _account common.Address) (string, error) { - var ( - ret0 = new(string) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "getAccountRole", _account) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountRole", _account) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + } // GetAccountRole is a free data retrieval call binding the contract method 0x81d66b23. // -// Solidity: function getAccountRole(address _account) constant returns(string) +// Solidity: function getAccountRole(address _account) view returns(string) func (_AcctManager *AcctManagerSession) GetAccountRole(_account common.Address) (string, error) { return _AcctManager.Contract.GetAccountRole(&_AcctManager.CallOpts, _account) } // GetAccountRole is a free data retrieval call binding the contract method 0x81d66b23. // -// Solidity: function getAccountRole(address _account) constant returns(string) +// Solidity: function getAccountRole(address _account) view returns(string) func (_AcctManager *AcctManagerCallerSession) GetAccountRole(_account common.Address) (string, error) { return _AcctManager.Contract.GetAccountRole(&_AcctManager.CallOpts, _account) } // GetAccountStatus is a free data retrieval call binding the contract method 0xfd4fa05a. // -// Solidity: function getAccountStatus(address _account) constant returns(uint256) +// Solidity: function getAccountStatus(address _account) view returns(uint256) func (_AcctManager *AcctManagerCaller) GetAccountStatus(opts *bind.CallOpts, _account common.Address) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "getAccountStatus", _account) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getAccountStatus", _account) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetAccountStatus is a free data retrieval call binding the contract method 0xfd4fa05a. // -// Solidity: function getAccountStatus(address _account) constant returns(uint256) +// Solidity: function getAccountStatus(address _account) view returns(uint256) func (_AcctManager *AcctManagerSession) GetAccountStatus(_account common.Address) (*big.Int, error) { return _AcctManager.Contract.GetAccountStatus(&_AcctManager.CallOpts, _account) } // GetAccountStatus is a free data retrieval call binding the contract method 0xfd4fa05a. // -// Solidity: function getAccountStatus(address _account) constant returns(uint256) +// Solidity: function getAccountStatus(address _account) view returns(uint256) func (_AcctManager *AcctManagerCallerSession) GetAccountStatus(_account common.Address) (*big.Int, error) { return _AcctManager.Contract.GetAccountStatus(&_AcctManager.CallOpts, _account) } // GetNumberOfAccounts is a free data retrieval call binding the contract method 0x309e36ef. // -// Solidity: function getNumberOfAccounts() constant returns(uint256) +// Solidity: function getNumberOfAccounts() view returns(uint256) func (_AcctManager *AcctManagerCaller) GetNumberOfAccounts(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "getNumberOfAccounts") - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "getNumberOfAccounts") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfAccounts is a free data retrieval call binding the contract method 0x309e36ef. // -// Solidity: function getNumberOfAccounts() constant returns(uint256) +// Solidity: function getNumberOfAccounts() view returns(uint256) func (_AcctManager *AcctManagerSession) GetNumberOfAccounts() (*big.Int, error) { return _AcctManager.Contract.GetNumberOfAccounts(&_AcctManager.CallOpts) } // GetNumberOfAccounts is a free data retrieval call binding the contract method 0x309e36ef. // -// Solidity: function getNumberOfAccounts() constant returns(uint256) +// Solidity: function getNumberOfAccounts() view returns(uint256) func (_AcctManager *AcctManagerCallerSession) GetNumberOfAccounts() (*big.Int, error) { return _AcctManager.Contract.GetNumberOfAccounts(&_AcctManager.CallOpts) } // OrgAdminExists is a free data retrieval call binding the contract method 0x950145cf. // -// Solidity: function orgAdminExists(string _orgId) constant returns(bool) +// Solidity: function orgAdminExists(string _orgId) view returns(bool) func (_AcctManager *AcctManagerCaller) OrgAdminExists(opts *bind.CallOpts, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "orgAdminExists", _orgId) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "orgAdminExists", _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // OrgAdminExists is a free data retrieval call binding the contract method 0x950145cf. // -// Solidity: function orgAdminExists(string _orgId) constant returns(bool) +// Solidity: function orgAdminExists(string _orgId) view returns(bool) func (_AcctManager *AcctManagerSession) OrgAdminExists(_orgId string) (bool, error) { return _AcctManager.Contract.OrgAdminExists(&_AcctManager.CallOpts, _orgId) } // OrgAdminExists is a free data retrieval call binding the contract method 0x950145cf. // -// Solidity: function orgAdminExists(string _orgId) constant returns(bool) +// Solidity: function orgAdminExists(string _orgId) view returns(bool) func (_AcctManager *AcctManagerCallerSession) OrgAdminExists(_orgId string) (bool, error) { return _AcctManager.Contract.OrgAdminExists(&_AcctManager.CallOpts, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_AcctManager *AcctManagerCaller) ValidateAccount(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _AcctManager.contract.Call(opts, out, "validateAccount", _account, _orgId) - return *ret0, err + var out []interface{} + err := _AcctManager.contract.Call(opts, &out, "validateAccount", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_AcctManager *AcctManagerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _AcctManager.Contract.ValidateAccount(&_AcctManager.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_AcctManager *AcctManagerCallerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _AcctManager.Contract.ValidateAccount(&_AcctManager.CallOpts, _account, _orgId) } diff --git a/permission/v2/bind/nodes.go b/permission/v2/bind/nodes.go index c9184b295b..d48e39f515 100644 --- a/permission/v2/bind/nodes.go +++ b/permission/v2/bind/nodes.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindNodeManager(address common.Address, caller bind.ContractCaller, transac // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_NodeManager *NodeManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_NodeManager *NodeManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _NodeManager.Contract.NodeManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_NodeManager *NodeManagerRaw) Transact(opts *bind.TransactOpts, method str // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_NodeManager *NodeManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_NodeManager *NodeManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _NodeManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,33 +192,38 @@ func (_NodeManager *NodeManagerTransactorRaw) Transact(opts *bind.TransactOpts, // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_NodeManager *NodeManagerCaller) ConnectionAllowed(opts *bind.CallOpts, _enodeId string, _ip string, _port uint16) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _NodeManager.contract.Call(opts, out, "connectionAllowed", _enodeId, _ip, _port) - return *ret0, err + var out []interface{} + err := _NodeManager.contract.Call(opts, &out, "connectionAllowed", _enodeId, _ip, _port) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_NodeManager *NodeManagerSession) ConnectionAllowed(_enodeId string, _ip string, _port uint16) (bool, error) { return _NodeManager.Contract.ConnectionAllowed(&_NodeManager.CallOpts, _enodeId, _ip, _port) } // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_NodeManager *NodeManagerCallerSession) ConnectionAllowed(_enodeId string, _ip string, _port uint16) (bool, error) { return _NodeManager.Contract.ConnectionAllowed(&_NodeManager.CallOpts, _enodeId, _ip, _port) } // GetNodeDetails is a free data retrieval call binding the contract method 0x3f0e0e47. // -// Solidity: function getNodeDetails(string enodeId) constant returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) +// Solidity: function getNodeDetails(string enodeId) view returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) func (_NodeManager *NodeManagerCaller) GetNodeDetails(opts *bind.CallOpts, enodeId string) (struct { OrgId string EnodeId string @@ -229,7 +232,10 @@ func (_NodeManager *NodeManagerCaller) GetNodeDetails(opts *bind.CallOpts, enode Raftport uint16 NodeStatus *big.Int }, error) { - ret := new(struct { + var out []interface{} + err := _NodeManager.contract.Call(opts, &out, "getNodeDetails", enodeId) + + outstruct := new(struct { OrgId string EnodeId string Ip string @@ -237,14 +243,21 @@ func (_NodeManager *NodeManagerCaller) GetNodeDetails(opts *bind.CallOpts, enode Raftport uint16 NodeStatus *big.Int }) - out := ret - err := _NodeManager.contract.Call(opts, out, "getNodeDetails", enodeId) - return *ret, err + + outstruct.OrgId = out[0].(string) + outstruct.EnodeId = out[1].(string) + outstruct.Ip = out[2].(string) + outstruct.Port = out[3].(uint16) + outstruct.Raftport = out[4].(uint16) + outstruct.NodeStatus = out[5].(*big.Int) + + return *outstruct, err + } // GetNodeDetails is a free data retrieval call binding the contract method 0x3f0e0e47. // -// Solidity: function getNodeDetails(string enodeId) constant returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) +// Solidity: function getNodeDetails(string enodeId) view returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) func (_NodeManager *NodeManagerSession) GetNodeDetails(enodeId string) (struct { OrgId string EnodeId string @@ -258,7 +271,7 @@ func (_NodeManager *NodeManagerSession) GetNodeDetails(enodeId string) (struct { // GetNodeDetails is a free data retrieval call binding the contract method 0x3f0e0e47. // -// Solidity: function getNodeDetails(string enodeId) constant returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) +// Solidity: function getNodeDetails(string enodeId) view returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) func (_NodeManager *NodeManagerCallerSession) GetNodeDetails(enodeId string) (struct { OrgId string EnodeId string @@ -272,7 +285,7 @@ func (_NodeManager *NodeManagerCallerSession) GetNodeDetails(enodeId string) (st // GetNodeDetailsFromIndex is a free data retrieval call binding the contract method 0x97c07a9b. // -// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) constant returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) +// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) view returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) func (_NodeManager *NodeManagerCaller) GetNodeDetailsFromIndex(opts *bind.CallOpts, _nodeIndex *big.Int) (struct { OrgId string EnodeId string @@ -281,7 +294,10 @@ func (_NodeManager *NodeManagerCaller) GetNodeDetailsFromIndex(opts *bind.CallOp Raftport uint16 NodeStatus *big.Int }, error) { - ret := new(struct { + var out []interface{} + err := _NodeManager.contract.Call(opts, &out, "getNodeDetailsFromIndex", _nodeIndex) + + outstruct := new(struct { OrgId string EnodeId string Ip string @@ -289,14 +305,21 @@ func (_NodeManager *NodeManagerCaller) GetNodeDetailsFromIndex(opts *bind.CallOp Raftport uint16 NodeStatus *big.Int }) - out := ret - err := _NodeManager.contract.Call(opts, out, "getNodeDetailsFromIndex", _nodeIndex) - return *ret, err + + outstruct.OrgId = out[0].(string) + outstruct.EnodeId = out[1].(string) + outstruct.Ip = out[2].(string) + outstruct.Port = out[3].(uint16) + outstruct.Raftport = out[4].(uint16) + outstruct.NodeStatus = out[5].(*big.Int) + + return *outstruct, err + } // GetNodeDetailsFromIndex is a free data retrieval call binding the contract method 0x97c07a9b. // -// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) constant returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) +// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) view returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) func (_NodeManager *NodeManagerSession) GetNodeDetailsFromIndex(_nodeIndex *big.Int) (struct { OrgId string EnodeId string @@ -310,7 +333,7 @@ func (_NodeManager *NodeManagerSession) GetNodeDetailsFromIndex(_nodeIndex *big. // GetNodeDetailsFromIndex is a free data retrieval call binding the contract method 0x97c07a9b. // -// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) constant returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) +// Solidity: function getNodeDetailsFromIndex(uint256 _nodeIndex) view returns(string _orgId, string _enodeId, string _ip, uint16 _port, uint16 _raftport, uint256 _nodeStatus) func (_NodeManager *NodeManagerCallerSession) GetNodeDetailsFromIndex(_nodeIndex *big.Int) (struct { OrgId string EnodeId string @@ -324,26 +347,31 @@ func (_NodeManager *NodeManagerCallerSession) GetNodeDetailsFromIndex(_nodeIndex // GetNumberOfNodes is a free data retrieval call binding the contract method 0xb81c806a. // -// Solidity: function getNumberOfNodes() constant returns(uint256) +// Solidity: function getNumberOfNodes() view returns(uint256) func (_NodeManager *NodeManagerCaller) GetNumberOfNodes(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _NodeManager.contract.Call(opts, out, "getNumberOfNodes") - return *ret0, err + var out []interface{} + err := _NodeManager.contract.Call(opts, &out, "getNumberOfNodes") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfNodes is a free data retrieval call binding the contract method 0xb81c806a. // -// Solidity: function getNumberOfNodes() constant returns(uint256) +// Solidity: function getNumberOfNodes() view returns(uint256) func (_NodeManager *NodeManagerSession) GetNumberOfNodes() (*big.Int, error) { return _NodeManager.Contract.GetNumberOfNodes(&_NodeManager.CallOpts) } // GetNumberOfNodes is a free data retrieval call binding the contract method 0xb81c806a. // -// Solidity: function getNumberOfNodes() constant returns(uint256) +// Solidity: function getNumberOfNodes() view returns(uint256) func (_NodeManager *NodeManagerCallerSession) GetNumberOfNodes() (*big.Int, error) { return _NodeManager.Contract.GetNumberOfNodes(&_NodeManager.CallOpts) } diff --git a/permission/v2/bind/org.go b/permission/v2/bind/org.go index 7c245ec6a1..d219e8e7b7 100644 --- a/permission/v2/bind/org.go +++ b/permission/v2/bind/org.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindOrgManager(address common.Address, caller bind.ContractCaller, transact // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_OrgManager *OrgManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_OrgManager *OrgManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _OrgManager.Contract.OrgManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_OrgManager *OrgManagerRaw) Transact(opts *bind.TransactOpts, method strin // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_OrgManager *OrgManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_OrgManager *OrgManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _OrgManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,228 +192,256 @@ func (_OrgManager *OrgManagerTransactorRaw) Transact(opts *bind.TransactOpts, me // CheckOrgActive is a free data retrieval call binding the contract method 0x3fd62ae7. // -// Solidity: function checkOrgActive(string _orgId) constant returns(bool) +// Solidity: function checkOrgActive(string _orgId) view returns(bool) func (_OrgManager *OrgManagerCaller) CheckOrgActive(opts *bind.CallOpts, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "checkOrgActive", _orgId) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "checkOrgActive", _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckOrgActive is a free data retrieval call binding the contract method 0x3fd62ae7. // -// Solidity: function checkOrgActive(string _orgId) constant returns(bool) +// Solidity: function checkOrgActive(string _orgId) view returns(bool) func (_OrgManager *OrgManagerSession) CheckOrgActive(_orgId string) (bool, error) { return _OrgManager.Contract.CheckOrgActive(&_OrgManager.CallOpts, _orgId) } // CheckOrgActive is a free data retrieval call binding the contract method 0x3fd62ae7. // -// Solidity: function checkOrgActive(string _orgId) constant returns(bool) +// Solidity: function checkOrgActive(string _orgId) view returns(bool) func (_OrgManager *OrgManagerCallerSession) CheckOrgActive(_orgId string) (bool, error) { return _OrgManager.Contract.CheckOrgActive(&_OrgManager.CallOpts, _orgId) } // CheckOrgExists is a free data retrieval call binding the contract method 0xffe40d1d. // -// Solidity: function checkOrgExists(string _orgId) constant returns(bool) +// Solidity: function checkOrgExists(string _orgId) view returns(bool) func (_OrgManager *OrgManagerCaller) CheckOrgExists(opts *bind.CallOpts, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "checkOrgExists", _orgId) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "checkOrgExists", _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckOrgExists is a free data retrieval call binding the contract method 0xffe40d1d. // -// Solidity: function checkOrgExists(string _orgId) constant returns(bool) +// Solidity: function checkOrgExists(string _orgId) view returns(bool) func (_OrgManager *OrgManagerSession) CheckOrgExists(_orgId string) (bool, error) { return _OrgManager.Contract.CheckOrgExists(&_OrgManager.CallOpts, _orgId) } // CheckOrgExists is a free data retrieval call binding the contract method 0xffe40d1d. // -// Solidity: function checkOrgExists(string _orgId) constant returns(bool) +// Solidity: function checkOrgExists(string _orgId) view returns(bool) func (_OrgManager *OrgManagerCallerSession) CheckOrgExists(_orgId string) (bool, error) { return _OrgManager.Contract.CheckOrgExists(&_OrgManager.CallOpts, _orgId) } // CheckOrgStatus is a free data retrieval call binding the contract method 0x8c8642df. // -// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) constant returns(bool) +// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) view returns(bool) func (_OrgManager *OrgManagerCaller) CheckOrgStatus(opts *bind.CallOpts, _orgId string, _orgStatus *big.Int) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "checkOrgStatus", _orgId, _orgStatus) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "checkOrgStatus", _orgId, _orgStatus) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // CheckOrgStatus is a free data retrieval call binding the contract method 0x8c8642df. // -// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) constant returns(bool) +// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) view returns(bool) func (_OrgManager *OrgManagerSession) CheckOrgStatus(_orgId string, _orgStatus *big.Int) (bool, error) { return _OrgManager.Contract.CheckOrgStatus(&_OrgManager.CallOpts, _orgId, _orgStatus) } // CheckOrgStatus is a free data retrieval call binding the contract method 0x8c8642df. // -// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) constant returns(bool) +// Solidity: function checkOrgStatus(string _orgId, uint256 _orgStatus) view returns(bool) func (_OrgManager *OrgManagerCallerSession) CheckOrgStatus(_orgId string, _orgStatus *big.Int) (bool, error) { return _OrgManager.Contract.CheckOrgStatus(&_OrgManager.CallOpts, _orgId, _orgStatus) } // GetNumberOfOrgs is a free data retrieval call binding the contract method 0x7755ebdd. // -// Solidity: function getNumberOfOrgs() constant returns(uint256) +// Solidity: function getNumberOfOrgs() view returns(uint256) func (_OrgManager *OrgManagerCaller) GetNumberOfOrgs(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "getNumberOfOrgs") - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getNumberOfOrgs") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfOrgs is a free data retrieval call binding the contract method 0x7755ebdd. // -// Solidity: function getNumberOfOrgs() constant returns(uint256) +// Solidity: function getNumberOfOrgs() view returns(uint256) func (_OrgManager *OrgManagerSession) GetNumberOfOrgs() (*big.Int, error) { return _OrgManager.Contract.GetNumberOfOrgs(&_OrgManager.CallOpts) } // GetNumberOfOrgs is a free data retrieval call binding the contract method 0x7755ebdd. // -// Solidity: function getNumberOfOrgs() constant returns(uint256) +// Solidity: function getNumberOfOrgs() view returns(uint256) func (_OrgManager *OrgManagerCallerSession) GetNumberOfOrgs() (*big.Int, error) { return _OrgManager.Contract.GetNumberOfOrgs(&_OrgManager.CallOpts) } // GetOrgDetails is a free data retrieval call binding the contract method 0xf4d6d9f5. // -// Solidity: function getOrgDetails(string _orgId) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgDetails(string _orgId) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCaller) GetOrgDetails(opts *bind.CallOpts, _orgId string) (string, string, string, *big.Int, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getOrgDetails", _orgId) + + if err != nil { + return *new(string), *new(string), *new(string), *new(*big.Int), *new(*big.Int), err } - err := _OrgManager.contract.Call(opts, out, "getOrgDetails", _orgId) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, out4, err + } // GetOrgDetails is a free data retrieval call binding the contract method 0xf4d6d9f5. // -// Solidity: function getOrgDetails(string _orgId) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgDetails(string _orgId) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerSession) GetOrgDetails(_orgId string) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgDetails(&_OrgManager.CallOpts, _orgId) } // GetOrgDetails is a free data retrieval call binding the contract method 0xf4d6d9f5. // -// Solidity: function getOrgDetails(string _orgId) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgDetails(string _orgId) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCallerSession) GetOrgDetails(_orgId string) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgDetails(&_OrgManager.CallOpts, _orgId) } // GetOrgInfo is a free data retrieval call binding the contract method 0x5c4f32ee. // -// Solidity: function getOrgInfo(uint256 _orgIndex) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgInfo(uint256 _orgIndex) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCaller) GetOrgInfo(opts *bind.CallOpts, _orgIndex *big.Int) (string, string, string, *big.Int, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(string) - ret3 = new(*big.Int) - ret4 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - ret4, + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getOrgInfo", _orgIndex) + + if err != nil { + return *new(string), *new(string), *new(string), *new(*big.Int), *new(*big.Int), err } - err := _OrgManager.contract.Call(opts, out, "getOrgInfo", _orgIndex) - return *ret0, *ret1, *ret2, *ret3, *ret4, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + out4 := *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, out4, err + } // GetOrgInfo is a free data retrieval call binding the contract method 0x5c4f32ee. // -// Solidity: function getOrgInfo(uint256 _orgIndex) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgInfo(uint256 _orgIndex) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerSession) GetOrgInfo(_orgIndex *big.Int) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgInfo(&_OrgManager.CallOpts, _orgIndex) } // GetOrgInfo is a free data retrieval call binding the contract method 0x5c4f32ee. // -// Solidity: function getOrgInfo(uint256 _orgIndex) constant returns(string, string, string, uint256, uint256) +// Solidity: function getOrgInfo(uint256 _orgIndex) view returns(string, string, string, uint256, uint256) func (_OrgManager *OrgManagerCallerSession) GetOrgInfo(_orgIndex *big.Int) (string, string, string, *big.Int, *big.Int, error) { return _OrgManager.Contract.GetOrgInfo(&_OrgManager.CallOpts, _orgIndex) } // GetSubOrgIndexes is a free data retrieval call binding the contract method 0x5e99f6e5. // -// Solidity: function getSubOrgIndexes(string _orgId) constant returns(uint256[]) +// Solidity: function getSubOrgIndexes(string _orgId) view returns(uint256[]) func (_OrgManager *OrgManagerCaller) GetSubOrgIndexes(opts *bind.CallOpts, _orgId string) ([]*big.Int, error) { - var ( - ret0 = new([]*big.Int) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "getSubOrgIndexes", _orgId) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getSubOrgIndexes", _orgId) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + } // GetSubOrgIndexes is a free data retrieval call binding the contract method 0x5e99f6e5. // -// Solidity: function getSubOrgIndexes(string _orgId) constant returns(uint256[]) +// Solidity: function getSubOrgIndexes(string _orgId) view returns(uint256[]) func (_OrgManager *OrgManagerSession) GetSubOrgIndexes(_orgId string) ([]*big.Int, error) { return _OrgManager.Contract.GetSubOrgIndexes(&_OrgManager.CallOpts, _orgId) } // GetSubOrgIndexes is a free data retrieval call binding the contract method 0x5e99f6e5. // -// Solidity: function getSubOrgIndexes(string _orgId) constant returns(uint256[]) +// Solidity: function getSubOrgIndexes(string _orgId) view returns(uint256[]) func (_OrgManager *OrgManagerCallerSession) GetSubOrgIndexes(_orgId string) ([]*big.Int, error) { return _OrgManager.Contract.GetSubOrgIndexes(&_OrgManager.CallOpts, _orgId) } // GetUltimateParent is a free data retrieval call binding the contract method 0x177c8d8a. // -// Solidity: function getUltimateParent(string _orgId) constant returns(string) +// Solidity: function getUltimateParent(string _orgId) view returns(string) func (_OrgManager *OrgManagerCaller) GetUltimateParent(opts *bind.CallOpts, _orgId string) (string, error) { - var ( - ret0 = new(string) - ) - out := ret0 - err := _OrgManager.contract.Call(opts, out, "getUltimateParent", _orgId) - return *ret0, err + var out []interface{} + err := _OrgManager.contract.Call(opts, &out, "getUltimateParent", _orgId) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + } // GetUltimateParent is a free data retrieval call binding the contract method 0x177c8d8a. // -// Solidity: function getUltimateParent(string _orgId) constant returns(string) +// Solidity: function getUltimateParent(string _orgId) view returns(string) func (_OrgManager *OrgManagerSession) GetUltimateParent(_orgId string) (string, error) { return _OrgManager.Contract.GetUltimateParent(&_OrgManager.CallOpts, _orgId) } // GetUltimateParent is a free data retrieval call binding the contract method 0x177c8d8a. // -// Solidity: function getUltimateParent(string _orgId) constant returns(string) +// Solidity: function getUltimateParent(string _orgId) view returns(string) func (_OrgManager *OrgManagerCallerSession) GetUltimateParent(_orgId string) (string, error) { return _OrgManager.Contract.GetUltimateParent(&_OrgManager.CallOpts, _orgId) } diff --git a/permission/v2/bind/permission_impl.go b/permission/v2/bind/permission_impl.go index 663308606a..6bbf8ba2f7 100644 --- a/permission/v2/bind/permission_impl.go +++ b/permission/v2/bind/permission_impl.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindPermImpl(address common.Address, caller bind.ContractCaller, transactor // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermImpl *PermImplRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermImpl *PermImplRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermImpl.Contract.PermImplCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_PermImpl *PermImplRaw) Transact(opts *bind.TransactOpts, method string, p // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermImpl *PermImplCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermImpl *PermImplCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermImpl.Contract.contract.Call(opts, result, method, params...) } @@ -194,224 +192,254 @@ func (_PermImpl *PermImplTransactorRaw) Transact(opts *bind.TransactOpts, method // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_PermImpl *PermImplCaller) ConnectionAllowed(opts *bind.CallOpts, _enodeId string, _ip string, _port uint16) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "connectionAllowed", _enodeId, _ip, _port) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "connectionAllowed", _enodeId, _ip, _port) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_PermImpl *PermImplSession) ConnectionAllowed(_enodeId string, _ip string, _port uint16) (bool, error) { return _PermImpl.Contract.ConnectionAllowed(&_PermImpl.CallOpts, _enodeId, _ip, _port) } // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_PermImpl *PermImplCallerSession) ConnectionAllowed(_enodeId string, _ip string, _port uint16) (bool, error) { return _PermImpl.Contract.ConnectionAllowed(&_PermImpl.CallOpts, _enodeId, _ip, _port) } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermImpl *PermImplCaller) GetNetworkBootStatus(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "getNetworkBootStatus") - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "getNetworkBootStatus") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermImpl *PermImplSession) GetNetworkBootStatus() (bool, error) { return _PermImpl.Contract.GetNetworkBootStatus(&_PermImpl.CallOpts) } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermImpl *PermImplCallerSession) GetNetworkBootStatus() (bool, error) { return _PermImpl.Contract.GetNetworkBootStatus(&_PermImpl.CallOpts) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermImpl *PermImplCaller) GetPendingOp(opts *bind.CallOpts, _orgId string) (string, string, common.Address, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(common.Address) - ret3 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "getPendingOp", _orgId) + + if err != nil { + return *new(string), *new(string), *new(common.Address), *new(*big.Int), err } - err := _PermImpl.contract.Call(opts, out, "getPendingOp", _orgId) - return *ret0, *ret1, *ret2, *ret3, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, err + } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermImpl *PermImplSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermImpl.Contract.GetPendingOp(&_PermImpl.CallOpts, _orgId) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermImpl *PermImplCallerSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermImpl.Contract.GetPendingOp(&_PermImpl.CallOpts, _orgId) } // GetPolicyDetails is a free data retrieval call binding the contract method 0xcc9ba6fa. // -// Solidity: function getPolicyDetails() constant returns(string, string, string, bool) +// Solidity: function getPolicyDetails() view returns(string, string, string, bool) func (_PermImpl *PermImplCaller) GetPolicyDetails(opts *bind.CallOpts) (string, string, string, bool, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(string) - ret3 = new(bool) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "getPolicyDetails") + + if err != nil { + return *new(string), *new(string), *new(string), *new(bool), err } - err := _PermImpl.contract.Call(opts, out, "getPolicyDetails") - return *ret0, *ret1, *ret2, *ret3, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(string)).(*string) + out3 := *abi.ConvertType(out[3], new(bool)).(*bool) + + return out0, out1, out2, out3, err + } // GetPolicyDetails is a free data retrieval call binding the contract method 0xcc9ba6fa. // -// Solidity: function getPolicyDetails() constant returns(string, string, string, bool) +// Solidity: function getPolicyDetails() view returns(string, string, string, bool) func (_PermImpl *PermImplSession) GetPolicyDetails() (string, string, string, bool, error) { return _PermImpl.Contract.GetPolicyDetails(&_PermImpl.CallOpts) } // GetPolicyDetails is a free data retrieval call binding the contract method 0xcc9ba6fa. // -// Solidity: function getPolicyDetails() constant returns(string, string, string, bool) +// Solidity: function getPolicyDetails() view returns(string, string, string, bool) func (_PermImpl *PermImplCallerSession) GetPolicyDetails() (string, string, string, bool, error) { return _PermImpl.Contract.GetPolicyDetails(&_PermImpl.CallOpts) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermImpl *PermImplCaller) IsNetworkAdmin(opts *bind.CallOpts, _account common.Address) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "isNetworkAdmin", _account) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "isNetworkAdmin", _account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermImpl *PermImplSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermImpl.Contract.IsNetworkAdmin(&_PermImpl.CallOpts, _account) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermImpl *PermImplCallerSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermImpl.Contract.IsNetworkAdmin(&_PermImpl.CallOpts, _account) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCaller) IsOrgAdmin(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "isOrgAdmin", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "isOrgAdmin", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.IsOrgAdmin(&_PermImpl.CallOpts, _account, _orgId) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCallerSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.IsOrgAdmin(&_PermImpl.CallOpts, _account, _orgId) } // TransactionAllowed is a free data retrieval call binding the contract method 0x936421d5. // -// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) constant returns(bool) +// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) view returns(bool) func (_PermImpl *PermImplCaller) TransactionAllowed(opts *bind.CallOpts, _sender common.Address, _target common.Address, _value *big.Int, _gasPrice *big.Int, _gasLimit *big.Int, _payload []byte) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "transactionAllowed", _sender, _target, _value, _gasPrice, _gasLimit, _payload) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "transactionAllowed", _sender, _target, _value, _gasPrice, _gasLimit, _payload) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // TransactionAllowed is a free data retrieval call binding the contract method 0x936421d5. // -// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) constant returns(bool) +// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) view returns(bool) func (_PermImpl *PermImplSession) TransactionAllowed(_sender common.Address, _target common.Address, _value *big.Int, _gasPrice *big.Int, _gasLimit *big.Int, _payload []byte) (bool, error) { return _PermImpl.Contract.TransactionAllowed(&_PermImpl.CallOpts, _sender, _target, _value, _gasPrice, _gasLimit, _payload) } // TransactionAllowed is a free data retrieval call binding the contract method 0x936421d5. // -// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) constant returns(bool) +// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) view returns(bool) func (_PermImpl *PermImplCallerSession) TransactionAllowed(_sender common.Address, _target common.Address, _value *big.Int, _gasPrice *big.Int, _gasLimit *big.Int, _payload []byte) (bool, error) { return _PermImpl.Contract.TransactionAllowed(&_PermImpl.CallOpts, _sender, _target, _value, _gasPrice, _gasLimit, _payload) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCaller) ValidateAccount(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermImpl.contract.Call(opts, out, "validateAccount", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermImpl.contract.Call(opts, &out, "validateAccount", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.ValidateAccount(&_PermImpl.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermImpl *PermImplCallerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermImpl.Contract.ValidateAccount(&_PermImpl.CallOpts, _account, _orgId) } diff --git a/permission/v2/bind/permission_interface.go b/permission/v2/bind/permission_interface.go index 3f0e032bcf..34a2fc1e8e 100644 --- a/permission/v2/bind/permission_interface.go +++ b/permission/v2/bind/permission_interface.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindPermInterface(address common.Address, caller bind.ContractCaller, trans // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermInterface *PermInterfaceRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermInterface *PermInterfaceRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermInterface.Contract.PermInterfaceCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_PermInterface *PermInterfaceRaw) Transact(opts *bind.TransactOpts, method // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermInterface *PermInterfaceCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermInterface *PermInterfaceCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermInterface.Contract.contract.Call(opts, result, method, params...) } @@ -194,216 +192,251 @@ func (_PermInterface *PermInterfaceTransactorRaw) Transact(opts *bind.TransactOp // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_PermInterface *PermInterfaceCaller) ConnectionAllowed(opts *bind.CallOpts, _enodeId string, _ip string, _port uint16) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "connectionAllowed", _enodeId, _ip, _port) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "connectionAllowed", _enodeId, _ip, _port) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_PermInterface *PermInterfaceSession) ConnectionAllowed(_enodeId string, _ip string, _port uint16) (bool, error) { return _PermInterface.Contract.ConnectionAllowed(&_PermInterface.CallOpts, _enodeId, _ip, _port) } // ConnectionAllowed is a free data retrieval call binding the contract method 0x45a59e5b. // -// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) constant returns(bool) +// Solidity: function connectionAllowed(string _enodeId, string _ip, uint16 _port) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) ConnectionAllowed(_enodeId string, _ip string, _port uint16) (bool, error) { return _PermInterface.Contract.ConnectionAllowed(&_PermInterface.CallOpts, _enodeId, _ip, _port) } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermInterface *PermInterfaceCaller) GetNetworkBootStatus(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "getNetworkBootStatus") - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "getNetworkBootStatus") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermInterface *PermInterfaceSession) GetNetworkBootStatus() (bool, error) { return _PermInterface.Contract.GetNetworkBootStatus(&_PermInterface.CallOpts) } // GetNetworkBootStatus is a free data retrieval call binding the contract method 0x4cbfa82e. // -// Solidity: function getNetworkBootStatus() constant returns(bool) +// Solidity: function getNetworkBootStatus() view returns(bool) func (_PermInterface *PermInterfaceCallerSession) GetNetworkBootStatus() (bool, error) { return _PermInterface.Contract.GetNetworkBootStatus(&_PermInterface.CallOpts) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermInterface *PermInterfaceCaller) GetPendingOp(opts *bind.CallOpts, _orgId string) (string, string, common.Address, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(common.Address) - ret3 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "getPendingOp", _orgId) + + if err != nil { + return *new(string), *new(string), *new(common.Address), *new(*big.Int), err } - err := _PermInterface.contract.Call(opts, out, "getPendingOp", _orgId) - return *ret0, *ret1, *ret2, *ret3, err + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, err + } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermInterface *PermInterfaceSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermInterface.Contract.GetPendingOp(&_PermInterface.CallOpts, _orgId) } // GetPendingOp is a free data retrieval call binding the contract method 0xf346a3a7. // -// Solidity: function getPendingOp(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOp(string _orgId) view returns(string, string, address, uint256) func (_PermInterface *PermInterfaceCallerSession) GetPendingOp(_orgId string) (string, string, common.Address, *big.Int, error) { return _PermInterface.Contract.GetPendingOp(&_PermInterface.CallOpts, _orgId) } // GetPermissionsImpl is a free data retrieval call binding the contract method 0x03ed6933. // -// Solidity: function getPermissionsImpl() constant returns(address) +// Solidity: function getPermissionsImpl() view returns(address) func (_PermInterface *PermInterfaceCaller) GetPermissionsImpl(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "getPermissionsImpl") - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "getPermissionsImpl") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetPermissionsImpl is a free data retrieval call binding the contract method 0x03ed6933. // -// Solidity: function getPermissionsImpl() constant returns(address) +// Solidity: function getPermissionsImpl() view returns(address) func (_PermInterface *PermInterfaceSession) GetPermissionsImpl() (common.Address, error) { return _PermInterface.Contract.GetPermissionsImpl(&_PermInterface.CallOpts) } // GetPermissionsImpl is a free data retrieval call binding the contract method 0x03ed6933. // -// Solidity: function getPermissionsImpl() constant returns(address) +// Solidity: function getPermissionsImpl() view returns(address) func (_PermInterface *PermInterfaceCallerSession) GetPermissionsImpl() (common.Address, error) { return _PermInterface.Contract.GetPermissionsImpl(&_PermInterface.CallOpts) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermInterface *PermInterfaceCaller) IsNetworkAdmin(opts *bind.CallOpts, _account common.Address) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "isNetworkAdmin", _account) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "isNetworkAdmin", _account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermInterface *PermInterfaceSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermInterface.Contract.IsNetworkAdmin(&_PermInterface.CallOpts, _account) } // IsNetworkAdmin is a free data retrieval call binding the contract method 0xd1aa0c20. // -// Solidity: function isNetworkAdmin(address _account) constant returns(bool) +// Solidity: function isNetworkAdmin(address _account) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) IsNetworkAdmin(_account common.Address) (bool, error) { return _PermInterface.Contract.IsNetworkAdmin(&_PermInterface.CallOpts, _account) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCaller) IsOrgAdmin(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "isOrgAdmin", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "isOrgAdmin", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.IsOrgAdmin(&_PermInterface.CallOpts, _account, _orgId) } // IsOrgAdmin is a free data retrieval call binding the contract method 0x9bd38101. // -// Solidity: function isOrgAdmin(address _account, string _orgId) constant returns(bool) +// Solidity: function isOrgAdmin(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) IsOrgAdmin(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.IsOrgAdmin(&_PermInterface.CallOpts, _account, _orgId) } // TransactionAllowed is a free data retrieval call binding the contract method 0x936421d5. // -// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) constant returns(bool) +// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) view returns(bool) func (_PermInterface *PermInterfaceCaller) TransactionAllowed(opts *bind.CallOpts, _sender common.Address, _target common.Address, _value *big.Int, _gasPrice *big.Int, _gasLimit *big.Int, _payload []byte) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "transactionAllowed", _sender, _target, _value, _gasPrice, _gasLimit, _payload) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "transactionAllowed", _sender, _target, _value, _gasPrice, _gasLimit, _payload) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // TransactionAllowed is a free data retrieval call binding the contract method 0x936421d5. // -// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) constant returns(bool) +// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) view returns(bool) func (_PermInterface *PermInterfaceSession) TransactionAllowed(_sender common.Address, _target common.Address, _value *big.Int, _gasPrice *big.Int, _gasLimit *big.Int, _payload []byte) (bool, error) { return _PermInterface.Contract.TransactionAllowed(&_PermInterface.CallOpts, _sender, _target, _value, _gasPrice, _gasLimit, _payload) } // TransactionAllowed is a free data retrieval call binding the contract method 0x936421d5. // -// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) constant returns(bool) +// Solidity: function transactionAllowed(address _sender, address _target, uint256 _value, uint256 _gasPrice, uint256 _gasLimit, bytes _payload) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) TransactionAllowed(_sender common.Address, _target common.Address, _value *big.Int, _gasPrice *big.Int, _gasLimit *big.Int, _payload []byte) (bool, error) { return _PermInterface.Contract.TransactionAllowed(&_PermInterface.CallOpts, _sender, _target, _value, _gasPrice, _gasLimit, _payload) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCaller) ValidateAccount(opts *bind.CallOpts, _account common.Address, _orgId string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _PermInterface.contract.Call(opts, out, "validateAccount", _account, _orgId) - return *ret0, err + var out []interface{} + err := _PermInterface.contract.Call(opts, &out, "validateAccount", _account, _orgId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.ValidateAccount(&_PermInterface.CallOpts, _account, _orgId) } // ValidateAccount is a free data retrieval call binding the contract method 0x6b568d76. // -// Solidity: function validateAccount(address _account, string _orgId) constant returns(bool) +// Solidity: function validateAccount(address _account, string _orgId) view returns(bool) func (_PermInterface *PermInterfaceCallerSession) ValidateAccount(_account common.Address, _orgId string) (bool, error) { return _PermInterface.Contract.ValidateAccount(&_PermInterface.CallOpts, _account, _orgId) } diff --git a/permission/v2/bind/permission_upgr.go b/permission/v2/bind/permission_upgr.go index 133e54ed7e..d3cfe8edc4 100644 --- a/permission/v2/bind/permission_upgr.go +++ b/permission/v2/bind/permission_upgr.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindPermUpgr(address common.Address, caller bind.ContractCaller, transactor // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermUpgr *PermUpgrRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermUpgr *PermUpgrRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermUpgr.Contract.PermUpgrCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_PermUpgr *PermUpgrRaw) Transact(opts *bind.TransactOpts, method string, p // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_PermUpgr *PermUpgrCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_PermUpgr *PermUpgrCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _PermUpgr.Contract.contract.Call(opts, result, method, params...) } @@ -194,78 +192,93 @@ func (_PermUpgr *PermUpgrTransactorRaw) Transact(opts *bind.TransactOpts, method // GetGuardian is a free data retrieval call binding the contract method 0xa75b87d2. // -// Solidity: function getGuardian() constant returns(address) +// Solidity: function getGuardian() view returns(address) func (_PermUpgr *PermUpgrCaller) GetGuardian(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermUpgr.contract.Call(opts, out, "getGuardian") - return *ret0, err + var out []interface{} + err := _PermUpgr.contract.Call(opts, &out, "getGuardian") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetGuardian is a free data retrieval call binding the contract method 0xa75b87d2. // -// Solidity: function getGuardian() constant returns(address) +// Solidity: function getGuardian() view returns(address) func (_PermUpgr *PermUpgrSession) GetGuardian() (common.Address, error) { return _PermUpgr.Contract.GetGuardian(&_PermUpgr.CallOpts) } // GetGuardian is a free data retrieval call binding the contract method 0xa75b87d2. // -// Solidity: function getGuardian() constant returns(address) +// Solidity: function getGuardian() view returns(address) func (_PermUpgr *PermUpgrCallerSession) GetGuardian() (common.Address, error) { return _PermUpgr.Contract.GetGuardian(&_PermUpgr.CallOpts) } // GetPermImpl is a free data retrieval call binding the contract method 0x0e32cf90. // -// Solidity: function getPermImpl() constant returns(address) +// Solidity: function getPermImpl() view returns(address) func (_PermUpgr *PermUpgrCaller) GetPermImpl(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermUpgr.contract.Call(opts, out, "getPermImpl") - return *ret0, err + var out []interface{} + err := _PermUpgr.contract.Call(opts, &out, "getPermImpl") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetPermImpl is a free data retrieval call binding the contract method 0x0e32cf90. // -// Solidity: function getPermImpl() constant returns(address) +// Solidity: function getPermImpl() view returns(address) func (_PermUpgr *PermUpgrSession) GetPermImpl() (common.Address, error) { return _PermUpgr.Contract.GetPermImpl(&_PermUpgr.CallOpts) } // GetPermImpl is a free data retrieval call binding the contract method 0x0e32cf90. // -// Solidity: function getPermImpl() constant returns(address) +// Solidity: function getPermImpl() view returns(address) func (_PermUpgr *PermUpgrCallerSession) GetPermImpl() (common.Address, error) { return _PermUpgr.Contract.GetPermImpl(&_PermUpgr.CallOpts) } // GetPermInterface is a free data retrieval call binding the contract method 0xe572515c. // -// Solidity: function getPermInterface() constant returns(address) +// Solidity: function getPermInterface() view returns(address) func (_PermUpgr *PermUpgrCaller) GetPermInterface(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _PermUpgr.contract.Call(opts, out, "getPermInterface") - return *ret0, err + var out []interface{} + err := _PermUpgr.contract.Call(opts, &out, "getPermInterface") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + } // GetPermInterface is a free data retrieval call binding the contract method 0xe572515c. // -// Solidity: function getPermInterface() constant returns(address) +// Solidity: function getPermInterface() view returns(address) func (_PermUpgr *PermUpgrSession) GetPermInterface() (common.Address, error) { return _PermUpgr.Contract.GetPermInterface(&_PermUpgr.CallOpts) } // GetPermInterface is a free data retrieval call binding the contract method 0xe572515c. // -// Solidity: function getPermInterface() constant returns(address) +// Solidity: function getPermInterface() view returns(address) func (_PermUpgr *PermUpgrCallerSession) GetPermInterface() (common.Address, error) { return _PermUpgr.Contract.GetPermInterface(&_PermUpgr.CallOpts) } diff --git a/permission/v2/bind/roles.go b/permission/v2/bind/roles.go index f0d6a37451..a2ec6237e6 100644 --- a/permission/v2/bind/roles.go +++ b/permission/v2/bind/roles.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindRoleManager(address common.Address, caller bind.ContractCaller, transac // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_RoleManager *RoleManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_RoleManager *RoleManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _RoleManager.Contract.RoleManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_RoleManager *RoleManagerRaw) Transact(opts *bind.TransactOpts, method str // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_RoleManager *RoleManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_RoleManager *RoleManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _RoleManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,33 +192,38 @@ func (_RoleManager *RoleManagerTransactorRaw) Transact(opts *bind.TransactOpts, // GetNumberOfRoles is a free data retrieval call binding the contract method 0x87f55d31. // -// Solidity: function getNumberOfRoles() constant returns(uint256) +// Solidity: function getNumberOfRoles() view returns(uint256) func (_RoleManager *RoleManagerCaller) GetNumberOfRoles(opts *bind.CallOpts) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "getNumberOfRoles") - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "getNumberOfRoles") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // GetNumberOfRoles is a free data retrieval call binding the contract method 0x87f55d31. // -// Solidity: function getNumberOfRoles() constant returns(uint256) +// Solidity: function getNumberOfRoles() view returns(uint256) func (_RoleManager *RoleManagerSession) GetNumberOfRoles() (*big.Int, error) { return _RoleManager.Contract.GetNumberOfRoles(&_RoleManager.CallOpts) } // GetNumberOfRoles is a free data retrieval call binding the contract method 0x87f55d31. // -// Solidity: function getNumberOfRoles() constant returns(uint256) +// Solidity: function getNumberOfRoles() view returns(uint256) func (_RoleManager *RoleManagerCallerSession) GetNumberOfRoles() (*big.Int, error) { return _RoleManager.Contract.GetNumberOfRoles(&_RoleManager.CallOpts) } // GetRoleDetails is a free data retrieval call binding the contract method 0x1870aba3. // -// Solidity: function getRoleDetails(string _roleId, string _orgId) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetails(string _roleId, string _orgId) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCaller) GetRoleDetails(opts *bind.CallOpts, _roleId string, _orgId string) (struct { RoleId string OrgId string @@ -229,7 +232,10 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetails(opts *bind.CallOpts, _role Admin bool Active bool }, error) { - ret := new(struct { + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "getRoleDetails", _roleId, _orgId) + + outstruct := new(struct { RoleId string OrgId string AccessType *big.Int @@ -237,14 +243,21 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetails(opts *bind.CallOpts, _role Admin bool Active bool }) - out := ret - err := _RoleManager.contract.Call(opts, out, "getRoleDetails", _roleId, _orgId) - return *ret, err + + outstruct.RoleId = out[0].(string) + outstruct.OrgId = out[1].(string) + outstruct.AccessType = out[2].(*big.Int) + outstruct.Voter = out[3].(bool) + outstruct.Admin = out[4].(bool) + outstruct.Active = out[5].(bool) + + return *outstruct, err + } // GetRoleDetails is a free data retrieval call binding the contract method 0x1870aba3. // -// Solidity: function getRoleDetails(string _roleId, string _orgId) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetails(string _roleId, string _orgId) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerSession) GetRoleDetails(_roleId string, _orgId string) (struct { RoleId string OrgId string @@ -258,7 +271,7 @@ func (_RoleManager *RoleManagerSession) GetRoleDetails(_roleId string, _orgId st // GetRoleDetails is a free data retrieval call binding the contract method 0x1870aba3. // -// Solidity: function getRoleDetails(string _roleId, string _orgId) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetails(string _roleId, string _orgId) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCallerSession) GetRoleDetails(_roleId string, _orgId string) (struct { RoleId string OrgId string @@ -272,7 +285,7 @@ func (_RoleManager *RoleManagerCallerSession) GetRoleDetails(_roleId string, _or // GetRoleDetailsFromIndex is a free data retrieval call binding the contract method 0xa451d4a8. // -// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCaller) GetRoleDetailsFromIndex(opts *bind.CallOpts, _rIndex *big.Int) (struct { RoleId string OrgId string @@ -281,7 +294,10 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetailsFromIndex(opts *bind.CallOp Admin bool Active bool }, error) { - ret := new(struct { + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "getRoleDetailsFromIndex", _rIndex) + + outstruct := new(struct { RoleId string OrgId string AccessType *big.Int @@ -289,14 +305,21 @@ func (_RoleManager *RoleManagerCaller) GetRoleDetailsFromIndex(opts *bind.CallOp Admin bool Active bool }) - out := ret - err := _RoleManager.contract.Call(opts, out, "getRoleDetailsFromIndex", _rIndex) - return *ret, err + + outstruct.RoleId = out[0].(string) + outstruct.OrgId = out[1].(string) + outstruct.AccessType = out[2].(*big.Int) + outstruct.Voter = out[3].(bool) + outstruct.Admin = out[4].(bool) + outstruct.Active = out[5].(bool) + + return *outstruct, err + } // GetRoleDetailsFromIndex is a free data retrieval call binding the contract method 0xa451d4a8. // -// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerSession) GetRoleDetailsFromIndex(_rIndex *big.Int) (struct { RoleId string OrgId string @@ -310,7 +333,7 @@ func (_RoleManager *RoleManagerSession) GetRoleDetailsFromIndex(_rIndex *big.Int // GetRoleDetailsFromIndex is a free data retrieval call binding the contract method 0xa451d4a8. // -// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) constant returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) +// Solidity: function getRoleDetailsFromIndex(uint256 _rIndex) view returns(string roleId, string orgId, uint256 accessType, bool voter, bool admin, bool active) func (_RoleManager *RoleManagerCallerSession) GetRoleDetailsFromIndex(_rIndex *big.Int) (struct { RoleId string OrgId string @@ -324,130 +347,155 @@ func (_RoleManager *RoleManagerCallerSession) GetRoleDetailsFromIndex(_rIndex *b // IsAdminRole is a free data retrieval call binding the contract method 0xbe322e54. // -// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCaller) IsAdminRole(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "isAdminRole", _roleId, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "isAdminRole", _roleId, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsAdminRole is a free data retrieval call binding the contract method 0xbe322e54. // -// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerSession) IsAdminRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsAdminRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // IsAdminRole is a free data retrieval call binding the contract method 0xbe322e54. // -// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isAdminRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCallerSession) IsAdminRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsAdminRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // IsVoterRole is a free data retrieval call binding the contract method 0xdeb16ba7. // -// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCaller) IsVoterRole(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "isVoterRole", _roleId, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "isVoterRole", _roleId, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // IsVoterRole is a free data retrieval call binding the contract method 0xdeb16ba7. // -// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerSession) IsVoterRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsVoterRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // IsVoterRole is a free data retrieval call binding the contract method 0xdeb16ba7. // -// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function isVoterRole(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCallerSession) IsVoterRole(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.IsVoterRole(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // RoleAccess is a free data retrieval call binding the contract method 0xcfc83dfa. // -// Solidity: function roleAccess(string _roleId, string _orgId, string _ultParent) constant returns(uint256) +// Solidity: function roleAccess(string _roleId, string _orgId, string _ultParent) view returns(uint256) func (_RoleManager *RoleManagerCaller) RoleAccess(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string) (*big.Int, error) { - var ( - ret0 = new(*big.Int) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "roleAccess", _roleId, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "roleAccess", _roleId, _orgId, _ultParent) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + } // RoleAccess is a free data retrieval call binding the contract method 0xcfc83dfa. // -// Solidity: function roleAccess(string _roleId, string _orgId, string _ultParent) constant returns(uint256) +// Solidity: function roleAccess(string _roleId, string _orgId, string _ultParent) view returns(uint256) func (_RoleManager *RoleManagerSession) RoleAccess(_roleId string, _orgId string, _ultParent string) (*big.Int, error) { return _RoleManager.Contract.RoleAccess(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // RoleAccess is a free data retrieval call binding the contract method 0xcfc83dfa. // -// Solidity: function roleAccess(string _roleId, string _orgId, string _ultParent) constant returns(uint256) +// Solidity: function roleAccess(string _roleId, string _orgId, string _ultParent) view returns(uint256) func (_RoleManager *RoleManagerCallerSession) RoleAccess(_roleId string, _orgId string, _ultParent string) (*big.Int, error) { return _RoleManager.Contract.RoleAccess(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // RoleExists is a free data retrieval call binding the contract method 0xabf5739f. // -// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCaller) RoleExists(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "roleExists", _roleId, _orgId, _ultParent) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "roleExists", _roleId, _orgId, _ultParent) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // RoleExists is a free data retrieval call binding the contract method 0xabf5739f. // -// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerSession) RoleExists(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.RoleExists(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // RoleExists is a free data retrieval call binding the contract method 0xabf5739f. // -// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) constant returns(bool) +// Solidity: function roleExists(string _roleId, string _orgId, string _ultParent) view returns(bool) func (_RoleManager *RoleManagerCallerSession) RoleExists(_roleId string, _orgId string, _ultParent string) (bool, error) { return _RoleManager.Contract.RoleExists(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent) } // TransactionAllowed is a free data retrieval call binding the contract method 0xd1f77866. // -// Solidity: function transactionAllowed(string _roleId, string _orgId, string _ultParent, uint256 _typeOfTxn) constant returns(bool) +// Solidity: function transactionAllowed(string _roleId, string _orgId, string _ultParent, uint256 _typeOfTxn) view returns(bool) func (_RoleManager *RoleManagerCaller) TransactionAllowed(opts *bind.CallOpts, _roleId string, _orgId string, _ultParent string, _typeOfTxn *big.Int) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _RoleManager.contract.Call(opts, out, "transactionAllowed", _roleId, _orgId, _ultParent, _typeOfTxn) - return *ret0, err + var out []interface{} + err := _RoleManager.contract.Call(opts, &out, "transactionAllowed", _roleId, _orgId, _ultParent, _typeOfTxn) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + } // TransactionAllowed is a free data retrieval call binding the contract method 0xd1f77866. // -// Solidity: function transactionAllowed(string _roleId, string _orgId, string _ultParent, uint256 _typeOfTxn) constant returns(bool) +// Solidity: function transactionAllowed(string _roleId, string _orgId, string _ultParent, uint256 _typeOfTxn) view returns(bool) func (_RoleManager *RoleManagerSession) TransactionAllowed(_roleId string, _orgId string, _ultParent string, _typeOfTxn *big.Int) (bool, error) { return _RoleManager.Contract.TransactionAllowed(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent, _typeOfTxn) } // TransactionAllowed is a free data retrieval call binding the contract method 0xd1f77866. // -// Solidity: function transactionAllowed(string _roleId, string _orgId, string _ultParent, uint256 _typeOfTxn) constant returns(bool) +// Solidity: function transactionAllowed(string _roleId, string _orgId, string _ultParent, uint256 _typeOfTxn) view returns(bool) func (_RoleManager *RoleManagerCallerSession) TransactionAllowed(_roleId string, _orgId string, _ultParent string, _typeOfTxn *big.Int) (bool, error) { return _RoleManager.Contract.TransactionAllowed(&_RoleManager.CallOpts, _roleId, _orgId, _ultParent, _typeOfTxn) } diff --git a/permission/v2/bind/voter.go b/permission/v2/bind/voter.go index af2ba5230e..8c8ee0d18f 100644 --- a/permission/v2/bind/voter.go +++ b/permission/v2/bind/voter.go @@ -1,10 +1,9 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permission +package bind import ( - "github.com/ethereum/go-ethereum/common/math" "math/big" "strings" @@ -21,7 +20,6 @@ var ( _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = math.U256Bytes _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -158,7 +156,7 @@ func bindVoterManager(address common.Address, caller bind.ContractCaller, transa // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_VoterManager *VoterManagerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_VoterManager *VoterManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _VoterManager.Contract.VoterManagerCaller.contract.Call(opts, result, method, params...) } @@ -177,7 +175,7 @@ func (_VoterManager *VoterManagerRaw) Transact(opts *bind.TransactOpts, method s // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_VoterManager *VoterManagerCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_VoterManager *VoterManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _VoterManager.Contract.contract.Call(opts, result, method, params...) } @@ -194,34 +192,34 @@ func (_VoterManager *VoterManagerTransactorRaw) Transact(opts *bind.TransactOpts // GetPendingOpDetails is a free data retrieval call binding the contract method 0x014e6acc. // -// Solidity: function getPendingOpDetails(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOpDetails(string _orgId) view returns(string, string, address, uint256) func (_VoterManager *VoterManagerCaller) GetPendingOpDetails(opts *bind.CallOpts, _orgId string) (string, string, common.Address, *big.Int, error) { - var ( - ret0 = new(string) - ret1 = new(string) - ret2 = new(common.Address) - ret3 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - ret3, - } - err := _VoterManager.contract.Call(opts, out, "getPendingOpDetails", _orgId) - return *ret0, *ret1, *ret2, *ret3, err + var out []interface{} + err := _VoterManager.contract.Call(opts, &out, "getPendingOpDetails", _orgId) + + if err != nil { + return *new(string), *new(string), *new(common.Address), *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(string)).(*string) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return out0, out1, out2, out3, err + } // GetPendingOpDetails is a free data retrieval call binding the contract method 0x014e6acc. // -// Solidity: function getPendingOpDetails(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOpDetails(string _orgId) view returns(string, string, address, uint256) func (_VoterManager *VoterManagerSession) GetPendingOpDetails(_orgId string) (string, string, common.Address, *big.Int, error) { return _VoterManager.Contract.GetPendingOpDetails(&_VoterManager.CallOpts, _orgId) } // GetPendingOpDetails is a free data retrieval call binding the contract method 0x014e6acc. // -// Solidity: function getPendingOpDetails(string _orgId) constant returns(string, string, address, uint256) +// Solidity: function getPendingOpDetails(string _orgId) view returns(string, string, address, uint256) func (_VoterManager *VoterManagerCallerSession) GetPendingOpDetails(_orgId string) (string, string, common.Address, *big.Int, error) { return _VoterManager.Contract.GetPendingOpDetails(&_VoterManager.CallOpts, _orgId) } diff --git a/signer/core/api.go b/signer/core/api.go index 4957946e05..1857d19a27 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -43,7 +43,7 @@ const ( // numberOfAccountsToDerive For hardware wallets, the number of accounts to derive numberOfAccountsToDerive = 10 // ExternalAPIVersion -- see extapi_changelog.md - ExternalAPIVersion = "6.0.0" + ExternalAPIVersion = "6.1.0" // InternalAPIVersion -- see intapi_changelog.md InternalAPIVersion = "7.0.1" ) @@ -64,6 +64,8 @@ type ExternalAPI interface { EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) // Version info about the APIs Version(ctx context.Context) (string, error) + // SignGnosisSafeTransaction signs/confirms a gnosis-safe multisig transaction + SignGnosisSafeTx(ctx context.Context, signerAddress common.MixedcaseAddress, gnosisTx GnosisSafeTx, methodSelector *string) (*GnosisSafeTx, error) } // UIClientAPI specifies what method a UI needs to implement to be able to be used as a @@ -244,6 +246,7 @@ type ( Address common.MixedcaseAddress `json:"address"` Rawdata []byte `json:"raw_data"` Messages []*NameValueType `json:"messages"` + Callinfo []ValidationInfo `json:"call_info"` Hash hexutil.Bytes `json:"hash"` Meta Metadata `json:"meta"` } @@ -390,7 +393,9 @@ func (api *SignerAPI) startUSBListener() { // List returns the set of wallet this signer manages. Each wallet can contain // multiple accounts. func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) { - var accs []accounts.Account + var accs = make([]accounts.Account, 0) + // accs is initialized as empty list, not nil. We use 'nil' to signal + // rejection, as opposed to an empty list. for _, wallet := range api.am.Wallets() { accs = append(accs, wallet.Accounts()...) } @@ -400,13 +405,11 @@ func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) { } if result.Accounts == nil { return nil, ErrRequestDenied - } addresses := make([]common.Address, 0) for _, acc := range result.Accounts { addresses = append(addresses, acc.Address) } - return addresses, nil } @@ -597,6 +600,33 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth } +func (api *SignerAPI) SignGnosisSafeTx(ctx context.Context, signerAddress common.MixedcaseAddress, gnosisTx GnosisSafeTx, methodSelector *string) (*GnosisSafeTx, error) { + // Do the usual validations, but on the last-stage transaction + args := gnosisTx.ArgsForValidation() + msgs, err := api.validator.ValidateTransaction(methodSelector, args) + if err != nil { + return nil, err + } + // If we are in 'rejectMode', then reject rather than show the user warnings + if api.rejectMode { + if err := msgs.getWarnings(); err != nil { + return nil, err + } + } + typedData := gnosisTx.ToTypedData() + signature, preimage, err := api.signTypedData(ctx, signerAddress, typedData, msgs) + if err != nil { + return nil, err + } + checkSummedSender, _ := common.NewMixedcaseAddressFromString(signerAddress.Address().Hex()) + + gnosisTx.Signature = signature + gnosisTx.SafeTxHash = common.BytesToHash(preimage) + gnosisTx.Sender = *checkSummedSender // Must be checksumed to be accepted by relay + + return &gnosisTx, nil +} + // Returns the external api version. This method does not require user acceptance. Available methods are // available via enumeration anyway, and this info does not contain user-specific data func (api *SignerAPI) Version(ctx context.Context) (string, error) { diff --git a/signer/core/auditlog.go b/signer/core/auditlog.go index 1092e7a923..bda88a8b2e 100644 --- a/signer/core/auditlog.go +++ b/signer/core/auditlog.go @@ -18,6 +18,7 @@ package core import ( "context" + "encoding/json" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -61,13 +62,32 @@ func (l *AuditLogger) SignTransaction(ctx context.Context, args SendTxArgs, meth } func (l *AuditLogger) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) { + marshalledData, _ := json.Marshal(data) // can ignore error, marshalling what we just unmarshalled l.log.Info("SignData", "type", "request", "metadata", MetadataFromContext(ctx).String(), - "addr", addr.String(), "data", data, "content-type", contentType) + "addr", addr.String(), "data", marshalledData, "content-type", contentType) b, e := l.api.SignData(ctx, contentType, addr, data) l.log.Info("SignData", "type", "response", "data", common.Bytes2Hex(b), "error", e) return b, e } +func (l *AuditLogger) SignGnosisSafeTx(ctx context.Context, addr common.MixedcaseAddress, gnosisTx GnosisSafeTx, methodSelector *string) (*GnosisSafeTx, error) { + sel := "" + if methodSelector != nil { + sel = *methodSelector + } + data, _ := json.Marshal(gnosisTx) // can ignore error, marshalling what we just unmarshalled + l.log.Info("SignGnosisSafeTx", "type", "request", "metadata", MetadataFromContext(ctx).String(), + "addr", addr.String(), "data", string(data), "selector", sel) + res, e := l.api.SignGnosisSafeTx(ctx, addr, gnosisTx, methodSelector) + if res != nil { + data, _ := json.Marshal(res) // can ignore error, marshalling what we just unmarshalled + l.log.Info("SignGnosisSafeTx", "type", "response", "data", string(data), "error", e) + } else { + l.log.Info("SignGnosisSafeTx", "type", "response", "data", res, "error", e) + } + return res, e +} + func (l *AuditLogger) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) { l.log.Info("SignTypedData", "type", "request", "metadata", MetadataFromContext(ctx).String(), "addr", addr.String(), "data", data) diff --git a/signer/core/cliui.go b/signer/core/cliui.go index 27a2f71aaa..cbfb56c9df 100644 --- a/signer/core/cliui.go +++ b/signer/core/cliui.go @@ -148,6 +148,13 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResp fmt.Printf("-------- Sign data request--------------\n") fmt.Printf("Account: %s\n", request.Address.String()) + if len(request.Callinfo) != 0 { + fmt.Printf("\nValidation messages:\n") + for _, m := range request.Callinfo { + fmt.Printf(" * %s : %s\n", m.Typ, m.Message) + } + fmt.Println() + } fmt.Printf("messages:\n") for _, nvt := range request.Messages { fmt.Printf("\u00a0\u00a0%v\n", strings.TrimSpace(nvt.Pprint(1))) diff --git a/signer/core/gnosis_safe.go b/signer/core/gnosis_safe.go new file mode 100644 index 0000000000..e4385f9dc3 --- /dev/null +++ b/signer/core/gnosis_safe.go @@ -0,0 +1,91 @@ +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +// GnosisSafeTx is a type to parse the safe-tx returned by the relayer, +// it also conforms to the API required by the Gnosis Safe tx relay service. +// See 'SafeMultisigTransaction' on https://safe-transaction.mainnet.gnosis.io/ +type GnosisSafeTx struct { + // These fields are only used on output + Signature hexutil.Bytes `json:"signature"` + SafeTxHash common.Hash `json:"contractTransactionHash"` + Sender common.MixedcaseAddress `json:"sender"` + // These fields are used both on input and output + Safe common.MixedcaseAddress `json:"safe"` + To common.MixedcaseAddress `json:"to"` + Value math.Decimal256 `json:"value"` + GasPrice math.Decimal256 `json:"gasPrice"` + Data *hexutil.Bytes `json:"data"` + Operation uint8 `json:"operation"` + GasToken common.Address `json:"gasToken"` + RefundReceiver common.Address `json:"refundReceiver"` + BaseGas big.Int `json:"baseGas"` + SafeTxGas big.Int `json:"safeTxGas"` + Nonce big.Int `json:"nonce"` + InputExpHash common.Hash `json:"safeTxHash"` +} + +// ToTypedData converts the tx to a EIP-712 Typed Data structure for signing +func (tx *GnosisSafeTx) ToTypedData() TypedData { + var data hexutil.Bytes + if tx.Data != nil { + data = *tx.Data + } + gnosisTypedData := TypedData{ + Types: Types{ + "EIP712Domain": []Type{{Name: "verifyingContract", Type: "address"}}, + "SafeTx": []Type{ + {Name: "to", Type: "address"}, + {Name: "value", Type: "uint256"}, + {Name: "data", Type: "bytes"}, + {Name: "operation", Type: "uint8"}, + {Name: "safeTxGas", Type: "uint256"}, + {Name: "baseGas", Type: "uint256"}, + {Name: "gasPrice", Type: "uint256"}, + {Name: "gasToken", Type: "address"}, + {Name: "refundReceiver", Type: "address"}, + {Name: "nonce", Type: "uint256"}, + }, + }, + Domain: TypedDataDomain{ + VerifyingContract: tx.Safe.Address().Hex(), + }, + PrimaryType: "SafeTx", + Message: TypedDataMessage{ + "to": tx.To.Address().Hex(), + "value": tx.Value.String(), + "data": data, + "operation": fmt.Sprintf("%d", tx.Operation), + "safeTxGas": fmt.Sprintf("%#d", &tx.SafeTxGas), + "baseGas": fmt.Sprintf("%#d", &tx.BaseGas), + "gasPrice": tx.GasPrice.String(), + "gasToken": tx.GasToken.Hex(), + "refundReceiver": tx.RefundReceiver.Hex(), + "nonce": fmt.Sprintf("%d", tx.Nonce.Uint64()), + }, + } + return gnosisTypedData +} + +// ArgsForValidation returns a SendTxArgs struct, which can be used for the +// common validations, e.g. look up 4byte destinations +func (tx *GnosisSafeTx) ArgsForValidation() *SendTxArgs { + args := &SendTxArgs{ + From: tx.Safe, + To: &tx.To, + Gas: hexutil.Uint64(tx.SafeTxGas.Uint64()), + GasPrice: hexutil.Big(tx.GasPrice), + Value: hexutil.Big(tx.Value), + Nonce: hexutil.Uint64(tx.Nonce.Uint64()), + Data: tx.Data, + Input: nil, + } + return args +} diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go index 7fc66b4b74..19377a521b 100644 --- a/signer/core/signed_data.go +++ b/signer/core/signed_data.go @@ -125,7 +125,7 @@ var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`) // // Note, the produced signature conforms to the secp256k1 curve R, S and V values, // where the V value will be 27 or 28 for legacy reasons, if legacyV==true. -func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, legacyV bool) (hexutil.Bytes, error) { +func (api *SignerAPI) sign(req *SignDataRequest, legacyV bool) (hexutil.Bytes, error) { // We make the request prior to looking up if we actually have the account, to prevent // account-enumeration via the API res, err := api.UI.ApproveSignData(req) @@ -136,7 +136,7 @@ func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, l return nil, ErrRequestDenied } // Look up the wallet containing the requested signer - account := accounts.Account{Address: addr.Address()} + account := accounts.Account{Address: req.Address.Address()} wallet, err := api.am.Find(account) if err != nil { return nil, err @@ -167,7 +167,7 @@ func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr com if err != nil { return nil, err } - signature, err := api.sign(addr, req, transformV) + signature, err := api.sign(req, transformV) if err != nil { api.UI.ShowError(err.Error()) return nil, err @@ -312,28 +312,47 @@ func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) // SignTypedData signs EIP-712 conformant typed data // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") +// It returns +// - the signature, +// - and/or any error func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) { + signature, _, err := api.signTypedData(ctx, addr, typedData, nil) + return signature, err +} + +// signTypedData is identical to the capitalized version, except that it also returns the hash (preimage) +// - the signature preimage (hash) +func (api *SignerAPI) signTypedData(ctx context.Context, addr common.MixedcaseAddress, + typedData TypedData, validationMessages *ValidationMessages) (hexutil.Bytes, hexutil.Bytes, error) { domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) if err != nil { - return nil, err + return nil, nil, err } typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) if err != nil { - return nil, err + return nil, nil, err } rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) sighash := crypto.Keccak256(rawData) messages, err := typedData.Format() if err != nil { - return nil, err - } - req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Messages: messages, Hash: sighash} - signature, err := api.sign(addr, req, true) + return nil, nil, err + } + req := &SignDataRequest{ + ContentType: DataTyped.Mime, + Rawdata: rawData, + Messages: messages, + Hash: sighash, + Address: addr} + if validationMessages != nil { + req.Callinfo = validationMessages.Messages + } + signature, err := api.sign(req, true) if err != nil { api.UI.ShowError(err.Error()) - return nil, err + return nil, nil, err } - return signature, nil + return signature, sighash, nil } // HashStruct generates a keccak256 hash of the encoding of the provided data @@ -420,8 +439,8 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter buffer := bytes.Buffer{} // Verify extra data - if len(typedData.Types[primaryType]) < len(data) { - return nil, errors.New("there is extra data provided in the message") + if exp, got := len(typedData.Types[primaryType]), len(data); exp < got { + return nil, fmt.Errorf("there is extra data provided in the message (%d < %d)", exp, got) } // Add typehash @@ -834,7 +853,11 @@ func (nvt *NameValueType) Pprint(depth int) string { output.WriteString(sublevel) } } else { - output.WriteString(fmt.Sprintf("%q\n", nvt.Value)) + if nvt.Value != nil { + output.WriteString(fmt.Sprintf("%q\n", nvt.Value)) + } else { + output.WriteString("\n") + } } return output.String() } diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go index ab5f2cc962..23b7b9897b 100644 --- a/signer/core/signed_data_test.go +++ b/signer/core/signed_data_test.go @@ -17,6 +17,7 @@ package core_test import ( + "bytes" "context" "encoding/json" "fmt" @@ -414,3 +415,119 @@ func TestFuzzerFiles(t *testing.T) { typedData.Format() } } + +var gnosisTypedData = ` +{ + "types": { + "EIP712Domain": [ + { "type": "address", "name": "verifyingContract" } + ], + "SafeTx": [ + { "type": "address", "name": "to" }, + { "type": "uint256", "name": "value" }, + { "type": "bytes", "name": "data" }, + { "type": "uint8", "name": "operation" }, + { "type": "uint256", "name": "safeTxGas" }, + { "type": "uint256", "name": "baseGas" }, + { "type": "uint256", "name": "gasPrice" }, + { "type": "address", "name": "gasToken" }, + { "type": "address", "name": "refundReceiver" }, + { "type": "uint256", "name": "nonce" } + ] + }, + "domain": { + "verifyingContract": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3" + }, + "primaryType": "SafeTx", + "message": { + "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", + "value": "20000000000000000", + "data": "0x", + "operation": 0, + "safeTxGas": 27845, + "baseGas": 0, + "gasPrice": "0", + "gasToken": "0x0000000000000000000000000000000000000000", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": 3 + } +}` + +var gnosisTx = ` +{ + "safe": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3", + "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", + "value": "20000000000000000", + "data": null, + "operation": 0, + "gasToken": "0x0000000000000000000000000000000000000000", + "safeTxGas": 27845, + "baseGas": 0, + "gasPrice": "0", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": 3, + "executionDate": null, + "submissionDate": "2020-09-15T21:59:23.815748Z", + "modified": "2020-09-15T21:59:23.815748Z", + "blockNumber": null, + "transactionHash": null, + "safeTxHash": "0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f", + "executor": null, + "isExecuted": false, + "isSuccessful": null, + "ethGasPrice": null, + "gasUsed": null, + "fee": null, + "origin": null, + "dataDecoded": null, + "confirmationsRequired": null, + "confirmations": [ + { + "owner": "0xAd2e180019FCa9e55CADe76E4487F126Fd08DA34", + "submissionDate": "2020-09-15T21:59:28.281243Z", + "transactionHash": null, + "confirmationType": "CONFIRMATION", + "signature": "0x5e562065a0cb15d766dac0cd49eb6d196a41183af302c4ecad45f1a81958d7797753f04424a9b0aa1cb0448e4ec8e189540fbcdda7530ef9b9d95dfc2d36cb521b", + "signatureType": "EOA" + } + ], + "signatures": null + } +` + +// TestGnosisTypedData tests the scenario where a user submits a full EIP-712 +// struct without using the gnosis-specific endpoint +func TestGnosisTypedData(t *testing.T) { + var td core.TypedData + err := json.Unmarshal([]byte(gnosisTypedData), &td) + if err != nil { + t.Fatalf("unmarshalling failed '%v'", err) + } + _, sighash, err := sign(td) + if err != nil { + t.Fatal(err) + } + expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") + if !bytes.Equal(expSigHash, sighash) { + t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) + } +} + +// TestGnosisCustomData tests the scenario where a user submits only the gnosis-safe +// specific data, and we fill the TypedData struct on our side +func TestGnosisCustomData(t *testing.T) { + var tx core.GnosisSafeTx + err := json.Unmarshal([]byte(gnosisTx), &tx) + if err != nil { + t.Fatal(err) + } + var td = tx.ToTypedData() + _, sighash, err := sign(td) + if err != nil { + t.Fatal(err) + } + expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") + if !bytes.Equal(expSigHash, sighash) { + t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) + } +} diff --git a/tests/fuzzers/abi/abifuzzer.go b/tests/fuzzers/abi/abifuzzer.go index ed5c7c0586..76d3c800f7 100644 --- a/tests/fuzzers/abi/abifuzzer.go +++ b/tests/fuzzers/abi/abifuzzer.go @@ -30,7 +30,7 @@ import ( func unpackPack(abi abi.ABI, method string, inputType []interface{}, input []byte) bool { outptr := reflect.New(reflect.TypeOf(inputType)) - if err := abi.Unpack(outptr.Interface(), method, input); err == nil { + if err := abi.UnpackIntoInterface(outptr.Interface(), method, input); err == nil { output, err := abi.Pack(method, input) if err != nil { // We have some false positives as we can unpack these type successfully, but not pack them @@ -51,7 +51,7 @@ func unpackPack(abi abi.ABI, method string, inputType []interface{}, input []byt func packUnpack(abi abi.ABI, method string, input []interface{}) bool { if packed, err := abi.Pack(method, input); err == nil { outptr := reflect.New(reflect.TypeOf(input)) - err := abi.Unpack(outptr.Interface(), method, packed) + err := abi.UnpackIntoInterface(outptr.Interface(), method, packed) if err != nil { panic(err) } diff --git a/trie/committer.go b/trie/committer.go index fc8b7ceda5..20c95bed08 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" ) @@ -33,10 +32,9 @@ const leafChanSize = 200 // leaf represents a trie leaf value type leaf struct { - size int // size of the rlp data (estimate) - hash common.Hash // hash of rlp data - node node // the node to commit - vnodes bool // set to true if the node (possibly) contains a valueNode + size int // size of the rlp data (estimate) + hash common.Hash // hash of rlp data + node node // the node to commit } // committer is a type used for the trie Commit operation. A committer has some @@ -74,18 +72,12 @@ func returnCommitterToPool(h *committer) { committerPool.Put(h) } -// commitNeeded returns 'false' if the given node is already in sync with db -func (c *committer) commitNeeded(n node) bool { - hash, dirty := n.cache() - return hash == nil || dirty -} - // commit collapses a node down into a hash node and inserts it into the database func (c *committer) Commit(n node, db *Database) (hashNode, error) { if db == nil { return nil, errors.New("no db provided") } - h, err := c.commit(n, db, true) + h, err := c.commit(n, db) if err != nil { return nil, err } @@ -93,7 +85,7 @@ func (c *committer) Commit(n node, db *Database) (hashNode, error) { } // commit collapses a node down into a hash node and inserts it into the database -func (c *committer) commit(n node, db *Database, force bool) (node, error) { +func (c *committer) commit(n node, db *Database) (node, error) { // if this path is clean, use available cached data hash, dirty := n.cache() if hash != nil && !dirty { @@ -104,8 +96,11 @@ func (c *committer) commit(n node, db *Database, force bool) (node, error) { case *shortNode: // Commit child collapsed := cn.copy() - if _, ok := cn.Val.(valueNode); !ok { - childV, err := c.commit(cn.Val, db, false) + + // If the child is fullnode, recursively commit. + // Otherwise it can only be hashNode or valueNode. + if _, ok := cn.Val.(*fullNode); ok { + childV, err := c.commit(cn.Val, db) if err != nil { return nil, err } @@ -113,78 +108,78 @@ func (c *committer) commit(n node, db *Database, force bool) (node, error) { } // The key needs to be copied, since we're delivering it to database collapsed.Key = hexToCompact(cn.Key) - hashedNode := c.store(collapsed, db, force, true) + hashedNode := c.store(collapsed, db) if hn, ok := hashedNode.(hashNode); ok { return hn, nil } return collapsed, nil case *fullNode: - hashedKids, hasVnodes, err := c.commitChildren(cn, db, force) + hashedKids, err := c.commitChildren(cn, db) if err != nil { return nil, err } collapsed := cn.copy() collapsed.Children = hashedKids - hashedNode := c.store(collapsed, db, force, hasVnodes) + hashedNode := c.store(collapsed, db) if hn, ok := hashedNode.(hashNode); ok { return hn, nil } return collapsed, nil - case valueNode: - return c.store(cn, db, force, false), nil - // hashnodes aren't stored case hashNode: return cn, nil + default: + // nil, valuenode shouldn't be committed + panic(fmt.Sprintf("%T: invalid node: %v", n, n)) } - return hash, nil } // commitChildren commits the children of the given fullnode -func (c *committer) commitChildren(n *fullNode, db *Database, force bool) ([17]node, bool, error) { +func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, error) { var children [17]node - var hasValueNodeChildren = false - for i, child := range n.Children { + for i := 0; i < 16; i++ { + child := n.Children[i] if child == nil { continue } - hnode, err := c.commit(child, db, false) - if err != nil { - return children, false, err + // If it's the hashed child, save the hash value directly. + // Note: it's impossible that the child in range [0, 15] + // is a valuenode. + if hn, ok := child.(hashNode); ok { + children[i] = hn + continue } - children[i] = hnode - if _, ok := hnode.(valueNode); ok { - hasValueNodeChildren = true + // Commit the child recursively and store the "hashed" value. + // Note the returned node can be some embedded nodes, so it's + // possible the type is not hashnode. + hashed, err := c.commit(child, db) + if err != nil { + return children, err } + children[i] = hashed } - return children, hasValueNodeChildren, nil + // For the 17th child, it's possible the type is valuenode. + if n.Children[16] != nil { + children[16] = n.Children[16] + } + return children, nil } // store hashes the node n and if we have a storage layer specified, it writes // the key/value pair to it and tracks any node->child references as well as any // node->external trie references. -func (c *committer) store(n node, db *Database, force bool, hasVnodeChildren bool) node { +func (c *committer) store(n node, db *Database) node { // Larger nodes are replaced by their hash and stored in the database. var ( hash, _ = n.cache() size int ) if hash == nil { - if vn, ok := n.(valueNode); ok { - c.tmp.Reset() - if err := rlp.Encode(&c.tmp, vn); err != nil { - panic("encode error: " + err.Error()) - } - size = len(c.tmp) - if size < 32 && !force { - return n // Nodes smaller than 32 bytes are stored inside their parent - } - hash = c.makeHashNode(c.tmp) - } else { - // This was not generated - must be a small node stored in the parent - // No need to do anything here - return n - } + // This was not generated - must be a small node stored in the parent. + // In theory we should apply the leafCall here if it's not nil(embedded + // node usually contains value). But small value(less than 32bytes) is + // not our target. + return n } else { // We have the hash already, estimate the RLP encoding-size of the node. // The size is used for mem tracking, does not need to be exact @@ -194,10 +189,9 @@ func (c *committer) store(n node, db *Database, force bool, hasVnodeChildren boo // The leaf channel will be active only when there an active leaf-callback if c.leafCh != nil { c.leafCh <- &leaf{ - size: size, - hash: common.BytesToHash(hash), - node: n, - vnodes: hasVnodeChildren, + size: size, + hash: common.BytesToHash(hash), + node: n, } } else if db != nil { // No leaf-callback used, but there's still a database. Do serial @@ -209,30 +203,30 @@ func (c *committer) store(n node, db *Database, force bool, hasVnodeChildren boo return hash } -// commitLoop does the actual insert + leaf callback for nodes +// commitLoop does the actual insert + leaf callback for nodes. func (c *committer) commitLoop(db *Database) { for item := range c.leafCh { var ( - hash = item.hash - size = item.size - n = item.node - hasVnodes = item.vnodes + hash = item.hash + size = item.size + n = item.node ) // We are pooling the trie nodes into an intermediate memory cache db.lock.Lock() db.insert(hash, size, n) db.lock.Unlock() - if c.onleaf != nil && hasVnodes { + + if c.onleaf != nil { switch n := n.(type) { case *shortNode: if child, ok := n.Val.(valueNode); ok { c.onleaf(nil, child, hash) } case *fullNode: - for i := 0; i < 16; i++ { - if child, ok := n.Children[i].(valueNode); ok { - c.onleaf(nil, child, hash) - } + // For children in range [0, 15], it's impossible + // to contain valuenode. Only check the 17th child. + if n.Children[16] != nil { + c.onleaf(nil, n.Children[16].(valueNode), hash) } } } diff --git a/trie/database.go b/trie/database.go index 87ce25d19e..d2d652b6ba 100644 --- a/trie/database.go +++ b/trie/database.go @@ -99,6 +99,11 @@ type rawNode []byte func (n rawNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") } func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") } +func (n rawNode) EncodeRLP(w io.Writer) error { + _, err := w.Write([]byte(n)) + return err +} + // rawFullNode represents only the useful data content of a full node, with the // caches and flags stripped out to minimize its data storage. This type honors // the same RLP encoding as the original parent. @@ -202,7 +207,7 @@ func forGatherChildren(n node, onChild func(hash common.Hash)) { } case hashNode: onChild(common.BytesToHash(n)) - case valueNode, nil: + case valueNode, nil, rawNode: default: panic(fmt.Sprintf("unknown node type: %T", n)) } diff --git a/trie/encoding.go b/trie/encoding.go index 1955a3e664..8ee0022ef3 100644 --- a/trie/encoding.go +++ b/trie/encoding.go @@ -51,6 +51,35 @@ func hexToCompact(hex []byte) []byte { return buf } +// hexToCompactInPlace places the compact key in input buffer, returning the length +// needed for the representation +func hexToCompactInPlace(hex []byte) int { + var ( + hexLen = len(hex) // length of the hex input + firstByte = byte(0) + ) + // Check if we have a terminator there + if hexLen > 0 && hex[hexLen-1] == 16 { + firstByte = 1 << 5 + hexLen-- // last part was the terminator, ignore that + } + var ( + binLen = hexLen/2 + 1 + ni = 0 // index in hex + bi = 1 // index in bin (compact) + ) + if hexLen&1 == 1 { + firstByte |= 1 << 4 // odd flag + firstByte |= hex[0] // first nibble is contained in the first byte + ni++ + } + for ; ni < hexLen; bi, ni = bi+1, ni+2 { + hex[bi] = hex[ni]<<4 | hex[ni+1] + } + hex[0] = firstByte + return binLen +} + func compactToHex(compact []byte) []byte { if len(compact) == 0 { return compact diff --git a/trie/encoding_test.go b/trie/encoding_test.go index 97d8da1361..16393313f7 100644 --- a/trie/encoding_test.go +++ b/trie/encoding_test.go @@ -18,6 +18,8 @@ package trie import ( "bytes" + "encoding/hex" + "math/rand" "testing" ) @@ -75,6 +77,40 @@ func TestHexKeybytes(t *testing.T) { } } +func TestHexToCompactInPlace(t *testing.T) { + for i, keyS := range []string{ + "00", + "060a040c0f000a090b040803010801010900080d090a0a0d0903000b10", + "10", + } { + hexBytes, _ := hex.DecodeString(keyS) + exp := hexToCompact(hexBytes) + sz := hexToCompactInPlace(hexBytes) + got := hexBytes[:sz] + if !bytes.Equal(exp, got) { + t.Fatalf("test %d: encoding err\ninp %v\ngot %x\nexp %x\n", i, keyS, got, exp) + } + } +} + +func TestHexToCompactInPlaceRandom(t *testing.T) { + for i := 0; i < 10000; i++ { + l := rand.Intn(128) + key := make([]byte, l) + rand.Read(key) + hexBytes := keybytesToHex(key) + hexOrig := []byte(string(hexBytes)) + exp := hexToCompact(hexBytes) + sz := hexToCompactInPlace(hexBytes) + got := hexBytes[:sz] + + if !bytes.Equal(exp, got) { + t.Fatalf("encoding err \ncpt %x\nhex %x\ngot %x\nexp %x\n", + key, hexOrig, got, exp) + } + } +} + func BenchmarkHexToCompact(b *testing.B) { testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/} for i := 0; i < b.N; i++ { diff --git a/trie/hasher.go b/trie/hasher.go index 57cd3e1f36..3a62a2f119 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -66,11 +66,11 @@ func returnHasherToPool(h *hasher) { // hash collapses a node down into a hash node, also returning a copy of the // original node initialized with the computed hash to replace the original one. func (h *hasher) hash(n node, force bool) (hashed node, cached node) { - // We're not storing the node, just hashing, use available cached data + // Return the cached hash if it's available if hash, _ := n.cache(); hash != nil { return hash, n } - // Trie not processed yet or needs storage, walk the children + // Trie not processed yet, walk the children switch n := n.(type) { case *shortNode: collapsed, cached := h.hashShortNodeChildren(n) diff --git a/trie/stacktrie.go b/trie/stacktrie.go new file mode 100644 index 0000000000..ffccbbf4ac --- /dev/null +++ b/trie/stacktrie.go @@ -0,0 +1,411 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +var ErrCommitDisabled = errors.New("no database for committing") + +var stPool = sync.Pool{ + New: func() interface{} { + return NewStackTrie(nil) + }, +} + +func stackTrieFromPool(db ethdb.KeyValueStore) *StackTrie { + st := stPool.Get().(*StackTrie) + st.db = db + return st +} + +func returnToPool(st *StackTrie) { + st.Reset() + stPool.Put(st) +} + +// StackTrie is a trie implementation that expects keys to be inserted +// in order. Once it determines that a subtree will no longer be inserted +// into, it will hash it and free up the memory it uses. +type StackTrie struct { + nodeType uint8 // node type (as in branch, ext, leaf) + val []byte // value contained by this node if it's a leaf + key []byte // key chunk covered by this (full|ext) node + keyOffset int // offset of the key chunk inside a full key + children [16]*StackTrie // list of children (for fullnodes and exts) + + db ethdb.KeyValueStore // Pointer to the commit db, can be nil +} + +// NewStackTrie allocates and initializes an empty trie. +func NewStackTrie(db ethdb.KeyValueStore) *StackTrie { + return &StackTrie{ + nodeType: emptyNode, + db: db, + } +} + +func newLeaf(ko int, key, val []byte, db ethdb.KeyValueStore) *StackTrie { + st := stackTrieFromPool(db) + st.nodeType = leafNode + st.keyOffset = ko + st.key = append(st.key, key[ko:]...) + st.val = val + return st +} + +func newExt(ko int, key []byte, child *StackTrie, db ethdb.KeyValueStore) *StackTrie { + st := stackTrieFromPool(db) + st.nodeType = extNode + st.keyOffset = ko + st.key = append(st.key, key[ko:]...) + st.children[0] = child + return st +} + +// List all values that StackTrie#nodeType can hold +const ( + emptyNode = iota + branchNode + extNode + leafNode + hashedNode +) + +// TryUpdate inserts a (key, value) pair into the stack trie +func (st *StackTrie) TryUpdate(key, value []byte) error { + k := keybytesToHex(key) + if len(value) == 0 { + panic("deletion not supported") + } + st.insert(k[:len(k)-1], value) + return nil +} + +func (st *StackTrie) Update(key, value []byte) { + if err := st.TryUpdate(key, value); err != nil { + log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) + } +} + +func (st *StackTrie) Reset() { + st.db = nil + st.key = st.key[:0] + st.val = nil + for i := range st.children { + st.children[i] = nil + } + st.nodeType = emptyNode + st.keyOffset = 0 +} + +// Helper function that, given a full key, determines the index +// at which the chunk pointed by st.keyOffset is different from +// the same chunk in the full key. +func (st *StackTrie) getDiffIndex(key []byte) int { + diffindex := 0 + for ; diffindex < len(st.key) && st.key[diffindex] == key[st.keyOffset+diffindex]; diffindex++ { + } + return diffindex +} + +// Helper function to that inserts a (key, value) pair into +// the trie. +func (st *StackTrie) insert(key, value []byte) { + switch st.nodeType { + case branchNode: /* Branch */ + idx := int(key[st.keyOffset]) + // Unresolve elder siblings + for i := idx - 1; i >= 0; i-- { + if st.children[i] != nil { + if st.children[i].nodeType != hashedNode { + st.children[i].hash() + } + break + } + } + // Add new child + if st.children[idx] == nil { + st.children[idx] = stackTrieFromPool(st.db) + st.children[idx].keyOffset = st.keyOffset + 1 + } + st.children[idx].insert(key, value) + case extNode: /* Ext */ + // Compare both key chunks and see where they differ + diffidx := st.getDiffIndex(key) + + // Check if chunks are identical. If so, recurse into + // the child node. Otherwise, the key has to be split + // into 1) an optional common prefix, 2) the fullnode + // representing the two differing path, and 3) a leaf + // for each of the differentiated subtrees. + if diffidx == len(st.key) { + // Ext key and key segment are identical, recurse into + // the child node. + st.children[0].insert(key, value) + return + } + // Save the original part. Depending if the break is + // at the extension's last byte or not, create an + // intermediate extension or use the extension's child + // node directly. + var n *StackTrie + if diffidx < len(st.key)-1 { + n = newExt(diffidx+1, st.key, st.children[0], st.db) + } else { + // Break on the last byte, no need to insert + // an extension node: reuse the current node + n = st.children[0] + } + // Convert to hash + n.hash() + var p *StackTrie + if diffidx == 0 { + // the break is on the first byte, so + // the current node is converted into + // a branch node. + st.children[0] = nil + p = st + st.nodeType = branchNode + } else { + // the common prefix is at least one byte + // long, insert a new intermediate branch + // node. + st.children[0] = stackTrieFromPool(st.db) + st.children[0].nodeType = branchNode + st.children[0].keyOffset = st.keyOffset + diffidx + p = st.children[0] + } + // Create a leaf for the inserted part + o := newLeaf(st.keyOffset+diffidx+1, key, value, st.db) + + // Insert both child leaves where they belong: + origIdx := st.key[diffidx] + newIdx := key[diffidx+st.keyOffset] + p.children[origIdx] = n + p.children[newIdx] = o + st.key = st.key[:diffidx] + + case leafNode: /* Leaf */ + // Compare both key chunks and see where they differ + diffidx := st.getDiffIndex(key) + + // Overwriting a key isn't supported, which means that + // the current leaf is expected to be split into 1) an + // optional extension for the common prefix of these 2 + // keys, 2) a fullnode selecting the path on which the + // keys differ, and 3) one leaf for the differentiated + // component of each key. + if diffidx >= len(st.key) { + panic("Trying to insert into existing key") + } + + // Check if the split occurs at the first nibble of the + // chunk. In that case, no prefix extnode is necessary. + // Otherwise, create that + var p *StackTrie + if diffidx == 0 { + // Convert current leaf into a branch + st.nodeType = branchNode + p = st + st.children[0] = nil + } else { + // Convert current node into an ext, + // and insert a child branch node. + st.nodeType = extNode + st.children[0] = NewStackTrie(st.db) + st.children[0].nodeType = branchNode + st.children[0].keyOffset = st.keyOffset + diffidx + p = st.children[0] + } + + // Create the two child leaves: the one containing the + // original value and the one containing the new value + // The child leave will be hashed directly in order to + // free up some memory. + origIdx := st.key[diffidx] + p.children[origIdx] = newLeaf(diffidx+1, st.key, st.val, st.db) + p.children[origIdx].hash() + + newIdx := key[diffidx+st.keyOffset] + p.children[newIdx] = newLeaf(p.keyOffset+1, key, value, st.db) + + // Finally, cut off the key part that has been passed + // over to the children. + st.key = st.key[:diffidx] + st.val = nil + case emptyNode: /* Empty */ + st.nodeType = leafNode + st.key = key[st.keyOffset:] + st.val = value + case hashedNode: + panic("trying to insert into hash") + default: + panic("invalid type") + } +} + +// hash() hashes the node 'st' and converts it into 'hashedNode', if possible. +// Possible outcomes: +// 1. The rlp-encoded value was >= 32 bytes: +// - Then the 32-byte `hash` will be accessible in `st.val`. +// - And the 'st.type' will be 'hashedNode' +// 2. The rlp-encoded value was < 32 bytes +// - Then the <32 byte rlp-encoded value will be accessible in 'st.val'. +// - And the 'st.type' will be 'hashedNode' AGAIN +// +// This method will also: +// set 'st.type' to hashedNode +// clear 'st.key' +func (st *StackTrie) hash() { + /* Shortcut if node is already hashed */ + if st.nodeType == hashedNode { + return + } + // The 'hasher' is taken from a pool, but we don't actually + // claim an instance until all children are done with their hashing, + // and we actually need one + var h *hasher + + switch st.nodeType { + case branchNode: + var nodes [17]node + for i, child := range st.children { + if child == nil { + nodes[i] = nilValueNode + continue + } + child.hash() + if len(child.val) < 32 { + nodes[i] = rawNode(child.val) + } else { + nodes[i] = hashNode(child.val) + } + st.children[i] = nil // Reclaim mem from subtree + returnToPool(child) + } + nodes[16] = nilValueNode + h = newHasher(false) + defer returnHasherToPool(h) + h.tmp.Reset() + if err := rlp.Encode(&h.tmp, nodes); err != nil { + panic(err) + } + case extNode: + h = newHasher(false) + defer returnHasherToPool(h) + h.tmp.Reset() + st.children[0].hash() + // This is also possible: + //sz := hexToCompactInPlace(st.key) + //n := [][]byte{ + // st.key[:sz], + // st.children[0].val, + //} + n := [][]byte{ + hexToCompact(st.key), + st.children[0].val, + } + if err := rlp.Encode(&h.tmp, n); err != nil { + panic(err) + } + returnToPool(st.children[0]) + st.children[0] = nil // Reclaim mem from subtree + case leafNode: + h = newHasher(false) + defer returnHasherToPool(h) + h.tmp.Reset() + st.key = append(st.key, byte(16)) + sz := hexToCompactInPlace(st.key) + n := [][]byte{st.key[:sz], st.val} + if err := rlp.Encode(&h.tmp, n); err != nil { + panic(err) + } + case emptyNode: + st.val = st.val[:0] + st.val = append(st.val, emptyRoot[:]...) + st.key = st.key[:0] + st.nodeType = hashedNode + return + default: + panic("Invalid node type") + } + st.key = st.key[:0] + st.nodeType = hashedNode + if len(h.tmp) < 32 { + st.val = st.val[:0] + st.val = append(st.val, h.tmp...) + return + } + // Going to write the hash to the 'val'. Need to ensure it's properly sized first + // Typically, 'branchNode's will have no 'val', and require this allocation + if required := 32 - len(st.val); required > 0 { + buf := make([]byte, required) + st.val = append(st.val, buf...) + } + st.val = st.val[:32] + h.sha.Reset() + h.sha.Write(h.tmp) + h.sha.Read(st.val) + if st.db != nil { + // TODO! Is it safe to Put the slice here? + // Do all db implementations copy the value provided? + st.db.Put(st.val, h.tmp) + } +} + +// Hash returns the hash of the current node +func (st *StackTrie) Hash() (h common.Hash) { + st.hash() + if len(st.val) != 32 { + // If the node's RLP isn't 32 bytes long, the node will not + // be hashed, and instead contain the rlp-encoding of the + // node. For the top level node, we need to force the hashing. + ret := make([]byte, 32) + h := newHasher(false) + defer returnHasherToPool(h) + h.sha.Reset() + h.sha.Write(st.val) + h.sha.Read(ret) + return common.BytesToHash(ret) + } + return common.BytesToHash(st.val) +} + +// Commit will firstly hash the entrie trie if it's still not hashed +// and then commit all nodes to the associated database. Actually most +// of the trie nodes MAY have been committed already. The main purpose +// here is to commit the root node. +// +// The associated database is expected, otherwise the whole commit +// functionality should be disabled. +func (st *StackTrie) Commit() (common.Hash, error) { + if st.db == nil { + return common.Hash{}, ErrCommitDisabled + } + st.hash() + h := common.BytesToHash(st.val) + return h, nil +} diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go new file mode 100644 index 0000000000..26e3bade27 --- /dev/null +++ b/trie/stacktrie_test.go @@ -0,0 +1,242 @@ +package trie + +import ( + "bytes" + "fmt" + "math/big" + mrand "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb/memorydb" +) + +func TestSizeBug(t *testing.T) { + st := NewStackTrie(nil) + nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) + + leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") + value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") + + nt.TryUpdate(leaf, value) + st.TryUpdate(leaf, value) + + if nt.Hash() != st.Hash() { + t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) + } +} + +func TestEmptyBug(t *testing.T) { + st := NewStackTrie(nil) + nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) + + //leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") + //value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") + kvs := []struct { + K string + V string + }{ + {K: "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace", V: "9496f4ec2bf9dab484cac6be589e8417d84781be08"}, + {K: "40edb63a35fcf86c08022722aa3287cdd36440d671b4918131b2514795fefa9c", V: "01"}, + {K: "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", V: "947a30f7736e48d6599356464ba4c150d8da0302ff"}, + {K: "c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b", V: "02"}, + } + + for _, kv := range kvs { + nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) + st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) + } + + if nt.Hash() != st.Hash() { + t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) + } +} + +func TestValLength56(t *testing.T) { + st := NewStackTrie(nil) + nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) + + //leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") + //value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") + kvs := []struct { + K string + V string + }{ + {K: "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace", V: "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"}, + } + + for _, kv := range kvs { + nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) + st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) + } + + if nt.Hash() != st.Hash() { + t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) + } +} + +func genTxs(num uint64) (types.Transactions, error) { + key, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") + if err != nil { + return nil, err + } + var addr = crypto.PubkeyToAddress(key.PublicKey) + newTx := func(i uint64) (*types.Transaction, error) { + signer := types.NewEIP155Signer(big.NewInt(18)) + tx, err := types.SignTx(types.NewTransaction(i, addr, new(big.Int), 0, new(big.Int).SetUint64(10000000), nil), signer, key) + return tx, err + } + var txs types.Transactions + for i := uint64(0); i < num; i++ { + tx, err := newTx(i) + if err != nil { + return nil, err + } + txs = append(txs, tx) + } + return txs, nil +} + +func TestDeriveSha(t *testing.T) { + txs, err := genTxs(0) + if err != nil { + t.Fatal(err) + } + for len(txs) < 1000 { + exp := types.DeriveSha(txs, newEmpty()) + got := types.DeriveSha(txs, NewStackTrie(nil)) + if !bytes.Equal(got[:], exp[:]) { + t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp) + } + newTxs, err := genTxs(uint64(len(txs) + 1)) + if err != nil { + t.Fatal(err) + } + txs = append(txs, newTxs...) + } +} + +func BenchmarkDeriveSha200(b *testing.B) { + txs, err := genTxs(200) + if err != nil { + b.Fatal(err) + } + var exp common.Hash + var got common.Hash + b.Run("std_trie", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + exp = types.DeriveSha(txs, newEmpty()) + } + }) + + b.Run("stack_trie", func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + got = types.DeriveSha(txs, NewStackTrie(nil)) + } + }) + if got != exp { + b.Errorf("got %x exp %x", got, exp) + } +} + +type dummyDerivableList struct { + len int + seed int +} + +func newDummy(seed int) *dummyDerivableList { + d := &dummyDerivableList{} + src := mrand.NewSource(int64(seed)) + // don't use lists longer than 4K items + d.len = int(src.Int63() & 0x0FFF) + d.seed = seed + return d +} + +func (d *dummyDerivableList) Len() int { + return d.len +} + +func (d *dummyDerivableList) GetRlp(i int) []byte { + src := mrand.NewSource(int64(d.seed + i)) + // max item size 256, at least 1 byte per item + size := 1 + src.Int63()&0x00FF + data := make([]byte, size) + _, err := mrand.New(src).Read(data) + if err != nil { + panic(err) + } + return data +} + +func printList(l types.DerivableList) { + fmt.Printf("list length: %d\n", l.Len()) + fmt.Printf("{\n") + for i := 0; i < l.Len(); i++ { + v := l.GetRlp(i) + fmt.Printf("\"0x%x\",\n", v) + } + fmt.Printf("},\n") +} + +func TestFuzzDeriveSha(t *testing.T) { + // increase this for longer runs -- it's set to quite low for travis + rndSeed := mrand.Int() + for i := 0; i < 10; i++ { + seed := rndSeed + i + exp := types.DeriveSha(newDummy(i), newEmpty()) + got := types.DeriveSha(newDummy(i), NewStackTrie(nil)) + if !bytes.Equal(got[:], exp[:]) { + printList(newDummy(seed)) + t.Fatalf("seed %d: got %x exp %x", seed, got, exp) + } + } +} + +type flatList struct { + rlpvals []string +} + +func newFlatList(rlpvals []string) *flatList { + return &flatList{rlpvals} +} +func (f *flatList) Len() int { + return len(f.rlpvals) +} +func (f *flatList) GetRlp(i int) []byte { + return hexutil.MustDecode(f.rlpvals[i]) +} + +// TestDerivableList contains testcases found via fuzzing +func TestDerivableList(t *testing.T) { + type tcase []string + tcs := []tcase{ + { + "0xc041", + }, + { + "0xf04cf757812428b0763112efb33b6f4fad7deb445e", + "0xf04cf757812428b0763112efb33b6f4fad7deb445e", + }, + { + "0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", + "0x6cd850eca0a7ac46bb1748d7b9cb88aa3bd21c57d852c28198ad8fa422c4595032e88a4494b4778b36b944fe47a52b8c5cd312910139dfcb4147ab8e972cc456bcb063f25dd78f54c4d34679e03142c42c662af52947d45bdb6e555751334ace76a5080ab5a0256a1d259855dfc5c0b8023b25befbb13fd3684f9f755cbd3d63544c78ee2001452dd54633a7593ade0b183891a0a4e9c7844e1254005fbe592b1b89149a502c24b6e1dca44c158aebedf01beae9c30cabe16a", + "0x14abd5c47c0be87b0454596baad2", + "0xca410605310cdc3bb8d4977ae4f0143df54a724ed873457e2272f39d66e0460e971d9d", + }, + } + for i, tc := range tcs[1:] { + exp := types.DeriveSha(newFlatList(tc), newEmpty()) + got := types.DeriveSha(newFlatList(tc), NewStackTrie(nil)) + if !bytes.Equal(got[:], exp[:]) { + t.Fatalf("case %d: got %x exp %x", i, got, exp) + } + } +} diff --git a/trie/trie.go b/trie/trie.go index 1e1749a4ff..6ddbbd78d3 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -505,13 +505,16 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) { if t.root == nil { return emptyRoot, nil } + // Derive the hash for all dirty nodes first. We hold the assumption + // in the following procedure that all nodes are hashed. rootHash := t.Hash() h := newCommitter() defer returnCommitterToPool(h) + // Do a quick check if we really need to commit, before we spin // up goroutines. This can happen e.g. if we load a trie for reading storage // values, but don't write to it. - if !h.commitNeeded(t.root) { + if _, dirty := t.root.cache(); !dirty { return rootHash, nil } var wg sync.WaitGroup diff --git a/trie/trie_test.go b/trie/trie_test.go index 2356b7a746..539451fbf4 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -19,7 +19,9 @@ package trie import ( "bytes" "encoding/binary" + "errors" "fmt" + "hash" "io/ioutil" "math/big" "math/rand" @@ -31,9 +33,11 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/leveldb" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" ) func init() { @@ -659,6 +663,196 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { return addresses, accounts } +// spongeDb is a dummy db backend which accumulates writes in a sponge +type spongeDb struct { + sponge hash.Hash + id string + journal []string +} + +func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } +func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") } +func (s *spongeDb) Delete(key []byte) error { panic("implement me") } +func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBatch{s} } +func (s *spongeDb) Stat(property string) (string, error) { panic("implement me") } +func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") } +func (s *spongeDb) Close() error { return nil } +func (s *spongeDb) Put(key []byte, value []byte) error { + valbrief := value + if len(valbrief) > 8 { + valbrief = valbrief[:8] + } + s.journal = append(s.journal, fmt.Sprintf("%v: PUT([%x...], [%d bytes] %x...)\n", s.id, key[:8], len(value), valbrief)) + s.sponge.Write(key) + s.sponge.Write(value) + return nil +} +func (s *spongeDb) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("implement me") } + +// spongeBatch is a dummy batch which immediately writes to the underlying spongedb +type spongeBatch struct { + db *spongeDb +} + +func (b *spongeBatch) Put(key, value []byte) error { + b.db.Put(key, value) + return nil +} +func (b *spongeBatch) Delete(key []byte) error { panic("implement me") } +func (b *spongeBatch) ValueSize() int { return 100 } +func (b *spongeBatch) Write() error { return nil } +func (b *spongeBatch) Reset() {} +func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil } + +// TestCommitSequence tests that the trie.Commit operation writes the elements of the trie +// in the expected order, and calls the callbacks in the expected order. +// The test data was based on the 'master' code, and is basically random. It can be used +// to check whether changes to the trie modifies the write order or data in any way. +func TestCommitSequence(t *testing.T) { + for i, tc := range []struct { + count int + expWriteSeqHash []byte + expCallbackSeqHash []byte + }{ + {20, common.FromHex("68c495e45209e243eb7e4f4e8ca8f9f7be71003bd9cafb8061b4534373740193"), + common.FromHex("01783213033d6b7781a641ab499e680d959336d025ac16f44d02f4f0c021bbf5")}, + {200, common.FromHex("3b20d16c13c4bc3eb3b8d0ad7a169fef3b1600e056c0665895d03d3d2b2ff236"), + common.FromHex("fb8db0ec82e8f02729f11228940885b181c3047ab0d654ed0110291ca57111a8")}, + {2000, common.FromHex("34eff3d1048bebdf77e9ae8bd939f2e7c742edc3dcd1173cff1aad9dbd20451a"), + common.FromHex("1c981604b1a9f8ffa40e0ae66b14830a87f5a4ed8345146a3912e6b2dcb05e63")}, + } { + addresses, accounts := makeAccounts(tc.count) + // This spongeDb is used to check the sequence of disk-db-writes + s := &spongeDb{sponge: sha3.NewLegacyKeccak256()} + db := NewDatabase(s) + trie, _ := New(common.Hash{}, db) + // Another sponge is used to check the callback-sequence + callbackSponge := sha3.NewLegacyKeccak256() + // Fill the trie with elements + for i := 0; i < tc.count; i++ { + trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) + } + // Flush trie -> database + root, _ := trie.Commit(nil) + // Flush memdb -> disk (sponge) + db.Commit(root, false, func(c common.Hash) { + // And spongify the callback-order + callbackSponge.Write(c[:]) + }) + if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { + t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) + } + if got, exp := callbackSponge.Sum(nil), tc.expCallbackSeqHash; !bytes.Equal(got, exp) { + t.Fatalf("test %d, call back sequence wrong:\ngot: %x exp %x\n", i, got, exp) + } + } +} + +// TestCommitSequenceRandomBlobs is identical to TestCommitSequence +// but uses random blobs instead of 'accounts' +func TestCommitSequenceRandomBlobs(t *testing.T) { + for i, tc := range []struct { + count int + expWriteSeqHash []byte + expCallbackSeqHash []byte + }{ + {20, common.FromHex("8e4a01548551d139fa9e833ebc4e66fc1ba40a4b9b7259d80db32cff7b64ebbc"), + common.FromHex("450238d73bc36dc6cc6f926987e5428535e64be403877c4560e238a52749ba24")}, + {200, common.FromHex("6869b4e7b95f3097a19ddb30ff735f922b915314047e041614df06958fc50554"), + common.FromHex("0ace0b03d6cb8c0b82f6289ef5b1a1838306b455a62dafc63cada8e2924f2550")}, + {2000, common.FromHex("444200e6f4e2df49f77752f629a96ccf7445d4698c164f962bbd85a0526ef424"), + common.FromHex("117d30dafaa62a1eed498c3dfd70982b377ba2b46dd3e725ed6120c80829e518")}, + } { + prng := rand.New(rand.NewSource(int64(i))) + // This spongeDb is used to check the sequence of disk-db-writes + s := &spongeDb{sponge: sha3.NewLegacyKeccak256()} + db := NewDatabase(s) + trie, _ := New(common.Hash{}, db) + // Another sponge is used to check the callback-sequence + callbackSponge := sha3.NewLegacyKeccak256() + // Fill the trie with elements + for i := 0; i < tc.count; i++ { + key := make([]byte, 32) + var val []byte + // 50% short elements, 50% large elements + if prng.Intn(2) == 0 { + val = make([]byte, 1+prng.Intn(32)) + } else { + val = make([]byte, 1+prng.Intn(4096)) + } + prng.Read(key) + prng.Read(val) + trie.Update(key, val) + } + // Flush trie -> database + root, _ := trie.Commit(nil) + // Flush memdb -> disk (sponge) + db.Commit(root, false, func(c common.Hash) { + // And spongify the callback-order + callbackSponge.Write(c[:]) + }) + if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { + t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) + } + if got, exp := callbackSponge.Sum(nil), tc.expCallbackSeqHash; !bytes.Equal(got, exp) { + t.Fatalf("test %d, call back sequence wrong:\ngot: %x exp %x\n", i, got, exp) + } + } +} + +func TestCommitSequenceStackTrie(t *testing.T) { + for count := 1; count < 200; count++ { + prng := rand.New(rand.NewSource(int64(count))) + // This spongeDb is used to check the sequence of disk-db-writes + s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"} + db := NewDatabase(s) + trie, _ := New(common.Hash{}, db) + // Another sponge is used for the stacktrie commits + stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"} + stTrie := NewStackTrie(stackTrieSponge) + // Fill the trie with elements + for i := 1; i < count; i++ { + // For the stack trie, we need to do inserts in proper order + key := make([]byte, 32) + binary.BigEndian.PutUint64(key, uint64(i)) + var val []byte + // 50% short elements, 50% large elements + if prng.Intn(2) == 0 { + val = make([]byte, 1+prng.Intn(32)) + } else { + val = make([]byte, 1+prng.Intn(1024)) + } + prng.Read(val) + trie.TryUpdate(key, common.CopyBytes(val)) + stTrie.TryUpdate(key, common.CopyBytes(val)) + } + // Flush trie -> database + root, _ := trie.Commit(nil) + // Flush memdb -> disk (sponge) + db.Commit(root, false, nil) + // And flush stacktrie -> disk + stRoot, err := stTrie.Commit() + if err != nil { + t.Fatalf("Failed to commit stack trie %v", err) + } + if stRoot != root { + t.Fatalf("root wrong, got %x exp %x", stRoot, root) + } + if got, exp := stackTrieSponge.sponge.Sum(nil), s.sponge.Sum(nil); !bytes.Equal(got, exp) { + // Show the journal + t.Logf("Expected:") + for i, v := range s.journal { + t.Logf("op %d: %v", i, v) + } + t.Logf("Stacktrie:") + for i, v := range stackTrieSponge.journal { + t.Logf("op %d: %v", i, v) + } + t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", count, got, exp) + } + } +} + // BenchmarkCommitAfterHashFixedSize benchmarks the Commit (after Hash) of a fixed number of updates to a trie. // This benchmark is meant to capture the difference on efficiency of small versus large changes. Typically, // storage tries are small (a couple of entries), whereas the full post-block account trie update is large (a couple