p2p/enr: improved documentation.

This commit is contained in:
Anton Evangelatov 2017-12-06 13:53:43 +01:00 committed by Felix Lange
parent c219a2ab8b
commit 3b304ff8bf
3 changed files with 36 additions and 0 deletions

View file

@ -58,6 +58,7 @@ type pair struct {
v rlp.RawValue
}
// Record represents Ethereum Node Record
type Record struct {
seq uint32 // sequence number
signature []byte // record's signature
@ -65,15 +66,20 @@ type Record struct {
pairs []pair // sorted list of all key/value pairs
}
// Seq return record's sequence number
func (r Record) Seq() uint32 {
return r.seq
}
// SetSeq update record's sequence number. Nodes should increase the number whenever the record changes.
func (r *Record) SetSeq(s uint32) {
r.signature = nil
r.seq = s
}
// Load is loading a key/value pair based on provided key from the record.
// It returns false if such key cannot be found.
// It returns an error if there is a problem with RLP decoding of the pair.
func (r *Record) Load(k Key) (bool, error) {
i := sort.Search(len(r.pairs), func(i int) bool { return r.pairs[i].k >= k.ENRKey() })
@ -109,6 +115,8 @@ func (r *Record) Set(k Key) {
r.pairs = append(r.pairs, pair{k.ENRKey(), blob})
}
// EncodeRLP implements rlp.Encoder.
// Sign must be called prior to calling rlp.Encode
func (r Record) EncodeRLP(w io.Writer) error {
if r.signature == nil {
return errors.New("record is not signed")
@ -117,6 +125,7 @@ func (r Record) EncodeRLP(w io.Writer) error {
return err
}
// DecodeRLP implements rlp.Decoder.
func (r *Record) DecodeRLP(s *rlp.Stream) error {
raw, err := s.Raw()
if err != nil {
@ -174,6 +183,7 @@ func (r *Record) DecodeRLP(s *rlp.Stream) error {
return nil
}
// NodeAddr returns node's address - keccak256 hash of the public key.
func (r *Record) NodeAddr() ([]byte, error) {
var secp256k1 Secp256k1
@ -189,6 +199,9 @@ func (r *Record) NodeAddr() ([]byte, error) {
return digest.Bytes(), nil
}
// Sign signs the record with the provided private key.
// It updates record's identity scheme and public key.
// It returns an error if signed record is bigger than 300 bytes.
func (r *Record) Sign(privkey *ecdsa.PrivateKey) error {
pk := (*btcec.PublicKey)(&privkey.PublicKey)
r.seq = r.seq + 1

View file

@ -34,6 +34,7 @@ const (
privkeyHex = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
)
// TestGetSetID tests encoding/decoding and setting/getting of the enr.ID type
func TestGetSetID(t *testing.T) {
id := ID("someid")
var r Record
@ -51,6 +52,7 @@ func TestGetSetID(t *testing.T) {
}
}
// TestGetSetIP4 tests encoding/decoding and setting/getting of the enr.IP4 type
func TestGetSetIP4(t *testing.T) {
ip := IP4(net.IP{192, 168, 0, 3})
var r Record
@ -68,6 +70,7 @@ func TestGetSetIP4(t *testing.T) {
}
}
// TestGetSetIP6 tests encoding/decoding and setting/getting of the enr.IP6 type
func TestGetSetIP6(t *testing.T) {
ip := IP6(net.IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68})
var r Record
@ -85,6 +88,7 @@ func TestGetSetIP6(t *testing.T) {
}
}
// TestGetSetDiscPort tests encoding/decoding and setting/getting of the enr.DiscPort type
func TestGetSetDiscPort(t *testing.T) {
port := DiscPort(30309)
var r Record
@ -102,6 +106,7 @@ func TestGetSetDiscPort(t *testing.T) {
}
}
// TestGetSetSecp256k1 tests encoding/decoding and setting/getting of the enr.Secp256k1 type
func TestGetSetSecp256k1(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil {
@ -129,6 +134,7 @@ func TestGetSetSecp256k1(t *testing.T) {
}
}
// TestDirty tests record signature removal on setting of new key/value pair in record.
func TestDirty(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil {
@ -153,6 +159,7 @@ func TestDirty(t *testing.T) {
}
}
// TestGetSetOverwrite tests value overwrite when setting a new value with an existing key in record.
func TestGetSetOverwrite(t *testing.T) {
var r Record
@ -174,6 +181,7 @@ func TestGetSetOverwrite(t *testing.T) {
}
}
// TestSignEncodeAndDecode tests signing, RLP encoding and RLP decoding of a record.
func TestSignEncodeAndDecode(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil {
@ -217,6 +225,7 @@ func TestSignEncodeAndDecode(t *testing.T) {
}
}
// TestNodeAddress tests that record returns correct node address - keccak256 hash of the public key.
func TestNodeAddress(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil {
@ -242,6 +251,7 @@ func TestNodeAddress(t *testing.T) {
}
}
// TestPythonInterop tests that Go implementation can successfully RLP decode a record produced by Python implementation.
func TestPythonInterop(t *testing.T) {
enc, _ := hex.DecodeString("f896b840638a54215d80a6713c8d523a6adc4e6e73652d859103a36b700851cb0e61b66b8ebfc1a610c57d732ec6e0a8f06a9a7a28df5051ece514702ff9cdff0b11f454018664697363763582765f82696490736563703235366b312d6b656363616b83697034847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138")
var r Record
@ -271,6 +281,7 @@ func TestPythonInterop(t *testing.T) {
}
}
// TestRecordTooBig tests that records bigger than 300 bytes cannot be signed.
func TestRecordTooBig(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil {
@ -296,6 +307,7 @@ func TestRecordTooBig(t *testing.T) {
}
}
// TestSignEncodeAndDecodeRandom tests encoding/decoding of records containing random key/value pairs.
func TestSignEncodeAndDecodeRandom(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil {

View file

@ -43,6 +43,8 @@ func (g *generic) DecodeRLP(s *rlp.Stream) error {
return s.Decode(&g.value)
}
// WithKey returns a new Key that can be set in a Record.
// v must implement the rlp.Encoder and rlp.Decoder interface.
func WithKey(k string, v interface{}) Key {
return &generic{key: k, value: v}
}
@ -50,6 +52,7 @@ func WithKey(k string, v interface{}) Key {
// DiscPort represents an UDP port for discovery v5.
type DiscPort uint16
// ENRKey returns the node record key for an UDP port for discovery.
func (DiscPort) ENRKey() string {
return "discv5"
}
@ -59,6 +62,7 @@ const ID_SECP256k1_KECCAK = "secp256k1-keccak" // identity scheme identifier
// ID is the name of the identity scheme, e.g. "secp256k1-keccak".
type ID string
// ENRKey returns the node record key for its identity scheme.
func (ID) ENRKey() string {
return "id"
}
@ -71,6 +75,7 @@ func (IP4) ENRKey() string {
return "ip4"
}
// EncodeRLP implements rlp.Encoder.
func (v IP4) EncodeRLP(w io.Writer) error {
ip4 := net.IP(v).To4()
if ip4 == nil {
@ -79,6 +84,7 @@ func (v IP4) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, ip4)
}
// DecodeRLP implements rlp.Decoder.
func (v *IP4) DecodeRLP(s *rlp.Stream) error {
if err := s.Decode((*net.IP)(v)); err != nil {
return err
@ -97,11 +103,13 @@ func (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
@ -115,16 +123,19 @@ func (v *IP6) DecodeRLP(s *rlp.Stream) error {
// Secp256k1 is compressed secp256k1 public key.
type Secp256k1 ecdsa.PublicKey
// ENRKey returns the node record key for the secp256k1 public key.
func (Secp256k1) ENRKey() string {
return "secp256k1"
}
// EncodeRLP implements rlp.Encoder.
func (v Secp256k1) EncodeRLP(w io.Writer) error {
pk := btcec.PublicKey(v)
return rlp.Encode(w, pk.SerializeCompressed())
}
// DecodeRLP implements rlp.Decoder.
func (v *Secp256k1) DecodeRLP(s *rlp.Stream) error {
buf := make([]byte, 33)
if err := s.Decode(&buf); err != nil {