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 v rlp.RawValue
} }
// Record represents Ethereum Node Record
type Record struct { type Record struct {
seq uint32 // sequence number seq uint32 // sequence number
signature []byte // record's signature signature []byte // record's signature
@ -65,15 +66,20 @@ type Record struct {
pairs []pair // sorted list of all key/value pairs pairs []pair // sorted list of all key/value pairs
} }
// Seq return record's sequence number
func (r Record) Seq() uint32 { func (r Record) Seq() uint32 {
return r.seq return r.seq
} }
// SetSeq update record's sequence number. Nodes should increase the number whenever the record changes.
func (r *Record) SetSeq(s uint32) { func (r *Record) SetSeq(s uint32) {
r.signature = nil r.signature = nil
r.seq = s 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) { 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() }) 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}) 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 { func (r Record) EncodeRLP(w io.Writer) error {
if r.signature == nil { if r.signature == nil {
return errors.New("record is not signed") return errors.New("record is not signed")
@ -117,6 +125,7 @@ func (r Record) EncodeRLP(w io.Writer) error {
return err return err
} }
// DecodeRLP implements rlp.Decoder.
func (r *Record) DecodeRLP(s *rlp.Stream) error { func (r *Record) DecodeRLP(s *rlp.Stream) error {
raw, err := s.Raw() raw, err := s.Raw()
if err != nil { if err != nil {
@ -174,6 +183,7 @@ func (r *Record) DecodeRLP(s *rlp.Stream) error {
return nil return nil
} }
// NodeAddr returns node's address - keccak256 hash of the public key.
func (r *Record) NodeAddr() ([]byte, error) { func (r *Record) NodeAddr() ([]byte, error) {
var secp256k1 Secp256k1 var secp256k1 Secp256k1
@ -189,6 +199,9 @@ func (r *Record) NodeAddr() ([]byte, error) {
return digest.Bytes(), nil 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 { func (r *Record) Sign(privkey *ecdsa.PrivateKey) error {
pk := (*btcec.PublicKey)(&privkey.PublicKey) pk := (*btcec.PublicKey)(&privkey.PublicKey)
r.seq = r.seq + 1 r.seq = r.seq + 1

View file

@ -34,6 +34,7 @@ const (
privkeyHex = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" privkeyHex = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
) )
// TestGetSetID tests encoding/decoding and setting/getting of the enr.ID type
func TestGetSetID(t *testing.T) { func TestGetSetID(t *testing.T) {
id := ID("someid") id := ID("someid")
var r Record 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) { func TestGetSetIP4(t *testing.T) {
ip := IP4(net.IP{192, 168, 0, 3}) ip := IP4(net.IP{192, 168, 0, 3})
var r Record 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) { 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}) ip := IP6(net.IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68})
var r Record 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) { func TestGetSetDiscPort(t *testing.T) {
port := DiscPort(30309) port := DiscPort(30309)
var r Record 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) { func TestGetSetSecp256k1(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex) privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil { 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) { func TestDirty(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex) privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil { 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) { func TestGetSetOverwrite(t *testing.T) {
var r Record 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) { func TestSignEncodeAndDecode(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex) privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil { 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) { func TestNodeAddress(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex) privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil { 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) { func TestPythonInterop(t *testing.T) {
enc, _ := hex.DecodeString("f896b840638a54215d80a6713c8d523a6adc4e6e73652d859103a36b700851cb0e61b66b8ebfc1a610c57d732ec6e0a8f06a9a7a28df5051ece514702ff9cdff0b11f454018664697363763582765f82696490736563703235366b312d6b656363616b83697034847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138") enc, _ := hex.DecodeString("f896b840638a54215d80a6713c8d523a6adc4e6e73652d859103a36b700851cb0e61b66b8ebfc1a610c57d732ec6e0a8f06a9a7a28df5051ece514702ff9cdff0b11f454018664697363763582765f82696490736563703235366b312d6b656363616b83697034847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138")
var r Record 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) { func TestRecordTooBig(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex) privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil { 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) { func TestSignEncodeAndDecodeRandom(t *testing.T) {
privkey, err := crypto.HexToECDSA(privkeyHex) privkey, err := crypto.HexToECDSA(privkeyHex)
if err != nil { if err != nil {

View file

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