diff --git a/cmd/horcrux/cmd/address.go b/cmd/horcrux/cmd/address.go index d0baf136..228f8163 100644 --- a/cmd/horcrux/cmd/address.go +++ b/cmd/horcrux/cmd/address.go @@ -47,7 +47,7 @@ func addressCmd() *cobra.Command { key, err := signer.LoadCosignerEd25519Key(keyFile) if err != nil { - return fmt.Errorf("error reading cosigner key: %w, check that key is present for chain ID: %s", err, chainID) + return fmt.Errorf("error reading cosigner key: %w, check that key is present for chain Index: %s", err, chainID) } pubKey = key.PubKey @@ -58,7 +58,7 @@ func addressCmd() *cobra.Command { } keyFile, err := config.KeyFileExistsSingleSigner(chainID) if err != nil { - return fmt.Errorf("error reading priv-validator key: %w, check that key is present for chain ID: %s", err, chainID) + return fmt.Errorf("error reading priv-validator key: %w, check that key is present for chain Index: %s", err, chainID) } filePV := cometprivval.LoadFilePVEmptyState(keyFile, "") diff --git a/cmd/horcrux/cmd/config_test.go b/cmd/horcrux/cmd/config_test.go index fae9db5e..755c34d2 100644 --- a/cmd/horcrux/cmd/config_test.go +++ b/cmd/horcrux/cmd/config_test.go @@ -94,7 +94,7 @@ grpcAddr: "" "--raft-timeout", "500ms", "--grpc-timeout", "500ms", }, - expectErr: `failed to parse cosigner (shard ID: 1) p2p address: parse "://10.168.1.1:2222": missing protocol scheme`, + expectErr: `failed to parse cosigner (shard Index: 1) p2p address: parse "://10.168.1.1:2222": missing protocol scheme`, }, { name: "invalid threshold", diff --git a/cmd/horcrux/cmd/leader_election.go b/cmd/horcrux/cmd/leader_election.go index c14b409e..8dba6858 100644 --- a/cmd/horcrux/cmd/leader_election.go +++ b/cmd/horcrux/cmd/leader_election.go @@ -24,7 +24,7 @@ func leaderElectionCmd() *cobra.Command { Use: "elect [node_id]", Short: "Elect new raft leader", Long: `To choose the next eligible leader, pass no argument. -To choose a specific leader, pass that leader's ID as an argument. +To choose a specific leader, pass that leader's Index as an argument. `, Args: cobra.RangeArgs(0, 1), Example: `horcrux elect # elect next eligible leader @@ -140,7 +140,7 @@ func getLeaderCmd() *cobra.Command { } if p2pListen == "" { - return fmt.Errorf("cosigner config does not exist for our shard ID %d", id) + return fmt.Errorf("cosigner config does not exist for our shard Index %d", id) } retryOpts := []grpcretry.CallOption{ diff --git a/cmd/horcrux/cmd/shards.go b/cmd/horcrux/cmd/shards.go index 5dbcb95b..6871b843 100644 --- a/cmd/horcrux/cmd/shards.go +++ b/cmd/horcrux/cmd/shards.go @@ -150,7 +150,7 @@ func createCosignerEd25519ShardsCmd() *cobra.Command { _ = cmd.MarkFlagRequired(flagThreshold) f.String(flagKeyFile, "", "priv_validator_key.json file to shard") _ = cmd.MarkFlagRequired(flagKeyFile) - f.String(flagChainID, "", "key shards will sign for this chain ID") + f.String(flagChainID, "", "key shards will sign for this chain Index") _ = cmd.MarkFlagRequired(flagChainID) return cmd diff --git a/cmd/horcrux/cmd/threshold.go b/cmd/horcrux/cmd/threshold.go index c1e035be..ac2df73f 100644 --- a/cmd/horcrux/cmd/threshold.go +++ b/cmd/horcrux/cmd/threshold.go @@ -55,7 +55,7 @@ func NewThresholdValidator( } if p2pListen == "" { - return nil, nil, fmt.Errorf("cosigner config does not exist for our shard ID %d", security.GetID()) + return nil, nil, fmt.Errorf("cosigner config does not exist for our shard Index %d", security.GetID()) } localCosigner := signer.NewLocalCosigner( @@ -74,7 +74,7 @@ func NewThresholdValidator( return nil, nil, fmt.Errorf("error creating raft directory: %w", err) } - // RAFT node ID is the cosigner ID + // RAFT node Index is the cosigner Index nodeID := fmt.Sprint(security.GetID()) // Start RAFT store listener diff --git a/pkg/types/nonce.go b/pkg/types/nonce.go new file mode 100644 index 00000000..ad4e92ce --- /dev/null +++ b/pkg/types/nonce.go @@ -0,0 +1,30 @@ +/* +Package types: Nonce.go contains the types for nonce and nonces +*/ +package types + +import "time" + +// Nonces contains the ephemeral information generated by one cosigner (local) for all other cosigners (remote). +type Nonces struct { + PubKey []byte + Shares [][]byte +} + +type NoncesWithExpiration struct { + Expiration time.Time + Nonces []Nonces +} + +// Nonce is the ephemeral information from another cosigner (remote) destined for this cosigner (local). +type Nonce struct { + Index int + Share []byte + PubKey []byte +} + +// PartialSignature contains the signature and identifier for a piece of the combined signature. +type PartialSignature struct { + Index int + Signature []byte +} diff --git a/signer/config.go b/signer/config.go index 7db96ff1..edf9bfb2 100644 --- a/signer/config.go +++ b/signer/config.go @@ -242,7 +242,7 @@ type CosignersConfig []CosignerConfig func (cosigners CosignersConfig) Validate() error { // Check IDs to make sure none are duplicated if dupl := duplicateCosigners(cosigners); len(dupl) != 0 { - return fmt.Errorf("found duplicate cosigner shard ID(s) in args: %v", dupl) + return fmt.Errorf("found duplicate cosigner shard Index(s) in args: %v", dupl) } shards := len(cosigners) @@ -250,18 +250,18 @@ func (cosigners CosignersConfig) Validate() error { // Make sure that the cosigner IDs match the number of cosigners. for _, cosigner := range cosigners { if cosigner.ShardID < 1 || cosigner.ShardID > shards { - return fmt.Errorf("cosigner shard ID %d in args is out of range, must be between 1 and %d, inclusive", + return fmt.Errorf("cosigner shard Index %d in args is out of range, must be between 1 and %d, inclusive", cosigner.ShardID, shards) } url, err := url.Parse(cosigner.P2PAddr) if err != nil { - return fmt.Errorf("failed to parse cosigner (shard ID: %d) p2p address: %w", cosigner.ShardID, err) + return fmt.Errorf("failed to parse cosigner (shard Index: %d) p2p address: %w", cosigner.ShardID, err) } host, _, err := net.SplitHostPort(url.Host) if err != nil { - return fmt.Errorf("failed to parse cosigner (shard ID: %d) host port: %w", cosigner.ShardID, err) + return fmt.Errorf("failed to parse cosigner (shard Index: %d) host port: %w", cosigner.ShardID, err) } if host == "0.0.0.0" { @@ -287,7 +287,7 @@ func duplicateCosigners(cosigners []CosignerConfig) (duplicates map[int][]string for shardID, cosigners := range idAddrs { if len(cosigners) == 1 { - // One address per ID is correct. + // One address per Index is correct. delete(idAddrs, shardID) } } diff --git a/signer/config_test.go b/signer/config_test.go index f2d0529d..649eb40a 100644 --- a/signer/config_test.go +++ b/signer/config_test.go @@ -167,7 +167,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, }, }, - expectErr: fmt.Errorf("failed to parse cosigner (shard ID: 1) p2p address: %w", &url.Error{ + expectErr: fmt.Errorf("failed to parse cosigner (shard Index: 1) p2p address: %w", &url.Error{ Op: "parse", URL: ":2222", Err: fmt.Errorf("missing protocol scheme"), @@ -517,7 +517,7 @@ func TestCosignerRSAPubKeysConfigValidate(t *testing.T) { P2PAddr: "tcp://127.0.0.1:2224", }, }, - expectErr: fmt.Errorf("cosigner shard ID 3 in args is out of range, must be between 1 and 2, inclusive"), + expectErr: fmt.Errorf("cosigner shard Index 3 in args is out of range, must be between 1 and 2, inclusive"), }, { name: "duplicate cosigner", @@ -532,7 +532,7 @@ func TestCosignerRSAPubKeysConfigValidate(t *testing.T) { }, }, expectErr: fmt.Errorf( - "found duplicate cosigner shard ID(s) in args: map[2:[tcp://127.0.0.1:2223 tcp://127.0.0.1:2223]]", + "found duplicate cosigner shard Index(s) in args: map[2:[tcp://127.0.0.1:2223 tcp://127.0.0.1:2223]]", ), }, } diff --git a/signer/cosigner.go b/signer/cosigner.go index 7cfa67d0..1be4cc77 100644 --- a/signer/cosigner.go +++ b/signer/cosigner.go @@ -1,3 +1,6 @@ +/* +Package signer: Cosinger is responsible for the network communication between the cosigners +*/ package signer import ( @@ -11,12 +14,19 @@ import ( "github.com/strangelove-ventures/horcrux/signer/proto" ) +type Localcosigner interface { + // TODO - add methods +} +type Remotecosigner interface { + // TODO - add methods +} + // Cosigner interface is a set of methods for an m-of-n threshold signature. // This interface abstracts the underlying key storage and management type Cosigner interface { - // Get the ID of the cosigner - // The ID is the shamir index: 1, 2, etc... - GetID() int + // GetIndex gets the index of the cosigner + // The index is the shamir index: 1, 2, etc... + GetIndex() int // Get the P2P URL (GRPC and Raft) GetAddress() string @@ -37,7 +47,7 @@ type Cosigners []Cosigner func (cosigners Cosigners) GetByID(id int) Cosigner { for _, cosigner := range cosigners { - if cosigner.GetID() == id { + if cosigner.GetIndex() == id { return cosigner } } diff --git a/signer/cosigner_grpc_server.go b/signer/cosigner_grpc_server.go index e41ca4a7..b3e72178 100644 --- a/signer/cosigner_grpc_server.go +++ b/signer/cosigner_grpc_server.go @@ -17,6 +17,7 @@ type CosignerGRPCServer struct { cosigner *LocalCosigner thresholdValidator *ThresholdValidator raftStore *RaftStore + // TODO: add logger and not rely on raftStore.logger proto.UnimplementedCosignerServer } @@ -104,6 +105,7 @@ func (rpc *CosignerGRPCServer) GetNonces( }, nil } +// TODO: // TransferLeadership should not be a CosignerGRPCServer method? func (rpc *CosignerGRPCServer) TransferLeadership( _ context.Context, req *proto.TransferLeadershipRequest, @@ -114,10 +116,10 @@ func (rpc *CosignerGRPCServer) TransferLeadership( leaderID := req.GetLeaderID() if leaderID != "" { for _, c := range rpc.raftStore.Cosigners { - shardID := fmt.Sprint(c.GetID()) + shardID := fmt.Sprint(c.GetIndex()) if shardID == leaderID { raftAddress := p2pURLToRaftAddress(c.GetAddress()) - fmt.Printf("Transferring leadership to ID: %s - Address: %s\n", shardID, raftAddress) + fmt.Printf("Transferring leadership to Index: %s - Address: %s\n", shardID, raftAddress) rpc.raftStore.raft.LeadershipTransferToServer(raft.ServerID(shardID), raft.ServerAddress(raftAddress)) return &proto.TransferLeadershipResponse{LeaderID: shardID, LeaderAddress: raftAddress}, nil } diff --git a/signer/cosigner_health.go b/signer/cosigner_health.go index 93d947ba..3c50e09b 100644 --- a/signer/cosigner_health.go +++ b/signer/cosigner_health.go @@ -62,7 +62,7 @@ func (ch *CosignerHealth) Start(ctx context.Context) { func (ch *CosignerHealth) MarkUnhealthy(cosigner Cosigner) { ch.mu.Lock() defer ch.mu.Unlock() - ch.rtt[cosigner.GetID()] = -1 + ch.rtt[cosigner.GetIndex()] = -1 } func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *RemoteCosigner, wg *sync.WaitGroup) { @@ -72,7 +72,7 @@ func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *RemoteCosigne defer func() { ch.mu.Lock() defer ch.mu.Unlock() - ch.rtt[cosigner.GetID()] = rtt + ch.rtt[cosigner.GetIndex()] = rtt }() start := time.Now() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) @@ -80,7 +80,7 @@ func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *RemoteCosigne _, err := cosigner.client.Ping(ctx, &proto.PingRequest{}) if err != nil { - ch.logger.Error("Failed to ping", "cosigner", cosigner.GetID(), "error", err) + ch.logger.Error("Failed to ping", "cosigner", cosigner.GetIndex(), "error", err) return } rtt = time.Since(start).Nanoseconds() @@ -94,8 +94,8 @@ func (ch *CosignerHealth) GetFastest() []Cosigner { copy(fastest, ch.cosigners) sort.Slice(fastest, func(i, j int) bool { - rtt1, ok1 := ch.rtt[fastest[i].GetID()] - rtt2, ok2 := ch.rtt[fastest[j].GetID()] + rtt1, ok1 := ch.rtt[fastest[i].GetIndex()] + rtt2, ok2 := ch.rtt[fastest[j].GetIndex()] if rtt1 == -1 || !ok1 { return false } diff --git a/signer/cosigner_health_test.go b/signer/cosigner_health_test.go index 4f7398c2..67dad1f1 100644 --- a/signer/cosigner_health_test.go +++ b/signer/cosigner_health_test.go @@ -31,6 +31,6 @@ func TestCosignerHealth(t *testing.T) { require.Len(t, fastest, 4) - require.Equal(t, 4, fastest[0].GetID()) - require.Equal(t, 2, fastest[1].GetID()) + require.Equal(t, 4, fastest[0].GetIndex()) + require.Equal(t, 2, fastest[1].GetIndex()) } diff --git a/signer/cosigner_nonce_cache.go b/signer/cosigner_nonce_cache.go index 825c9ac4..78edcf42 100644 --- a/signer/cosigner_nonce_cache.go +++ b/signer/cosigner_nonce_cache.go @@ -285,7 +285,7 @@ func (cnc *CosignerNonceCache) LoadN(ctx context.Context, n int) { missedNonces.WithLabelValues(p.GetAddress()).Add(float64(1)) totalMissedNonces.WithLabelValues(p.GetAddress()).Inc() - cnc.logger.Error("Failed to get nonces from peer", "peer", p.GetID(), "error", err) + cnc.logger.Error("Failed to get nonces from peer", "peer", p.GetIndex(), "error", err) return } @@ -350,7 +350,7 @@ CheckNoncesLoop: for _, p := range fastestPeers { found := false for _, n := range cn.Nonces { - if n.Cosigner.GetID() == p.GetID() { + if n.Cosigner.GetIndex() == p.GetIndex() { found = true nonces = append(nonces, n.Nonces...) break @@ -383,7 +383,7 @@ CheckNoncesLoop: // no nonces found cosignerInts := make([]int, len(fastestPeers)) for i, p := range fastestPeers { - cosignerInts[i] = p.GetID() + cosignerInts[i] = p.GetIndex() } return nil, fmt.Errorf("no nonces found involving cosigners %+v", cosignerInts) } @@ -418,7 +418,7 @@ func (cnc *CosignerNonceCache) ClearNonces(cosigner Cosigner) { deleteID := -1 for j, n := range cn.Nonces { - if n.Cosigner.GetID() == cosigner.GetID() { + if n.Cosigner.GetIndex() == cosigner.GetIndex() { // remove cosigner from this nonce. deleteID = j break diff --git a/signer/cosigner_security.go b/signer/cosigner_security.go index dc22254a..996a52a2 100644 --- a/signer/cosigner_security.go +++ b/signer/cosigner_security.go @@ -2,7 +2,7 @@ package signer // CosignerSecurity is an interface for the security layer of the cosigner. type CosignerSecurity interface { - // GetID returns the ID of the cosigner. + // GetID returns the Index of the cosigner. GetID() int // EncryptAndSign encrypts the nonce and signs it for authentication. diff --git a/signer/cosigner_security_ecies.go b/signer/cosigner_security_ecies.go index cde12d12..c5af8857 100644 --- a/signer/cosigner_security_ecies.go +++ b/signer/cosigner_security_ecies.go @@ -130,7 +130,7 @@ func NewCosignerSecurityECIES(key CosignerECIESKey) *CosignerSecurityECIES { return c } -// GetID returns the ID of the cosigner. +// GetID returns the Index of the cosigner. func (c *CosignerSecurityECIES) GetID() int { return c.key.ID } @@ -141,10 +141,10 @@ func (c *CosignerSecurityECIES) EncryptAndSign(id int, noncePub []byte, nonceSha SourceID: c.key.ID, } - // grab the cosigner info for the ID being requested + // grab the cosigner info for the Index being requested pubKey, ok := c.eciesPubKeys[id] if !ok { - return nonce, fmt.Errorf("unknown cosigner ID: %d", id) + return nonce, fmt.Errorf("unknown cosigner Index: %d", id) } var encryptedPub []byte diff --git a/signer/cosigner_security_rsa.go b/signer/cosigner_security_rsa.go index eb46b442..a2d02675 100644 --- a/signer/cosigner_security_rsa.go +++ b/signer/cosigner_security_rsa.go @@ -122,7 +122,7 @@ func NewCosignerSecurityRSA(key CosignerRSAKey) *CosignerSecurityRSA { return c } -// GetID returns the ID of the cosigner. +// GetID returns the Index of the cosigner. func (c *CosignerSecurityRSA) GetID() int { return c.key.ID } @@ -133,10 +133,10 @@ func (c *CosignerSecurityRSA) EncryptAndSign(id int, noncePub []byte, nonceShare SourceID: c.key.ID, } - // grab the cosigner info for the ID being requested + // grab the cosigner info for the Index being requested pubKey, ok := c.rsaPubKeys[id] if !ok { - return nonce, fmt.Errorf("unknown cosigner ID: %d", id) + return nonce, fmt.Errorf("unknown cosigner Index: %d", id) } var encryptedPub []byte diff --git a/signer/leader_mock.go b/signer/leader_mock.go index aebd6f60..daef26d9 100644 --- a/signer/leader_mock.go +++ b/signer/leader_mock.go @@ -18,7 +18,7 @@ type MockLeader struct { func (m *MockLeader) IsLeader() bool { m.mu.Lock() defer m.mu.Unlock() - return m.leader != nil && m.leader.myCosigner.GetID() == m.id + return m.leader != nil && m.leader.myCosigner.GetIndex() == m.id } func (m *MockLeader) SetLeader(tv *ThresholdValidator) { diff --git a/signer/local_cosigner.go b/signer/local_cosigner.go index afa6c943..ea4eee19 100644 --- a/signer/local_cosigner.go +++ b/signer/local_cosigner.go @@ -33,7 +33,7 @@ type LocalCosigner struct { address string pendingDiskWG sync.WaitGroup - nonces map[uuid.UUID]*NoncesWithExpiration + nonces map[uuid.UUID]*types.NoncesWithExpiration // protects the nonces map noncesMu sync.RWMutex } @@ -49,7 +49,7 @@ func NewLocalCosigner( config: config, security: security, address: address, - nonces: make(map[uuid.UUID]*NoncesWithExpiration), + nonces: make(map[uuid.UUID]*types.NoncesWithExpiration), } } @@ -86,7 +86,7 @@ func (cosigner *LocalCosigner) pruneNonces() { } } -func (cosigner *LocalCosigner) combinedNonces(myID int, threshold uint8, uuid uuid.UUID) ([]Nonce, error) { +func (cosigner *LocalCosigner) combinedNonces(myID int, threshold uint8, uuid uuid.UUID) ([]types.Nonce, error) { cosigner.noncesMu.RLock() defer cosigner.noncesMu.RUnlock() @@ -95,7 +95,7 @@ func (cosigner *LocalCosigner) combinedNonces(myID int, threshold uint8, uuid uu return nil, errors.New("no metadata at HRS") } - combinedNonces := make([]Nonce, 0, threshold) + combinedNonces := make([]types.Nonce, 0, threshold) // calculate secret and public keys for _, c := range nonces.Nonces { @@ -103,7 +103,7 @@ func (cosigner *LocalCosigner) combinedNonces(myID int, threshold uint8, uuid uu continue } - combinedNonces = append(combinedNonces, Nonce{ + combinedNonces = append(combinedNonces, types.Nonce{ Share: c.Shares[myID-1], PubKey: c.PubKey, }) @@ -136,7 +136,7 @@ func (cosigner *LocalCosigner) waitForSignStatesToFlushToDisk() { // GetID returns the id of the cosigner // Implements Cosigner interface -func (cosigner *LocalCosigner) GetID() int { +func (cosigner *LocalCosigner) GetIndex() int { return cosigner.security.GetID() } @@ -176,7 +176,7 @@ func (cosigner *LocalCosigner) GetPubKey(chainID string) (cometcrypto.PubKey, er } // CombineSignatures combines partial signatures into a full signature. -func (cosigner *LocalCosigner) CombineSignatures(chainID string, signatures []PartialSignature) ([]byte, error) { +func (cosigner *LocalCosigner) CombineSignatures(chainID string, signatures []types.PartialSignature) ([]byte, error) { ccs, err := cosigner.getChainState(chainID) if err != nil { return nil, err @@ -235,7 +235,7 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon } nonces, err := cosigner.combinedNonces( - cosigner.GetID(), + cosigner.GetIndex(), uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), req.UUID, ) @@ -274,9 +274,9 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon return res, nil } -func (cosigner *LocalCosigner) generateNonces() ([]Nonces, error) { +func (cosigner *LocalCosigner) generateNonces() ([]types.Nonces, error) { total := len(cosigner.config.Config.ThresholdModeConfig.Cosigners) - meta := make([]Nonces, total) + meta := make([]types.Nonces, total) nonces, err := GenerateNonces( uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), @@ -286,7 +286,7 @@ func (cosigner *LocalCosigner) generateNonces() ([]Nonces, error) { return nil, err } - meta[cosigner.GetID()-1] = nonces + meta[cosigner.GetIndex()-1] = nonces return meta, nil } @@ -307,7 +307,7 @@ func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { var signer ThresholdSigner - signer, err = NewThresholdSignerSoft(cosigner.config, cosigner.GetID(), chainID) + signer, err = NewThresholdSignerSoft(cosigner.config, cosigner.GetIndex(), chainID) if err != nil { return err } @@ -331,7 +331,7 @@ func (cosigner *LocalCosigner) GetNonces( res := make(CosignerUUIDNoncesMultiple, len(uuids)) - id := cosigner.GetID() + id := cosigner.GetIndex() var outerEg errgroup.Group // getting nonces requires encrypting and signing for each cosigner, @@ -387,7 +387,7 @@ func (cosigner *LocalCosigner) GetNonces( return res, nil } -func (cosigner *LocalCosigner) generateNoncesIfNecessary(uuid uuid.UUID) (*NoncesWithExpiration, error) { +func (cosigner *LocalCosigner) generateNoncesIfNecessary(uuid uuid.UUID) (*types.NoncesWithExpiration, error) { // protects the meta map cosigner.noncesMu.Lock() defer cosigner.noncesMu.Unlock() @@ -401,7 +401,7 @@ func (cosigner *LocalCosigner) generateNoncesIfNecessary(uuid uuid.UUID) (*Nonce return nil, err } - res := NoncesWithExpiration{ + res := types.NoncesWithExpiration{ Nonces: newNonces, Expiration: time.Now().Add(nonceExpiration), } @@ -418,7 +418,7 @@ func (cosigner *LocalCosigner) getNonce( ) (CosignerNonce, error) { zero := CosignerNonce{} - id := cosigner.GetID() + id := cosigner.GetIndex() meta, err := cosigner.generateNoncesIfNecessary(uuid) if err != nil { @@ -464,7 +464,7 @@ func (cosigner *LocalCosigner) setNonce(uuid uuid.UUID, nonce CosignerNonce) err if n.Nonces[nonce.SourceID-1].Shares == nil { n.Nonces[nonce.SourceID-1].Shares = make([][]byte, len(cosigner.config.Config.ThresholdModeConfig.Cosigners)) } - n.Nonces[nonce.SourceID-1].Shares[cosigner.GetID()-1] = nonceShare + n.Nonces[nonce.SourceID-1].Shares[cosigner.GetIndex()-1] = nonceShare n.Nonces[nonce.SourceID-1].PubKey = noncePub return nil diff --git a/signer/local_cosigner_test.go b/signer/local_cosigner_test.go index 9711a072..0dc690a8 100644 --- a/signer/local_cosigner_test.go +++ b/signer/local_cosigner_test.go @@ -173,7 +173,7 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi err = cosigner.LoadSignStateIfNecessary(testChainID) require.NoError(t, err) - require.Equal(t, cosigner.GetID(), id) + require.Equal(t, cosigner.GetIndex(), id) if i < int(threshold) { thresholdCosigners[i] = cosigner @@ -194,7 +194,7 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi signBytes := comet.VoteSignBytes("chain-id", &vote) - sigs := make([]PartialSignature, threshold) + sigs := make([]types.PartialSignature, threshold) for i, cosigner := range thresholdCosigners { cosignerNonces := make([]CosignerNonce, 0, threshold-1) @@ -205,7 +205,7 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi } for _, n := range nonce { - if n.DestinationID == cosigner.GetID() { + if n.DestinationID == cosigner.GetIndex() { cosignerNonces = append(cosignerNonces, n) } } @@ -222,8 +222,8 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi }) require.NoError(t, err) - sigs[i] = PartialSignature{ - ID: cosigner.GetID(), + sigs[i] = types.PartialSignature{ + Index: cosigner.GetIndex(), Signature: sigRes.Signature, } } diff --git a/signer/raft_store.go b/signer/raft_store.go index cd807e5f..27e2271a 100644 --- a/signer/raft_store.go +++ b/signer/raft_store.go @@ -188,7 +188,7 @@ func (s *RaftStore) Open() (*raftgrpctransport.Manager, error) { } for _, c := range s.Cosigners { configuration.Servers = append(configuration.Servers, raft.Server{ - ID: raft.ServerID(fmt.Sprint(c.GetID())), + ID: raft.ServerID(fmt.Sprint(c.GetIndex())), // TODO: Refactor out the use of cosigner. Address: raft.ServerAddress(p2pURLToRaftAddress(c.GetAddress())), }) } @@ -261,10 +261,10 @@ func (s *RaftStore) Join(nodeID, addr string) error { } for _, srv := range configFuture.Configuration().Servers { - // If a node already exists with either the joining node's ID or address, + // If a node already exists with either the joining node's Index or address, // that node may need to be removed from the config first. if srv.ID == raft.ServerID(nodeID) || srv.Address == raft.ServerAddress(addr) { - // However if *both* the ID and the address are the same, then nothing -- not even + // However if *both* the Index and the address are the same, then nothing -- not even // a join operation -- is needed. if srv.Address == raft.ServerAddress(addr) && srv.ID == raft.ServerID(nodeID) { s.logger.Error("node already member of cluster, ignoring join request", nodeID, addr) diff --git a/signer/remote_cosigner.go b/signer/remote_cosigner.go index 0579605d..62f9df48 100644 --- a/signer/remote_cosigner.go +++ b/signer/remote_cosigner.go @@ -39,9 +39,9 @@ func NewRemoteCosigner(id int, address string) (*RemoteCosigner, error) { return cosigner, nil } -// GetID returns the ID of the remote cosigner +// GetID returns the Index of the remote cosigner // Implements the cosigner interface -func (cosigner *RemoteCosigner) GetID() int { +func (cosigner *RemoteCosigner) GetIndex() int { return cosigner.id } @@ -78,7 +78,7 @@ func getGRPCClient(address string) (proto.CosignerClient, error) { return proto.NewCosignerClient(conn), nil } -// Implements the cosigner interface +// GetNonces implements the cosigner interface func (cosigner *RemoteCosigner) GetNonces( ctx context.Context, uuids []uuid.UUID, diff --git a/signer/services.go b/signer/services.go index e6263f06..f9ca8e9e 100644 --- a/signer/services.go +++ b/signer/services.go @@ -50,7 +50,7 @@ func RequireNotRunning(log cometlog.Logger, pidFilePath string) error { } if errors.Is(err, os.ErrProcessDone) { log.Error( - "Unclean shutdown detected. PID file exists at but process with that ID cannot be found. Removing lock file", + "Unclean shutdown detected. PID file exists at but process with that Index cannot be found. Removing lock file", "pid", pid, "pid_file", pidFilePath, "error", err, diff --git a/signer/single_signer_validator_test.go b/signer/single_signer_validator_test.go index 61edcecf..81341e28 100644 --- a/signer/single_signer_validator_test.go +++ b/signer/single_signer_validator_test.go @@ -71,7 +71,7 @@ func TestSingleSignerValidator(t *testing.T) { _, _, err = validator.Sign(ctx, testChainID, types.ProposalToBlock(testChainID, &proposal)) require.NoError(t, err) - // construct different block ID for proposal at same height as highest signed + // construct different block Index for proposal at same height as highest signed randHash := cometrand.Bytes(tmhash.Size) blockID := cometproto.BlockID{Hash: randHash, PartSetHeader: cometproto.PartSetHeader{Total: 5, Hash: randHash}} @@ -93,7 +93,7 @@ func TestSingleSignerValidator(t *testing.T) { _, _, err = validator.Sign(ctx, testChainID, types.ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") - // lower LSS should sign for different chain ID + // lower LSS should sign for different chain Index _, _, err = validator.Sign(ctx, "different", types.ProposalToBlock("different", &proposal)) require.NoError(t, err) diff --git a/signer/threshold_signer.go b/signer/threshold_signer.go index f8e9ef34..80809428 100644 --- a/signer/threshold_signer.go +++ b/signer/threshold_signer.go @@ -1,6 +1,8 @@ package signer -import "time" +import ( + "github.com/strangelove-ventures/horcrux/pkg/types" +) // Interface for the local signer whether it's a soft sign or HSM type ThresholdSigner interface { @@ -8,32 +10,8 @@ type ThresholdSigner interface { PubKey() []byte // Sign signs a byte payload with the provided nonces. - Sign(nonces []Nonce, payload []byte) ([]byte, error) + Sign(nonces []types.Nonce, payload []byte) ([]byte, error) // CombineSignatures combines multiple partial signatures to a full signature. - CombineSignatures([]PartialSignature) ([]byte, error) -} - -// Nonces contains the ephemeral information generated by one cosigner for all other cosigners. -type Nonces struct { - PubKey []byte - Shares [][]byte -} - -type NoncesWithExpiration struct { - Expiration time.Time - Nonces []Nonces -} - -// Nonce is the ephemeral information from another cosigner destined for this cosigner. -type Nonce struct { - ID int - Share []byte - PubKey []byte -} - -// PartialSignature contains the signature and identifier for a piece of the combined signature. -type PartialSignature struct { - ID int - Signature []byte + CombineSignatures([]types.PartialSignature) ([]byte, error) } diff --git a/signer/threshold_signer_bls.go b/signer/threshold_signer_bls.go index 12a8b567..ff8a2065 100644 --- a/signer/threshold_signer_bls.go +++ b/signer/threshold_signer_bls.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "github.com/strangelove-ventures/horcrux/pkg/types" "gitlab.com/unit410/edwards25519" tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" @@ -30,7 +31,7 @@ func NewThresholdSignerSoftBLS(config *RuntimeConfig, id int, chainID string) (* } if key.ID != id { - return nil, fmt.Errorf("key shard ID (%d) in (%s) does not match cosigner ID (%d)", key.ID, keyFile, id) + return nil, fmt.Errorf("key shard Index (%d) in (%s) does not match cosigner Index (%d)", key.ID, keyFile, id) } s := ThresholdSignerSoftBLS{ @@ -47,7 +48,7 @@ func (s *ThresholdSignerSoftBLS) PubKey() []byte { return s.pubKey } -func (s *ThresholdSignerSoftBLS) Sign(nonces []Nonce, payload []byte) ([]byte, error) { +func (s *ThresholdSignerSoftBLS) Sign(nonces []types.Nonce, payload []byte) ([]byte, error) { nonceShare, noncePub, err := s.sumNonces(nonces) if err != nil { return nil, fmt.Errorf("failed to combine nonces: %w", err) @@ -58,7 +59,7 @@ func (s *ThresholdSignerSoftBLS) Sign(nonces []Nonce, payload []byte) ([]byte, e return append(noncePub, sig...), nil } -func (s *ThresholdSignerSoftBLS) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed25519.Element, error) { +func (s *ThresholdSignerSoftBLS) sumNonces(nonces []types.Nonce) (tsed25519.Scalar, tsed25519.Element, error) { shareParts := make([]tsed25519.Scalar, len(nonces)) publicKeys := make([]tsed25519.Element, len(nonces)) @@ -84,13 +85,13 @@ func (s *ThresholdSignerSoftBLS) sumNonces(nonces []Nonce) (tsed25519.Scalar, ts return nonceShare, noncePub, nil } -func (s *ThresholdSignerSoftBLS) CombineSignatures(signatures []PartialSignature) ([]byte, error) { +func (s *ThresholdSignerSoftBLS) CombineSignatures(signatures []types.PartialSignature) ([]byte, error) { sigIds := make([]int, len(signatures)) shareSigs := make([][]byte, len(signatures)) var ephPub []byte for i, sig := range signatures { - sigIds[i] = sig.ID + sigIds[i] = sig.Index if i == 0 { ephPub = sig.Signature[:32] } else if !bytes.Equal(sig.Signature[:32], ephPub) { diff --git a/signer/threshold_signer_soft.go b/signer/threshold_signer_soft.go index c94500cc..ade74465 100644 --- a/signer/threshold_signer_soft.go +++ b/signer/threshold_signer_soft.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "errors" "fmt" + "github.com/strangelove-ventures/horcrux/pkg/types" "gitlab.com/unit410/edwards25519" tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" @@ -31,7 +32,7 @@ func NewThresholdSignerSoft(config *RuntimeConfig, id int, chainID string) (*Thr } if key.ID != id { - return nil, fmt.Errorf("key shard ID (%d) in (%s) does not match cosigner ID (%d)", key.ID, keyFile, id) + return nil, fmt.Errorf("key shard Index (%d) in (%s) does not match cosigner Index (%d)", key.ID, keyFile, id) } s := ThresholdSignerSoft{ @@ -48,7 +49,7 @@ func (s *ThresholdSignerSoft) PubKey() []byte { return s.pubKey } -func (s *ThresholdSignerSoft) Sign(nonces []Nonce, payload []byte) ([]byte, error) { +func (s *ThresholdSignerSoft) Sign(nonces []types.Nonce, payload []byte) ([]byte, error) { nonceShare, noncePub, err := s.sumNonces(nonces) if err != nil { return nil, fmt.Errorf("failed to combine nonces: %w", err) @@ -59,7 +60,7 @@ func (s *ThresholdSignerSoft) Sign(nonces []Nonce, payload []byte) ([]byte, erro return append(noncePub, sig...), nil } -func (s *ThresholdSignerSoft) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed25519.Element, error) { +func (s *ThresholdSignerSoft) sumNonces(nonces []types.Nonce) (tsed25519.Scalar, tsed25519.Element, error) { shareParts := make([]tsed25519.Scalar, len(nonces)) publicKeys := make([]tsed25519.Element, len(nonces)) @@ -85,13 +86,13 @@ func (s *ThresholdSignerSoft) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed2 return nonceShare, noncePub, nil } -func GenerateNonces(threshold, total uint8) (Nonces, error) { +func GenerateNonces(threshold, total uint8) (types.Nonces, error) { secret := make([]byte, 32) if _, err := rand.Read(secret); err != nil { - return Nonces{}, err + return types.Nonces{}, err } - nonces := Nonces{ + nonces := types.Nonces{ PubKey: tsed25519.ScalarMultiplyBase(secret), Shares: make([][]byte, total), } @@ -105,13 +106,13 @@ func GenerateNonces(threshold, total uint8) (Nonces, error) { return nonces, nil } -func (s *ThresholdSignerSoft) CombineSignatures(signatures []PartialSignature) ([]byte, error) { +func (s *ThresholdSignerSoft) CombineSignatures(signatures []types.PartialSignature) ([]byte, error) { sigIds := make([]int, len(signatures)) shareSigs := make([][]byte, len(signatures)) var ephPub []byte for i, sig := range signatures { - sigIds[i] = sig.ID + sigIds[i] = sig.Index if i == 0 { ephPub = sig.Signature[:32] } else if !bytes.Equal(sig.Signature[:32], ephPub) { diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index 60635406..4ba23ffc 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -77,7 +77,7 @@ func NewThresholdValidator( copy(allCosigners[1:], peerCosigners) for _, cosigner := range peerCosigners { - logger.Debug("Peer cosigner", "id", cosigner.GetID()) + logger.Debug("Peer cosigner", "id", cosigner.GetIndex()) } nc := NewCosignerNonceCache( @@ -162,7 +162,8 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated( // There was an error saving the last sign state, so check if there is an existing signature for this block. existingSignature, existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(chainID, block) - if _, ok := err.(*types.SameHRSError); !ok { + var sameHRSError *types.SameHRSError + if !errors.As(err, &sameHRSError) { if sameBlockErr == nil { return existingSignature, block.Timestamp, nil } @@ -178,7 +179,8 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated( return nil, time.Time{}, nil } - if _, ok := sameBlockErr.(*StillWaitingForBlockError); !ok { + var stillWaitingForBlockError *StillWaitingForBlockError + if !errors.As(sameBlockErr, &stillWaitingForBlockError) { // we have an error other than still waiting for block. return error. return nil, existingTimestamp, fmt.Errorf( "same block error, but we are not still waiting for signature: %w", @@ -506,7 +508,7 @@ func (pv *ThresholdValidator) waitForPeerNonces( missedNonces.WithLabelValues(peer.GetAddress()).Inc() totalMissedNonces.WithLabelValues(peer.GetAddress()).Inc() - pv.logger.Error("Error getting nonces", "cosigner", peer.GetID(), "err", err) + pv.logger.Error("Error getting nonces", "cosigner", peer.GetIndex(), "err", err) return } @@ -546,7 +548,7 @@ func (pv *ThresholdValidator) proxyIfNecessary( return true, nil, stamp, fmt.Errorf("timed out waiting for raft leader") } - if leader == pv.myCosigner.GetID() { + if leader == pv.myCosigner.GetIndex() { return false, nil, time.Time{}, nil } @@ -673,7 +675,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty cosignersForThisBlockInt := make([]int, len(cosignersForThisBlock)) for i, cosigner := range cosignersForThisBlock { - cosignersForThisBlockInt[i] = cosigner.GetID() + cosignersForThisBlockInt[i] = cosigner.GetIndex() } // destination for share signatures @@ -692,18 +694,18 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty // set peerNonces and sign in single rpc call. sigRes, err := cosigner.SetNoncesAndSign(signCtx, CosignerSetNoncesAndSignRequest{ ChainID: chainID, - Nonces: nonces.For(cosigner.GetID()), + Nonces: nonces.For(cosigner.GetIndex()), HRST: hrst, SignBytes: signBytes, }) if err != nil { log.Error( "Cosigner failed to set nonces and sign", - "cosigner", cosigner.GetID(), + "cosigner", cosigner.GetIndex(), "err", err.Error(), ) - if cosigner.GetID() == pv.myCosigner.GetID() { + if cosigner.GetIndex() == pv.myCosigner.GetIndex() { return err } @@ -726,7 +728,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty if cosigner != pv.myCosigner { timedCosignerSignLag.WithLabelValues(cosigner.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) } - shareSignatures[cosigner.GetID()-1] = sigRes.Signature + shareSignatures[cosigner.GetIndex()-1] = sigRes.Signature return nil } @@ -742,7 +744,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty timedSignBlockCosignerLag.Observe(time.Since(timeStartSignBlock).Seconds()) // collect all valid responses into array of partial signatures - shareSigs := make([]PartialSignature, 0, pv.threshold) + shareSigs := make([]types.PartialSignature, 0, pv.threshold) for idx, shareSig := range shareSignatures { if len(shareSig) == 0 { continue @@ -753,8 +755,8 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty // we are ok to use the share signatures - complete boolean // prevents future concurrent access - shareSigs = append(shareSigs, PartialSignature{ - ID: idx + 1, + shareSigs = append(shareSigs, types.PartialSignature{ + Index: idx + 1, Signature: sig, }) } diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index fe07763f..95aca51d 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -54,7 +54,7 @@ func loadKeyForLocalCosigner( key := CosignerEd25519Key{ PubKey: pubKey, PrivateShard: privateShard, - ID: cosigner.GetID(), + ID: cosigner.GetIndex(), } keyBz, err := key.MarshalJSON() @@ -71,7 +71,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { thresholdCosigners := make([]Cosigner, 0, threshold-1) for i, cosigner := range cosigners { - require.Equal(t, i+1, cosigner.GetID()) + require.Equal(t, i+1, cosigner.GetIndex()) if i != 0 && len(thresholdCosigners) != int(threshold)-1 { thresholdCosigners = append(thresholdCosigners, cosigner) @@ -131,7 +131,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { _, _, err = validator.Sign(ctx, testChainID, block) require.NoError(t, err) - // construct different block ID for proposal at same height as highest signed + // construct different block Index for proposal at same height as highest signed randHash := cometrand.Bytes(tmhash.Size) blockID := cometproto.BlockID{Hash: randHash, PartSetHeader: cometproto.PartSetHeader{Total: 5, Hash: randHash}} @@ -162,7 +162,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { validator.nonceCache.LoadN(ctx, 1) - // lower LSS should sign for different chain ID + // lower LSS should sign for different chain Index _, _, err = validator.Sign(ctx, testChainID2, types.ProposalToBlock(testChainID2, &proposal)) require.NoError(t, err) @@ -381,7 +381,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) peers = append(peers, otherCosigner) } } - leaders[i] = &MockLeader{id: cosigner.GetID(), leader: leader} + leaders[i] = &MockLeader{id: cosigner.GetIndex(), leader: leader} tv := NewThresholdValidator( cometlog.NewNopLogger(), cosigner.config, @@ -430,7 +430,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) for _, l := range leaders { l.SetLeader(newLeader) } - t.Logf("New leader: %d", newLeader.myCosigner.GetID()) + t.Logf("New leader: %d", newLeader.myCosigner.GetIndex()) // time with new leader time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec diff --git a/test/docker.go b/test/docker.go index cdd8b741..943be1ce 100644 --- a/test/docker.go +++ b/test/docker.go @@ -19,7 +19,7 @@ type DockerImageBuildErrorDetail struct { } type DockerImageBuildLogAux struct { - ID string `json:"ID"` + ID string `json:"Index"` } type DockerImageBuildLog struct { @@ -64,7 +64,7 @@ func BuildHorcruxImage(ctx context.Context, client *client.Client) error { fmt.Printf(dockerLogLine.Stream) } if dockerLogLine.Aux != nil { - fmt.Printf("Image ID: %s\n", dockerLogLine.Aux.ID) + fmt.Printf("Image Index: %s\n", dockerLogLine.Aux.ID) } if dockerLogLine.Error != "" { return errors.New(dockerLogLine.Error) diff --git a/test/validator_threshold.go b/test/validator_threshold.go index 1d646cf0..9012a554 100644 --- a/test/validator_threshold.go +++ b/test/validator_threshold.go @@ -262,7 +262,7 @@ func getShardedPrivvalKey(ctx context.Context, node *cosmos.ChainNode, threshold return ed25519Shards, pvKey.PubKey, nil } -// chainEd25519Shard is a wrapper for a chain ID and a shard of an ed25519 consensus key. +// chainEd25519Shard is a wrapper for a chain Index and a shard of an ed25519 consensus key. type chainEd25519Shard struct { chainID string key signer.CosignerEd25519Key