diff --git a/bond/bond_info.go b/bond/bond_info.go index 5294732..17e560b 100644 --- a/bond/bond_info.go +++ b/bond/bond_info.go @@ -128,12 +128,14 @@ func (cbi *BondInfo) RemoveBondAtIndex(index uint32) error { } // PostBondReqFromBondInfo converts the provided bond info into a post bond request. -func PostBondReqFromBondInfo(info *BondInfo) (*protocolsPb.PostBondRequest, error) { +func PostBondReqFromBondInfo(info *BondInfo, accountID []byte) (*protocolsPb.PostBondRequest, error) { if len(info.bondParams) == 0 { return nil, fmt.Errorf("no bonds provided") } - req := &protocolsPb.PostBondRequest{} + req := &protocolsPb.PostBondRequest{ + AccountID: accountID, + } for _, bp := range info.bondParams { req.Bonds = append(req.Bonds, &protocolsPb.Bond{BondID: []byte(bp.ID)}) } diff --git a/bond/bond_info_test.go b/bond/bond_info_test.go index 73030af..91af8c1 100644 --- a/bond/bond_info_test.go +++ b/bond/bond_info_test.go @@ -46,11 +46,16 @@ func TestBondInfo(t *testing.T) { // Ensure a post bond request can be created from a bond info. bInfo.AddBonds([]*BondParams{bond1}, mockTime) - bondReq, err := PostBondReqFromBondInfo(bInfo) + testAccountID := []byte("test-account-id") + bondReq, err := PostBondReqFromBondInfo(bInfo, testAccountID) if err != nil { t.Fatalf("Unexpected error creating a post bond request from bond info") } + if string(bondReq.AccountID) != string(testAccountID) { + t.Errorf("Expected account ID %s, got %s", string(testAccountID), string(bondReq.AccountID)) + } + if string(bondReq.Bonds[0].BondID) != bond1.ID { t.Fatalf("Expected bond id %s in request, got %s", bond1.ID, string(bondReq.Bonds[0].BondID)) } diff --git a/client/client.go b/client/client.go index 7ef4ce0..fb2eed9 100644 --- a/client/client.go +++ b/client/client.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/decred/dcrd/crypto/blake256" "github.com/decred/slog" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/crypto" @@ -44,6 +45,7 @@ type Config struct { // Client represents a tatanka client. type Client struct { cfg *Config + accountID []byte host host.Host topicRegistry *topicRegistry bondInfo *bond.BondInfo @@ -61,6 +63,11 @@ func (c *Client) PeerID() peer.ID { return c.host.ID() } +// AccountID returns the account ID of the client. +func (c *Client) AccountID() []byte { + return c.accountID +} + // ConnectedTatankaNodePeerID returns the peer ID of the currently connected // tatanka node. If no tatanka node connection is active, an empty string // is returned. @@ -72,10 +79,28 @@ func (c *Client) ConnectedTatankaNodePeerID() string { return mc.remotePeerID().String() } +// accountIDFromPublicKey generates an account ID from a public key using BLAKE256 hash. +func accountIDFromPublicKey(privKey crypto.PrivKey) ([]byte, error) { + pubKey := privKey.GetPublic() + pubKeyBytes, err := crypto.MarshalPublicKey(pubKey) + if err != nil { + return nil, fmt.Errorf("failed to marshal public key: %w", err) + } + + hash := blake256.Sum256(pubKeyBytes) + return hash[:], nil +} + // NewClient initializes a new tatanka client. func NewClient(cfg *Config) (*Client, error) { + accountID, err := accountIDFromPublicKey(cfg.PrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to generate account ID: %w", err) + } + c := &Client{ cfg: cfg, + accountID: accountID, topicRegistry: newTopicRegistry(), bondInfo: bond.NewBondInfo(), log: cfg.Logger, @@ -286,7 +311,7 @@ func (c *Client) Run(ctx context.Context, bonds []*bond.BondParams) error { // Set default connection factory if not already set (tests may override). if c.connFactory == nil { c.connFactory = func(peerID peer.ID) meshConn { - return newMeshConnection(c.host, peerID, c.log, c.bondInfo, c.topicRegistry.fetchTopics, c.handlePushMessage) + return newMeshConnection(c.host, peerID, c.log, c.bondInfo, c.accountID, c.topicRegistry.fetchTopics, c.handlePushMessage) } } diff --git a/client/mesh_connection.go b/client/mesh_connection.go index 54c3002..3d42f1a 100644 --- a/client/mesh_connection.go +++ b/client/mesh_connection.go @@ -34,6 +34,7 @@ type meshConnection struct { handleMessage func(*protocolsPb.PushMessage) fetchTopics func() []string bondInfo *bond.BondInfo + accountID []byte log slog.Logger cancelFuncMtx sync.RWMutex @@ -45,7 +46,7 @@ type meshConnection struct { var _ meshConn = (*meshConnection)(nil) -func newMeshConnection(host host.Host, peerID peer.ID, logger slog.Logger, bondInfo *bond.BondInfo, fetchTopics func() []string, handleMessage func(*protocolsPb.PushMessage)) *meshConnection { +func newMeshConnection(host host.Host, peerID peer.ID, logger slog.Logger, bondInfo *bond.BondInfo, accountID []byte, fetchTopics func() []string, handleMessage func(*protocolsPb.PushMessage)) *meshConnection { return &meshConnection{ host: host, peerID: peerID, @@ -53,6 +54,7 @@ func newMeshConnection(host host.Host, peerID peer.ID, logger slog.Logger, bondI handleMessage: handleMessage, fetchTopics: fetchTopics, bondInfo: bondInfo, + accountID: accountID, readyCh: make(chan struct{}, 1), } } @@ -210,7 +212,7 @@ func (m *meshConnection) postBondInternal(ctx context.Context, req *protocolsPb. func (m *meshConnection) postBond(ctx context.Context) error { hostID := m.host.ID() for range maxPostBondRetries { - req, err := bond.PostBondReqFromBondInfo(m.bondInfo) + req, err := bond.PostBondReqFromBondInfo(m.bondInfo, m.accountID) if err != nil { return fmt.Errorf("failed to create post bond request: %w", err) } diff --git a/client/mesh_connection_test.go b/client/mesh_connection_test.go index bfe41f6..88e229e 100644 --- a/client/mesh_connection_test.go +++ b/client/mesh_connection_test.go @@ -167,6 +167,7 @@ func newMeshConnHarness(t *testing.T, topics []string) *meshConnHarness { h.tatankaHost.ID(), h.logger, bondInfo, + []byte("test-account-id"), func() []string { return topics }, func(msg *protocolsPb.PushMessage) { h.clientReceived <- msg @@ -514,7 +515,7 @@ func TestMeshConnection_PostBondWithRetries(t *testing.T) { }, } - bondReq, err := bond.PostBondReqFromBondInfo(h.meshConn.bondInfo) + bondReq, err := bond.PostBondReqFromBondInfo(h.meshConn.bondInfo, h.meshConn.accountID) if err != nil { t.Fatalf("Unexpected error creating post bond request: %v", err) } diff --git a/go.mod b/go.mod index 7d92e36..51d2686 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/martonp/tatanka-mesh go 1.24.9 require ( + github.com/decred/dcrd/crypto/blake256 v1.1.0 github.com/decred/slog v1.2.0 github.com/go-chi/chi/v5 v5.2.3 github.com/go-chi/cors v1.2.2 diff --git a/protocols/pb/protocols.pb.go b/protocols/pb/protocols.pb.go index 8b3b970..72c2831 100644 --- a/protocols/pb/protocols.pb.go +++ b/protocols/pb/protocols.pb.go @@ -915,7 +915,8 @@ func (x *Bond) GetBondID() []byte { type PostBondRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Bonds []*Bond `protobuf:"bytes,1,rep,name=bonds,proto3" json:"bonds,omitempty"` + AccountID []byte `protobuf:"bytes,1,opt,name=accountID,proto3" json:"accountID,omitempty"` + Bonds []*Bond `protobuf:"bytes,2,rep,name=bonds,proto3" json:"bonds,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -950,6 +951,13 @@ func (*PostBondRequest) Descriptor() ([]byte, []int) { return file_protocols_proto_rawDescGZIP(), []int{14} } +func (x *PostBondRequest) GetAccountID() []byte { + if x != nil { + return x.AccountID + } + return nil +} + func (x *PostBondRequest) GetBonds() []*Bond { if x != nil { return x.Bonds @@ -1683,9 +1691,10 @@ const file_protocols_proto_rawDesc = "" + "\x05addrs\x18\x01 \x03(\fR\x05addrs\"8\n" + "\x04Bond\x12\x18\n" + "\aassetID\x18\x01 \x01(\rR\aassetID\x12\x16\n" + - "\x06bondID\x18\x02 \x01(\fR\x06bondID\"1\n" + - "\x0fPostBondRequest\x12\x1e\n" + - "\x05bonds\x18\x01 \x03(\v2\b.pb.BondR\x05bonds\"6\n" + + "\x06bondID\x18\x02 \x01(\fR\x06bondID\"O\n" + + "\x0fPostBondRequest\x12\x1c\n" + + "\taccountID\x18\x01 \x01(\fR\taccountID\x12\x1e\n" + + "\x05bonds\x18\x02 \x03(\v2\b.pb.BondR\x05bonds\"6\n" + "\x10PostBondResponse\x12\"\n" + "\fbondStrength\x18\x01 \x01(\rR\fbondStrength\"0\n" + "\bPeerInfo\x12\x0e\n" + diff --git a/protocols/pb/protocols.proto b/protocols/pb/protocols.proto index 5f86337..c372059 100644 --- a/protocols/pb/protocols.proto +++ b/protocols/pb/protocols.proto @@ -104,7 +104,8 @@ message Bond { } message PostBondRequest { - repeated Bond bonds = 1; + bytes accountID = 1; + repeated Bond bonds = 2; }