-
Notifications
You must be signed in to change notification settings - Fork 2.2k
channeldb: add persist nodeannounment config in db #8690
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b0815a1
d7719d8
815e069
75ffcaf
03e2ac6
31820dc
9365f95
371c12e
5899f80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,222 @@ | ||
| package channeldb | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "image/color" | ||
| "io" | ||
| "net" | ||
|
|
||
| "github.com/btcsuite/btcd/btcec/v2" | ||
| "github.com/lightningnetwork/lnd/kvdb" | ||
| "github.com/lightningnetwork/lnd/lnwire" | ||
| ) | ||
|
|
||
| var ( | ||
| // nodeAnnouncementBucket stores announcement config pertaining to node. | ||
| // This bucket allows one to query for persisted node announcement | ||
| // config and use it when starting orrestarting a node | ||
| nodeAnnouncementBucket = []byte("nab") | ||
| ) | ||
|
|
||
| // NodeAlias is a hex encoded UTF-8 string that may be displayed as an | ||
| // alternative to the node's ID. Notice that aliases are not unique and may be | ||
| // freely chosen by the node operators. | ||
| type NodeAlias [32]byte | ||
|
|
||
| // String returns a utf8 string representation of the alias bytes. | ||
| func (n NodeAlias) String() string { | ||
| // Trim trailing zero-bytes for presentation | ||
| return string(bytes.Trim(n[:], "\x00")) | ||
| } | ||
|
|
||
| type NodeAnnouncement struct { | ||
| // Alias is used to customize node's appearance in maps and | ||
| // graphs | ||
| Alias NodeAlias | ||
|
|
||
| // Color represent the hexadecimal value that node operators can assign | ||
| // to their nodes. It's represented as a hex string. | ||
| Color color.RGBA | ||
|
|
||
| // NodeID is a public key which is used as node identification. | ||
| NodeID [33]byte | ||
|
|
||
| // Address includes two specification fields: 'ipv6' and 'port' on | ||
| // which the node is accepting incoming connections. | ||
| Addresses []net.Addr | ||
|
|
||
| // Features is the list of protocol features this node supports. | ||
| Features *lnwire.RawFeatureVector | ||
| } | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we already persist our node announcement in the bucket storing all the collected node announcement. Can we not just grab our latest one from there?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know where the collected node announcements are stored. Are you referring to here where all nodes within a channel graph are stored and their node announcements (if they have any)?.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes - we store our node announcement there too right? if so, then i dont think we need a whole new bucket for it. Let me know if my assumption is incorrect
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ellemouton I explored the code related to graph DB, and it turns out you're right. We're indeed storing our node along with the node announcement, and we can easily retrieve the stored values from this method when lnd starts. Since this is the case, I will close this PR and open a new one to replace it. Does that make sense? |
||
| // Sync performs a full database sync which writes the current up-to-date data | ||
| // within the struct to the database. | ||
| func (n *NodeAnnouncement) Sync(db *DB) error { | ||
| return kvdb.Update(db, func(tx kvdb.RwTx) error { | ||
| nodeAnnBucket := tx.ReadWriteBucket(nodeAnnouncementBucket) | ||
| if nodeAnnBucket == nil { | ||
| return ErrNodeAnnBucketNotFound | ||
| } | ||
|
|
||
| return putNodeAnnouncement(nodeAnnBucket, n) | ||
| }, func() {}) | ||
| } | ||
|
|
||
| // FetchNodeAnnouncement attempts to lookup the data for NodeAnnouncement based | ||
| // on a target identity public key. If a particular NodeAnnouncement for the | ||
| // passed identity public key cannot be found, then returns ErrNodeAnnNotFound | ||
| func (d *DB) FetchNodeAnnouncement(identity *btcec.PublicKey) (*NodeAnnouncement, error) { | ||
| var nodeAnnouncement *NodeAnnouncement | ||
|
|
||
| err := kvdb.View(d, func(tx kvdb.RTx) error { | ||
| nodeAnn, err := fetchNodeAnnouncement(tx, identity) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| nodeAnnouncement = nodeAnn | ||
| return nil | ||
| }, func() { | ||
| nodeAnnouncement = nil | ||
| }) | ||
|
|
||
| return nodeAnnouncement, err | ||
| } | ||
|
|
||
| func fetchNodeAnnouncement(tx kvdb.RTx, targetPub *btcec.PublicKey) (*NodeAnnouncement, error) { | ||
| // First fetch the bucket for storing node announcement, bailing out | ||
| // early if it hasn't been created yet. | ||
| nodeAnnBucket := tx.ReadBucket(nodeAnnouncementBucket) | ||
| if nodeAnnBucket == nil { | ||
| return nil, ErrNodeAnnBucketNotFound | ||
| } | ||
|
|
||
| // If a node announcement for that particular public key cannot be | ||
| // located, then exit early with ErrNodeAnnNotFound | ||
| pubkey := targetPub.SerializeCompressed() | ||
| nodeAnnBytes := nodeAnnBucket.Get(pubkey) | ||
| if nodeAnnBytes == nil { | ||
| return nil, ErrNodeAnnNotFound | ||
| } | ||
|
|
||
| // FInally, decode and allocate a fresh NodeAnnouncement object to be | ||
| // returned to the caller | ||
| nodeAnnReader := bytes.NewReader(nodeAnnBytes) | ||
| return deserializeNodeAnnouncement(nodeAnnReader) | ||
|
|
||
| } | ||
|
|
||
| func (d *DB) PutNodeAnnouncement(pubkey [33]byte, alias [32]byte, color color.RGBA, | ||
| addresses []net.Addr, features *lnwire.RawFeatureVector) error { | ||
| nodeAnn := &NodeAnnouncement{ | ||
| Alias: alias, | ||
| Color: color, | ||
| NodeID: pubkey, | ||
| Addresses: addresses, | ||
| Features: features, | ||
| } | ||
|
|
||
| return kvdb.Update(d, func(tx kvdb.RwTx) error { | ||
| nodeAnnBucket := tx.ReadWriteBucket(nodeAnnouncementBucket) | ||
| if nodeAnnBucket == nil { | ||
| return ErrNodeAnnBucketNotFound | ||
| } | ||
|
|
||
| return putNodeAnnouncement(nodeAnnBucket, nodeAnn) | ||
|
|
||
| }, func() {}) | ||
| } | ||
|
|
||
| func putNodeAnnouncement(nodeAnnBucket kvdb.RwBucket, n *NodeAnnouncement) error { | ||
| var b bytes.Buffer | ||
| if err := serializeNodeAnnouncement(&b, n); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| nodePub := n.NodeID[:] | ||
| return nodeAnnBucket.Put(nodePub, b.Bytes()) | ||
| } | ||
|
|
||
| func serializeNodeAnnouncement(w io.Writer, n *NodeAnnouncement) error { | ||
| // Serialize Alias | ||
| if _, err := w.Write([]byte(n.Alias[:])); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Serialize Color | ||
| // Write R | ||
| if _, err := w.Write([]byte{n.Color.R}); err != nil { | ||
| return err | ||
| } | ||
| // Write G | ||
| if _, err := w.Write([]byte{n.Color.G}); err != nil { | ||
| return err | ||
| } | ||
| // Write B | ||
| if _, err := w.Write([]byte{n.Color.B}); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Serialize NodeID | ||
| if _, err := w.Write(n.NodeID[:]); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Serialize Addresses | ||
| var addrBuffer bytes.Buffer | ||
| if err := lnwire.WriteNetAddrs(&addrBuffer, n.Addresses); err != nil { | ||
| return err | ||
| } | ||
| if _, err := w.Write(addrBuffer.Bytes()); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Serialize Features | ||
| var featsBuffer bytes.Buffer | ||
| if err := lnwire.WriteRawFeatureVector(&featsBuffer, n.Features); err != nil { | ||
| return err | ||
| } | ||
| if _, err := w.Write(featsBuffer.Bytes()); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func deserializeNodeAnnouncement(r io.Reader) (*NodeAnnouncement, error) { | ||
| var err error | ||
| nodeAnn := &NodeAnnouncement{} | ||
|
|
||
| // Read Alias | ||
| aliasBuf := make([]byte, 32) | ||
| if _, err := io.ReadFull(r, aliasBuf); err != nil { | ||
| return nil, err | ||
| } | ||
| nodeAnn.Alias = [32]byte(aliasBuf) | ||
|
|
||
| // Read Color | ||
| // colorBuf contains R, G, B, A (alpha), but the color.RGBA type only | ||
| // expects R, G, B, so we need to slice it. | ||
| colorBuf := make([]byte, 3) | ||
| if _, err := io.ReadFull(r, colorBuf); err != nil { | ||
| return nil, err | ||
| } | ||
| nodeAnn.Color = color.RGBA{colorBuf[0], colorBuf[1], colorBuf[2], 0} | ||
|
|
||
| var pub [33]byte | ||
| if _, err := io.ReadFull(r, pub[:]); err != nil { | ||
| return nil, err | ||
| } | ||
| nodeAnn.NodeID = pub | ||
|
|
||
| if err := lnwire.ReadElement(r, &nodeAnn.Addresses); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| if err := lnwire.ReadElement(r, &nodeAnn.Features); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return nodeAnn, err | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package channeldb | ||
|
|
||
| import ( | ||
| "image/color" | ||
| "net" | ||
| "reflect" | ||
| "testing" | ||
|
|
||
| "github.com/btcsuite/btcd/btcec/v2" | ||
| "github.com/lightningnetwork/lnd/lnwire" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestNodeAnnouncementEncodeDecode(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| fullDB, err := MakeTestDB(t) | ||
| require.NoError(t, err, "unable to make test database") | ||
|
|
||
| // We'll start by creating initial data to use for populating our test | ||
| // node announcement instance | ||
| var alias [32]byte | ||
| copy(alias[:], []byte("alice")) | ||
| color := color.RGBA{255, 255, 255, 0} | ||
| _, pub := btcec.PrivKeyFromBytes(key[:]) | ||
| address := []net.Addr{testAddr} | ||
| features := lnwire.RawFeatureVector{} | ||
|
|
||
| nodeAnn := &NodeAnnouncement{ | ||
| Alias: alias, | ||
| Color: color, | ||
| NodeID: [33]byte(pub.SerializeCompressed()), | ||
| Addresses: address, | ||
| Features: &features, | ||
| } | ||
| if err := nodeAnn.Sync(fullDB); err != nil { | ||
| t.Fatalf("unable to sync node announcement: %v", err) | ||
| } | ||
|
|
||
| // Fetch the current node announcement from the database, it should | ||
| // match the one we just persisted | ||
| persistedNodeAnn, err := fullDB.FetchNodeAnnouncement(pub) | ||
| require.NoError(t, err, "unable to fetch node announcement") | ||
| if nodeAnn.Alias != persistedNodeAnn.Alias { | ||
| t.Fatalf("node aliases don't match: expected %v, got %v", | ||
| nodeAnn.Alias.String(), persistedNodeAnn.Alias.String()) | ||
| } | ||
|
|
||
| if nodeAnn.Color != persistedNodeAnn.Color { | ||
| t.Fatalf("node colors don't match: expected %v, got %v", | ||
| nodeAnn.Color, persistedNodeAnn.Color) | ||
| } | ||
|
|
||
| if nodeAnn.NodeID != persistedNodeAnn.NodeID { | ||
| t.Fatalf("node nodeIds don't match: expected %v, got %v", | ||
| nodeAnn.NodeID, persistedNodeAnn.NodeID) | ||
| } | ||
|
|
||
| // Verify that the addresses of the node announcements are the same. | ||
| if !reflect.DeepEqual(nodeAnn.Addresses, persistedNodeAnn.Addresses) { | ||
| t.Fatalf("node addresses don't match: expected %v, got %v", | ||
| nodeAnn.Addresses, persistedNodeAnn.Addresses) | ||
| } | ||
|
|
||
| } |
Uh oh!
There was an error while loading. Please reload this page.