mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
Merge branch 'master' of https://github.com/ethereum/go-ethereum
This commit is contained in:
commit
3ccc825364
11 changed files with 479 additions and 197 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1.8.8
|
||||
1.8.9
|
||||
|
|
|
|||
|
|
@ -111,9 +111,14 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
|
|||
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the output interface is a struct, make sure names don't collide
|
||||
|
||||
// If the interface is a struct, get of abi->struct_field mapping
|
||||
|
||||
var abi2struct map[string]string
|
||||
if kind == reflect.Struct {
|
||||
if err := requireUniqueStructFieldNames(arguments); err != nil {
|
||||
var err error
|
||||
abi2struct, err = mapAbiToStructFields(arguments, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -123,9 +128,10 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
|
|||
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
err := unpackStruct(value, reflectValue, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
if structField, ok := abi2struct[arg.Name]; ok {
|
||||
if err := set(value.FieldByName(structField), reflectValue, arg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
if value.Len() < i {
|
||||
|
|
@ -151,17 +157,22 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interf
|
|||
if len(marshalledValues) != 1 {
|
||||
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
|
||||
}
|
||||
|
||||
elem := reflect.ValueOf(v).Elem()
|
||||
kind := elem.Kind()
|
||||
reflectValue := reflect.ValueOf(marshalledValues[0])
|
||||
|
||||
var abi2struct map[string]string
|
||||
if kind == reflect.Struct {
|
||||
//make sure names don't collide
|
||||
if err := requireUniqueStructFieldNames(arguments); err != nil {
|
||||
var err error
|
||||
if abi2struct, err = mapAbiToStructFields(arguments, elem); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return unpackStruct(elem, reflectValue, arguments[0])
|
||||
arg := arguments.NonIndexed()[0]
|
||||
if structField, ok := abi2struct[arg.Name]; ok {
|
||||
return set(elem.FieldByName(structField), reflectValue, arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return set(elem, reflectValue, arguments.NonIndexed()[0])
|
||||
|
|
@ -277,18 +288,3 @@ func capitalise(input string) string {
|
|||
}
|
||||
return strings.ToUpper(input[:1]) + input[1:]
|
||||
}
|
||||
|
||||
//unpackStruct extracts each argument into its corresponding struct field
|
||||
func unpackStruct(value, reflectValue reflect.Value, arg Argument) error {
|
||||
name := capitalise(arg.Name)
|
||||
typ := value.Type()
|
||||
for j := 0; j < typ.NumField(); j++ {
|
||||
// TODO read tags: `abi:"fieldName"`
|
||||
if typ.Field(j).Name == name {
|
||||
if err := set(value.Field(j), reflectValue, arg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,12 +58,28 @@ var jsonEventPledge = []byte(`{
|
|||
"type": "event"
|
||||
}`)
|
||||
|
||||
var jsonEventMixedCase = []byte(`{
|
||||
"anonymous": false,
|
||||
"inputs": [{
|
||||
"indexed": false, "name": "value", "type": "uint256"
|
||||
}, {
|
||||
"indexed": false, "name": "_value", "type": "uint256"
|
||||
}, {
|
||||
"indexed": false, "name": "Value", "type": "uint256"
|
||||
}],
|
||||
"name": "MixedCase",
|
||||
"type": "event"
|
||||
}`)
|
||||
|
||||
// 1000000
|
||||
var transferData1 = "00000000000000000000000000000000000000000000000000000000000f4240"
|
||||
|
||||
// "0x00Ce0d46d924CC8437c806721496599FC3FFA268", 2218516807680, "usd"
|
||||
var pledgeData1 = "00000000000000000000000000ce0d46d924cc8437c806721496599fc3ffa2680000000000000000000000000000000000000000000000000000020489e800007573640000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
// 1000000,2218516807680,1000001
|
||||
var mixedCaseData1 = "00000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000020489e8000000000000000000000000000000000000000000000000000000000000000f4241"
|
||||
|
||||
func TestEventId(t *testing.T) {
|
||||
var table = []struct {
|
||||
definition string
|
||||
|
|
@ -121,6 +137,27 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||
Value *big.Int
|
||||
}
|
||||
|
||||
type EventTransferWithTag struct {
|
||||
// this is valid because `value` is not exportable,
|
||||
// so value is only unmarshalled into `Value1`.
|
||||
value *big.Int
|
||||
Value1 *big.Int `abi:"value"`
|
||||
}
|
||||
|
||||
type BadEventTransferWithSameFieldAndTag struct {
|
||||
Value *big.Int
|
||||
Value1 *big.Int `abi:"value"`
|
||||
}
|
||||
|
||||
type BadEventTransferWithDuplicatedTag struct {
|
||||
Value1 *big.Int `abi:"value"`
|
||||
Value2 *big.Int `abi:"value"`
|
||||
}
|
||||
|
||||
type BadEventTransferWithEmptyTag struct {
|
||||
Value *big.Int `abi:""`
|
||||
}
|
||||
|
||||
type EventPledge struct {
|
||||
Who common.Address
|
||||
Wad *big.Int
|
||||
|
|
@ -133,9 +170,16 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||
Currency [3]byte
|
||||
}
|
||||
|
||||
type EventMixedCase struct {
|
||||
Value1 *big.Int `abi:"value"`
|
||||
Value2 *big.Int `abi:"_value"`
|
||||
Value3 *big.Int `abi:"Value"`
|
||||
}
|
||||
|
||||
bigint := new(big.Int)
|
||||
bigintExpected := big.NewInt(1000000)
|
||||
bigintExpected2 := big.NewInt(2218516807680)
|
||||
bigintExpected3 := big.NewInt(1000001)
|
||||
addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
|
||||
var testCases = []struct {
|
||||
data string
|
||||
|
|
@ -158,6 +202,34 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||
jsonEventTransfer,
|
||||
"",
|
||||
"Can unpack ERC20 Transfer event into slice",
|
||||
}, {
|
||||
transferData1,
|
||||
&EventTransferWithTag{},
|
||||
&EventTransferWithTag{Value1: bigintExpected},
|
||||
jsonEventTransfer,
|
||||
"",
|
||||
"Can unpack ERC20 Transfer event into structure with abi: tag",
|
||||
}, {
|
||||
transferData1,
|
||||
&BadEventTransferWithDuplicatedTag{},
|
||||
&BadEventTransferWithDuplicatedTag{},
|
||||
jsonEventTransfer,
|
||||
"struct: abi tag in 'Value2' already mapped",
|
||||
"Can not unpack ERC20 Transfer event with duplicated abi tag",
|
||||
}, {
|
||||
transferData1,
|
||||
&BadEventTransferWithSameFieldAndTag{},
|
||||
&BadEventTransferWithSameFieldAndTag{},
|
||||
jsonEventTransfer,
|
||||
"abi: multiple variables maps to the same abi field 'value'",
|
||||
"Can not unpack ERC20 Transfer event with a field and a tag mapping to the same abi variable",
|
||||
}, {
|
||||
transferData1,
|
||||
&BadEventTransferWithEmptyTag{},
|
||||
&BadEventTransferWithEmptyTag{},
|
||||
jsonEventTransfer,
|
||||
"struct: abi tag in 'Value' is empty",
|
||||
"Can not unpack ERC20 Transfer event with an empty tag",
|
||||
}, {
|
||||
pledgeData1,
|
||||
&EventPledge{},
|
||||
|
|
@ -216,6 +288,13 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||
jsonEventPledge,
|
||||
"abi: cannot unmarshal tuple into map[string]interface {}",
|
||||
"Can not unpack Pledge event into map",
|
||||
}, {
|
||||
mixedCaseData1,
|
||||
&EventMixedCase{},
|
||||
&EventMixedCase{Value1: bigintExpected, Value2: bigintExpected2, Value3: bigintExpected3},
|
||||
jsonEventMixedCase,
|
||||
"",
|
||||
"Can unpack abi variables with mixed case",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
@ -227,7 +306,7 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||
assert.Nil(err, "Should be able to unpack event data.")
|
||||
assert.Equal(tc.expected, tc.dest, tc.name)
|
||||
} else {
|
||||
assert.EqualError(err, tc.error)
|
||||
assert.EqualError(err, tc.error, tc.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package abi
|
|||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// indirect recursively dereferences the value until it either gets the value
|
||||
|
|
@ -111,18 +112,101 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
|
|||
return nil
|
||||
}
|
||||
|
||||
// requireUniqueStructFieldNames makes sure field names don't collide
|
||||
func requireUniqueStructFieldNames(args Arguments) error {
|
||||
exists := make(map[string]bool)
|
||||
for _, arg := range args {
|
||||
field := capitalise(arg.Name)
|
||||
if field == "" {
|
||||
return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
|
||||
// mapAbiToStringField maps abi to struct fields.
|
||||
// first round: for each Exportable field that contains a `abi:""` tag
|
||||
// and this field name exists in the arguments, pair them together.
|
||||
// second round: for each argument field that has not been already linked,
|
||||
// find what variable is expected to be mapped into, if it exists and has not been
|
||||
// used, pair them.
|
||||
func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]string, error) {
|
||||
|
||||
typ := value.Type()
|
||||
|
||||
abi2struct := make(map[string]string)
|
||||
struct2abi := make(map[string]string)
|
||||
|
||||
// first round ~~~
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
structFieldName := typ.Field(i).Name
|
||||
|
||||
// skip private struct fields.
|
||||
if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
|
||||
continue
|
||||
}
|
||||
if exists[field] {
|
||||
return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
|
||||
|
||||
// skip fields that have no abi:"" tag.
|
||||
var ok bool
|
||||
var tagName string
|
||||
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
|
||||
continue
|
||||
}
|
||||
exists[field] = true
|
||||
|
||||
// check if tag is empty.
|
||||
if tagName == "" {
|
||||
return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
|
||||
}
|
||||
|
||||
// check which argument field matches with the abi tag.
|
||||
found := false
|
||||
for _, abiField := range args.NonIndexed() {
|
||||
if abiField.Name == tagName {
|
||||
if abi2struct[abiField.Name] != "" {
|
||||
return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
|
||||
}
|
||||
// pair them
|
||||
abi2struct[abiField.Name] = structFieldName
|
||||
struct2abi[structFieldName] = abiField.Name
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
// check if this tag has been mapped.
|
||||
if !found {
|
||||
return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
|
||||
// second round ~~~
|
||||
for _, arg := range args {
|
||||
|
||||
abiFieldName := arg.Name
|
||||
structFieldName := capitalise(abiFieldName)
|
||||
|
||||
if structFieldName == "" {
|
||||
return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
|
||||
}
|
||||
|
||||
// this abi has already been paired, skip it... unless there exists another, yet unassigned
|
||||
// struct field with the same field name. If so, raise an error:
|
||||
// abi: [ { "name": "value" } ]
|
||||
// struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
|
||||
if abi2struct[abiFieldName] != "" {
|
||||
if abi2struct[abiFieldName] != structFieldName &&
|
||||
struct2abi[structFieldName] == "" &&
|
||||
value.FieldByName(structFieldName).IsValid() {
|
||||
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", abiFieldName)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// return an error if this struct field has already been paired.
|
||||
if struct2abi[structFieldName] != "" {
|
||||
return nil, fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", structFieldName)
|
||||
}
|
||||
|
||||
if value.FieldByName(structFieldName).IsValid() {
|
||||
// pair them
|
||||
abi2struct[abiFieldName] = structFieldName
|
||||
struct2abi[structFieldName] = abiFieldName
|
||||
} else {
|
||||
// not paired, but annotate as used, to detect cases like
|
||||
// abi : [ { "name": "value" }, { "name": "_value" } ]
|
||||
// struct { Value *big.Int }
|
||||
struct2abi[structFieldName] = abiFieldName
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return abi2struct, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ environment:
|
|||
install:
|
||||
- git submodule update --init
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.1.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.10.1.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.2.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.10.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- gcc --version
|
||||
|
||||
|
|
|
|||
159
p2p/enr/enr.go
159
p2p/enr/enr.go
|
|
@ -29,21 +29,16 @@ package enr
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
const SizeLimit = 300 // maximum encoded size of a node record in bytes
|
||||
|
||||
const ID_SECP256k1_KECCAK = ID("secp256k1-keccak") // the default identity scheme
|
||||
|
||||
var (
|
||||
errNoID = errors.New("unknown or unspecified identity scheme")
|
||||
errInvalidSig = errors.New("invalid signature")
|
||||
|
|
@ -80,8 +75,8 @@ func (r *Record) Seq() uint64 {
|
|||
}
|
||||
|
||||
// SetSeq updates the record sequence number. This invalidates any signature on the record.
|
||||
// Calling SetSeq is usually not required because signing the redord increments the
|
||||
// sequence number.
|
||||
// Calling SetSeq is usually not required because setting any key in a signed record
|
||||
// increments the sequence number.
|
||||
func (r *Record) SetSeq(s uint64) {
|
||||
r.signature = nil
|
||||
r.raw = nil
|
||||
|
|
@ -104,33 +99,42 @@ func (r *Record) Load(e Entry) error {
|
|||
return &KeyError{Key: e.ENRKey(), Err: errNotFound}
|
||||
}
|
||||
|
||||
// Set adds or updates the given entry in the record.
|
||||
// It panics if the value can't be encoded.
|
||||
// Set adds or updates the given entry in the record. It panics if the value can't be
|
||||
// encoded. If the record is signed, Set increments the sequence number and invalidates
|
||||
// the sequence number.
|
||||
func (r *Record) Set(e Entry) {
|
||||
r.signature = nil
|
||||
r.raw = nil
|
||||
blob, err := rlp.EncodeToBytes(e)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("enr: can't encode %s: %v", e.ENRKey(), err))
|
||||
}
|
||||
r.invalidate()
|
||||
|
||||
i := sort.Search(len(r.pairs), func(i int) bool { return r.pairs[i].k >= e.ENRKey() })
|
||||
|
||||
if i < len(r.pairs) && r.pairs[i].k == e.ENRKey() {
|
||||
pairs := make([]pair, len(r.pairs))
|
||||
copy(pairs, r.pairs)
|
||||
i := sort.Search(len(pairs), func(i int) bool { return pairs[i].k >= e.ENRKey() })
|
||||
switch {
|
||||
case i < len(pairs) && pairs[i].k == e.ENRKey():
|
||||
// element is present at r.pairs[i]
|
||||
r.pairs[i].v = blob
|
||||
return
|
||||
} else if i < len(r.pairs) {
|
||||
pairs[i].v = blob
|
||||
case i < len(r.pairs):
|
||||
// insert pair before i-th elem
|
||||
el := pair{e.ENRKey(), blob}
|
||||
r.pairs = append(r.pairs, pair{})
|
||||
copy(r.pairs[i+1:], r.pairs[i:])
|
||||
r.pairs[i] = el
|
||||
return
|
||||
pairs = append(pairs, pair{})
|
||||
copy(pairs[i+1:], pairs[i:])
|
||||
pairs[i] = el
|
||||
default:
|
||||
// element should be placed at the end of r.pairs
|
||||
pairs = append(pairs, pair{e.ENRKey(), blob})
|
||||
}
|
||||
r.pairs = pairs
|
||||
}
|
||||
|
||||
// element should be placed at the end of r.pairs
|
||||
r.pairs = append(r.pairs, pair{e.ENRKey(), blob})
|
||||
func (r *Record) invalidate() {
|
||||
if r.signature == nil {
|
||||
r.seq++
|
||||
}
|
||||
r.signature = nil
|
||||
r.raw = nil
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder. Encoding fails if
|
||||
|
|
@ -196,39 +200,55 @@ func (r *Record) DecodeRLP(s *rlp.Stream) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Verify signature.
|
||||
if err = dec.verifySignature(); err != nil {
|
||||
_, scheme := dec.idScheme()
|
||||
if scheme == nil {
|
||||
return errNoID
|
||||
}
|
||||
if err := scheme.Verify(&dec, dec.signature); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = dec
|
||||
return nil
|
||||
}
|
||||
|
||||
type s256raw []byte
|
||||
|
||||
func (s256raw) ENRKey() string { return "secp256k1" }
|
||||
|
||||
// NodeAddr returns the node address. The return value will be nil if the record is
|
||||
// unsigned.
|
||||
// unsigned or uses an unknown identity scheme.
|
||||
func (r *Record) NodeAddr() []byte {
|
||||
var entry s256raw
|
||||
if r.Load(&entry) != nil {
|
||||
_, scheme := r.idScheme()
|
||||
if scheme == nil {
|
||||
return nil
|
||||
}
|
||||
return crypto.Keccak256(entry)
|
||||
return scheme.NodeAddr(r)
|
||||
}
|
||||
|
||||
// Sign signs the record with the given private key. It updates the record's identity
|
||||
// scheme, public key and increments the sequence number. Sign returns an error if the
|
||||
// encoded record is larger than the size limit.
|
||||
func (r *Record) Sign(privkey *ecdsa.PrivateKey) error {
|
||||
r.seq = r.seq + 1
|
||||
r.Set(ID_SECP256k1_KECCAK)
|
||||
r.Set(Secp256k1(privkey.PublicKey))
|
||||
return r.signAndEncode(privkey)
|
||||
// SetSig sets the record signature. It returns an error if the encoded record is larger
|
||||
// than the size limit or if the signature is invalid according to the passed scheme.
|
||||
func (r *Record) SetSig(idscheme string, sig []byte) error {
|
||||
// Check that "id" is set and matches the given scheme. This panics because
|
||||
// inconsitencies here are always implementation bugs in the signing function calling
|
||||
// this method.
|
||||
id, s := r.idScheme()
|
||||
if s == nil {
|
||||
panic(errNoID)
|
||||
}
|
||||
if id != idscheme {
|
||||
panic(fmt.Errorf("identity scheme mismatch in Sign: record has %s, want %s", id, idscheme))
|
||||
}
|
||||
|
||||
// Verify against the scheme.
|
||||
if err := s.Verify(r, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := r.encode(sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.signature, r.raw = sig, raw
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Record) appendPairs(list []interface{}) []interface{} {
|
||||
// AppendElements appends the sequence number and entries to the given slice.
|
||||
func (r *Record) AppendElements(list []interface{}) []interface{} {
|
||||
list = append(list, r.seq)
|
||||
for _, p := range r.pairs {
|
||||
list = append(list, p.k, p.v)
|
||||
|
|
@ -236,54 +256,23 @@ func (r *Record) appendPairs(list []interface{}) []interface{} {
|
|||
return list
|
||||
}
|
||||
|
||||
func (r *Record) signAndEncode(privkey *ecdsa.PrivateKey) error {
|
||||
// Put record elements into a flat list. Leave room for the signature.
|
||||
list := make([]interface{}, 1, len(r.pairs)*2+2)
|
||||
list = r.appendPairs(list)
|
||||
|
||||
// Sign the tail of the list.
|
||||
h := sha3.NewKeccak256()
|
||||
rlp.Encode(h, list[1:])
|
||||
sig, err := crypto.Sign(h.Sum(nil), privkey)
|
||||
if err != nil {
|
||||
return err
|
||||
func (r *Record) encode(sig []byte) (raw []byte, err error) {
|
||||
list := make([]interface{}, 1, 2*len(r.pairs)+1)
|
||||
list[0] = sig
|
||||
list = r.AppendElements(list)
|
||||
if raw, err = rlp.EncodeToBytes(list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig = sig[:len(sig)-1] // remove v
|
||||
|
||||
// Put signature in front.
|
||||
r.signature, list[0] = sig, sig
|
||||
r.raw, err = rlp.EncodeToBytes(list)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(raw) > SizeLimit {
|
||||
return nil, errTooBig
|
||||
}
|
||||
if len(r.raw) > SizeLimit {
|
||||
return errTooBig
|
||||
}
|
||||
return nil
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (r *Record) verifySignature() error {
|
||||
// Get identity scheme, public key, signature.
|
||||
func (r *Record) idScheme() (string, IdentityScheme) {
|
||||
var id ID
|
||||
var entry s256raw
|
||||
if err := r.Load(&id); err != nil {
|
||||
return err
|
||||
} else if id != ID_SECP256k1_KECCAK {
|
||||
return errNoID
|
||||
return "", nil
|
||||
}
|
||||
if err := r.Load(&entry); err != nil {
|
||||
return err
|
||||
} else if len(entry) != 33 {
|
||||
return fmt.Errorf("invalid public key")
|
||||
}
|
||||
|
||||
// Verify the signature.
|
||||
list := make([]interface{}, 0, len(r.pairs)*2+1)
|
||||
list = r.appendPairs(list)
|
||||
h := sha3.NewKeccak256()
|
||||
rlp.Encode(h, list)
|
||||
if !crypto.VerifySignature(entry, h.Sum(nil), r.signature) {
|
||||
return errInvalidSig
|
||||
}
|
||||
return nil
|
||||
return string(id), FindIdentityScheme(string(id))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,35 +54,35 @@ func TestGetSetID(t *testing.T) {
|
|||
assert.Equal(t, id, id2)
|
||||
}
|
||||
|
||||
// TestGetSetIP4 tests encoding/decoding and setting/getting of the IP4 key.
|
||||
// TestGetSetIP4 tests encoding/decoding and setting/getting of the IP key.
|
||||
func TestGetSetIP4(t *testing.T) {
|
||||
ip := IP4{192, 168, 0, 3}
|
||||
ip := IP{192, 168, 0, 3}
|
||||
var r Record
|
||||
r.Set(ip)
|
||||
|
||||
var ip2 IP4
|
||||
var ip2 IP
|
||||
require.NoError(t, r.Load(&ip2))
|
||||
assert.Equal(t, ip, ip2)
|
||||
}
|
||||
|
||||
// TestGetSetIP6 tests encoding/decoding and setting/getting of the IP6 key.
|
||||
// TestGetSetIP6 tests encoding/decoding and setting/getting of the IP key.
|
||||
func TestGetSetIP6(t *testing.T) {
|
||||
ip := IP6{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}
|
||||
ip := IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}
|
||||
var r Record
|
||||
r.Set(ip)
|
||||
|
||||
var ip2 IP6
|
||||
var ip2 IP
|
||||
require.NoError(t, r.Load(&ip2))
|
||||
assert.Equal(t, ip, ip2)
|
||||
}
|
||||
|
||||
// TestGetSetDiscPort tests encoding/decoding and setting/getting of the DiscPort key.
|
||||
func TestGetSetDiscPort(t *testing.T) {
|
||||
port := DiscPort(30309)
|
||||
func TestGetSetUDP(t *testing.T) {
|
||||
port := UDP(30309)
|
||||
var r Record
|
||||
r.Set(port)
|
||||
|
||||
var port2 DiscPort
|
||||
var port2 UDP
|
||||
require.NoError(t, r.Load(&port2))
|
||||
assert.Equal(t, port, port2)
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ func TestGetSetDiscPort(t *testing.T) {
|
|||
// TestGetSetSecp256k1 tests encoding/decoding and setting/getting of the Secp256k1 key.
|
||||
func TestGetSetSecp256k1(t *testing.T) {
|
||||
var r Record
|
||||
if err := r.Sign(privkey); err != nil {
|
||||
if err := SignV4(&r, privkey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
@ -101,16 +101,16 @@ func TestGetSetSecp256k1(t *testing.T) {
|
|||
|
||||
func TestLoadErrors(t *testing.T) {
|
||||
var r Record
|
||||
ip4 := IP4{127, 0, 0, 1}
|
||||
ip4 := IP{127, 0, 0, 1}
|
||||
r.Set(ip4)
|
||||
|
||||
// Check error for missing keys.
|
||||
var ip6 IP6
|
||||
err := r.Load(&ip6)
|
||||
var udp UDP
|
||||
err := r.Load(&udp)
|
||||
if !IsNotFound(err) {
|
||||
t.Error("IsNotFound should return true for missing key")
|
||||
}
|
||||
assert.Equal(t, &KeyError{Key: ip6.ENRKey(), Err: errNotFound}, err)
|
||||
assert.Equal(t, &KeyError{Key: udp.ENRKey(), Err: errNotFound}, err)
|
||||
|
||||
// Check error for invalid keys.
|
||||
var list []uint
|
||||
|
|
@ -174,7 +174,7 @@ func TestDirty(t *testing.T) {
|
|||
t.Errorf("expected errEncodeUnsigned, got %#v", err)
|
||||
}
|
||||
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
if !r.Signed() {
|
||||
t.Error("Signed return false for signed record")
|
||||
}
|
||||
|
|
@ -194,13 +194,13 @@ func TestDirty(t *testing.T) {
|
|||
func TestGetSetOverwrite(t *testing.T) {
|
||||
var r Record
|
||||
|
||||
ip := IP4{192, 168, 0, 3}
|
||||
ip := IP{192, 168, 0, 3}
|
||||
r.Set(ip)
|
||||
|
||||
ip2 := IP4{192, 168, 0, 4}
|
||||
ip2 := IP{192, 168, 0, 4}
|
||||
r.Set(ip2)
|
||||
|
||||
var ip3 IP4
|
||||
var ip3 IP
|
||||
require.NoError(t, r.Load(&ip3))
|
||||
assert.Equal(t, ip2, ip3)
|
||||
}
|
||||
|
|
@ -208,9 +208,9 @@ func TestGetSetOverwrite(t *testing.T) {
|
|||
// TestSignEncodeAndDecode tests signing, RLP encoding and RLP decoding of a record.
|
||||
func TestSignEncodeAndDecode(t *testing.T) {
|
||||
var r Record
|
||||
r.Set(DiscPort(30303))
|
||||
r.Set(IP4{127, 0, 0, 1})
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
r.Set(UDP(30303))
|
||||
r.Set(IP{127, 0, 0, 1})
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
|
||||
blob, err := rlp.EncodeToBytes(r)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -230,12 +230,12 @@ func TestNodeAddr(t *testing.T) {
|
|||
t.Errorf("wrong address on empty record: got %v, want %v", addr, nil)
|
||||
}
|
||||
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
expected := "caaa1485d83b18b32ed9ad666026151bf0cae8a0a88c857ae2d4c5be2daa6726"
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
expected := "a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7"
|
||||
assert.Equal(t, expected, hex.EncodeToString(r.NodeAddr()))
|
||||
}
|
||||
|
||||
var pyRecord, _ = hex.DecodeString("f896b840954dc36583c1f4b69ab59b1375f362f06ee99f3723cd77e64b6de6d211c27d7870642a79d4516997f94091325d2a7ca6215376971455fb221d34f35b277149a1018664697363763582765f82696490736563703235366b312d6b656363616b83697034847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138")
|
||||
var pyRecord, _ = hex.DecodeString("f884b8407098ad865b00a582051940cb9cf36836572411a47278783077011599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11df72ecf1145ccb9c01826964827634826970847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31388375647082765f")
|
||||
|
||||
// TestPythonInterop checks that we can decode and verify a record produced by the Python
|
||||
// implementation.
|
||||
|
|
@ -246,10 +246,10 @@ func TestPythonInterop(t *testing.T) {
|
|||
}
|
||||
|
||||
var (
|
||||
wantAddr, _ = hex.DecodeString("caaa1485d83b18b32ed9ad666026151bf0cae8a0a88c857ae2d4c5be2daa6726")
|
||||
wantSeq = uint64(1)
|
||||
wantIP = IP4{127, 0, 0, 1}
|
||||
wantDiscport = DiscPort(30303)
|
||||
wantAddr, _ = hex.DecodeString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7")
|
||||
wantSeq = uint64(1)
|
||||
wantIP = IP{127, 0, 0, 1}
|
||||
wantUDP = UDP(30303)
|
||||
)
|
||||
if r.Seq() != wantSeq {
|
||||
t.Errorf("wrong seq: got %d, want %d", r.Seq(), wantSeq)
|
||||
|
|
@ -257,7 +257,7 @@ func TestPythonInterop(t *testing.T) {
|
|||
if addr := r.NodeAddr(); !bytes.Equal(addr, wantAddr) {
|
||||
t.Errorf("wrong addr: got %x, want %x", addr, wantAddr)
|
||||
}
|
||||
want := map[Entry]interface{}{new(IP4): &wantIP, new(DiscPort): &wantDiscport}
|
||||
want := map[Entry]interface{}{new(IP): &wantIP, new(UDP): &wantUDP}
|
||||
for k, v := range want {
|
||||
desc := fmt.Sprintf("loading key %q", k.ENRKey())
|
||||
if assert.NoError(t, r.Load(k), desc) {
|
||||
|
|
@ -272,14 +272,14 @@ func TestRecordTooBig(t *testing.T) {
|
|||
key := randomString(10)
|
||||
|
||||
// set a big value for random key, expect error
|
||||
r.Set(WithEntry(key, randomString(300)))
|
||||
if err := r.Sign(privkey); err != errTooBig {
|
||||
r.Set(WithEntry(key, randomString(SizeLimit)))
|
||||
if err := SignV4(&r, privkey); err != errTooBig {
|
||||
t.Fatalf("expected to get errTooBig, got %#v", err)
|
||||
}
|
||||
|
||||
// set an acceptable value for random key, expect no error
|
||||
r.Set(WithEntry(key, randomString(100)))
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
}
|
||||
|
||||
// TestSignEncodeAndDecodeRandom tests encoding/decoding of records containing random key/value pairs.
|
||||
|
|
@ -295,7 +295,7 @@ func TestSignEncodeAndDecodeRandom(t *testing.T) {
|
|||
r.Set(WithEntry(key, &value))
|
||||
}
|
||||
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
_, err := rlp.EncodeToBytes(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
|||
|
|
@ -57,59 +57,43 @@ func WithEntry(k string, v interface{}) Entry {
|
|||
return &generic{key: k, value: v}
|
||||
}
|
||||
|
||||
// DiscPort is the "discv5" key, which holds the UDP port for discovery v5.
|
||||
type DiscPort uint16
|
||||
// TCP is the "tcp" key, which holds the TCP port of the node.
|
||||
type TCP uint16
|
||||
|
||||
func (v DiscPort) ENRKey() string { return "discv5" }
|
||||
func (v TCP) ENRKey() string { return "tcp" }
|
||||
|
||||
// UDP is the "udp" key, which holds the UDP port of the node.
|
||||
type UDP uint16
|
||||
|
||||
func (v UDP) ENRKey() string { return "udp" }
|
||||
|
||||
// ID is the "id" key, which holds the name of the identity scheme.
|
||||
type ID string
|
||||
|
||||
const IDv4 = ID("v4") // the default identity scheme
|
||||
|
||||
func (v ID) ENRKey() string { return "id" }
|
||||
|
||||
// IP4 is the "ip4" key, which holds a 4-byte IPv4 address.
|
||||
type IP4 net.IP
|
||||
// IP is the "ip" key, which holds the IP address of the node.
|
||||
type IP net.IP
|
||||
|
||||
func (v IP4) ENRKey() string { return "ip4" }
|
||||
func (v IP) ENRKey() string { return "ip" }
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (v IP4) EncodeRLP(w io.Writer) error {
|
||||
ip4 := net.IP(v).To4()
|
||||
if ip4 == nil {
|
||||
return fmt.Errorf("invalid IPv4 address: %v", v)
|
||||
func (v IP) EncodeRLP(w io.Writer) error {
|
||||
if ip4 := net.IP(v).To4(); ip4 != nil {
|
||||
return rlp.Encode(w, ip4)
|
||||
}
|
||||
return rlp.Encode(w, ip4)
|
||||
return rlp.Encode(w, net.IP(v))
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (v *IP4) DecodeRLP(s *rlp.Stream) error {
|
||||
func (v *IP) DecodeRLP(s *rlp.Stream) error {
|
||||
if err := s.Decode((*net.IP)(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(*v) != 4 {
|
||||
return fmt.Errorf("invalid IPv4 address, want 4 bytes: %v", *v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IP6 is the "ip6" key, which holds a 16-byte IPv6 address.
|
||||
type IP6 net.IP
|
||||
|
||||
func (v IP6) ENRKey() string { return "ip6" }
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (v IP6) EncodeRLP(w io.Writer) error {
|
||||
ip6 := net.IP(v)
|
||||
return rlp.Encode(w, ip6)
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (v *IP6) DecodeRLP(s *rlp.Stream) error {
|
||||
if err := s.Decode((*net.IP)(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(*v) != 16 {
|
||||
return fmt.Errorf("invalid IPv6 address, want 16 bytes: %v", *v)
|
||||
if len(*v) != 4 && len(*v) != 16 {
|
||||
return fmt.Errorf("invalid IP address, want 4 or 16 bytes: %v", *v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
114
p2p/enr/idscheme.go
Normal file
114
p2p/enr/idscheme.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enr
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Registry of known identity schemes.
|
||||
var schemes sync.Map
|
||||
|
||||
// An IdentityScheme is capable of verifying record signatures and
|
||||
// deriving node addresses.
|
||||
type IdentityScheme interface {
|
||||
Verify(r *Record, sig []byte) error
|
||||
NodeAddr(r *Record) []byte
|
||||
}
|
||||
|
||||
// RegisterIdentityScheme adds an identity scheme to the global registry.
|
||||
func RegisterIdentityScheme(name string, scheme IdentityScheme) {
|
||||
if _, loaded := schemes.LoadOrStore(name, scheme); loaded {
|
||||
panic("identity scheme " + name + " already registered")
|
||||
}
|
||||
}
|
||||
|
||||
// FindIdentityScheme resolves name to an identity scheme in the global registry.
|
||||
func FindIdentityScheme(name string) IdentityScheme {
|
||||
s, ok := schemes.Load(name)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return s.(IdentityScheme)
|
||||
}
|
||||
|
||||
// v4ID is the "v4" identity scheme.
|
||||
type v4ID struct{}
|
||||
|
||||
func init() {
|
||||
RegisterIdentityScheme("v4", v4ID{})
|
||||
}
|
||||
|
||||
// SignV4 signs a record using the v4 scheme.
|
||||
func SignV4(r *Record, privkey *ecdsa.PrivateKey) error {
|
||||
// Copy r to avoid modifying it if signing fails.
|
||||
cpy := *r
|
||||
cpy.Set(ID("v4"))
|
||||
cpy.Set(Secp256k1(privkey.PublicKey))
|
||||
|
||||
h := sha3.NewKeccak256()
|
||||
rlp.Encode(h, cpy.AppendElements(nil))
|
||||
sig, err := crypto.Sign(h.Sum(nil), privkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sig = sig[:len(sig)-1] // remove v
|
||||
if err = cpy.SetSig("v4", sig); err == nil {
|
||||
*r = cpy
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// s256raw is an unparsed secp256k1 public key entry.
|
||||
type s256raw []byte
|
||||
|
||||
func (s256raw) ENRKey() string { return "secp256k1" }
|
||||
|
||||
func (v4ID) Verify(r *Record, sig []byte) error {
|
||||
var entry s256raw
|
||||
if err := r.Load(&entry); err != nil {
|
||||
return err
|
||||
} else if len(entry) != 33 {
|
||||
return fmt.Errorf("invalid public key")
|
||||
}
|
||||
|
||||
h := sha3.NewKeccak256()
|
||||
rlp.Encode(h, r.AppendElements(nil))
|
||||
if !crypto.VerifySignature(entry, h.Sum(nil), sig) {
|
||||
return errInvalidSig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v4ID) NodeAddr(r *Record) []byte {
|
||||
var pubkey Secp256k1
|
||||
err := r.Load(&pubkey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
buf := make([]byte, 64)
|
||||
math.ReadBits(pubkey.X, buf[:32])
|
||||
math.ReadBits(pubkey.Y, buf[32:])
|
||||
return crypto.Keccak256(buf)
|
||||
}
|
||||
36
p2p/enr/idscheme_test.go
Normal file
36
p2p/enr/idscheme_test.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enr
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Checks that failure to sign leaves the record unmodified.
|
||||
func TestSignError(t *testing.T) {
|
||||
invalidKey := &ecdsa.PrivateKey{D: new(big.Int), PublicKey: *pubkey}
|
||||
|
||||
var r Record
|
||||
if err := SignV4(&r, invalidKey); err == nil {
|
||||
t.Fatal("expected error from SignV4")
|
||||
}
|
||||
if len(r.pairs) > 0 {
|
||||
t.Fatal("expected empty record, have", r.pairs)
|
||||
}
|
||||
}
|
||||
|
|
@ -21,10 +21,10 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 8 // Minor version component of the current release
|
||||
VersionPatch = 8 // Patch version component of the current release
|
||||
VersionMeta = "stable" // Version metadata to append to the version string
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 8 // Minor version component of the current release
|
||||
VersionPatch = 9 // Patch version component of the current release
|
||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
// Version holds the textual version string.
|
||||
|
|
|
|||
Loading…
Reference in a new issue