From 03f8dabd0aea39a9fa857b8958fd7e76f7a8ceb4 Mon Sep 17 00:00:00 2001 From: r4f43l Date: Mon, 28 Aug 2023 14:03:17 +0200 Subject: [PATCH] interfaces defined where it is used. --- cmd/horcrux/cmd/threshold.go | 4 +- pkg/multiresolver/multi_test.go | 4 +- pkg/node/grpc_server.go | 31 ++++--- pkg/node/icosigner.go | 30 +++++++ pkg/node/raft_store.go | 3 +- pkg/node/threshold_validator.go | 20 +++-- pkg/node/threshold_validator_test.go | 5 +- pkg/pcosigner/cosigner.go | 23 ----- pkg/pcosigner/cosigner_key_shares.go | 4 +- pkg/pcosigner/cosigner_signer.go | 13 ++- pkg/pcosigner/cosigner_signer_soft.go | 15 ++-- pkg/pcosigner/cosigner_signer_soft_test.go | 97 ++++++++++++++++++++++ pkg/pcosigner/local_cosigner.go | 24 +++--- pkg/pcosigner/remote_cosigner.go | 23 +++-- 14 files changed, 216 insertions(+), 80 deletions(-) create mode 100644 pkg/node/icosigner.go diff --git a/cmd/horcrux/cmd/threshold.go b/cmd/horcrux/cmd/threshold.go index 9cad730b..bc973fbe 100644 --- a/cmd/horcrux/cmd/threshold.go +++ b/cmd/horcrux/cmd/threshold.go @@ -24,8 +24,8 @@ func NewThresholdValidator( } thresholdCfg := config.Config.ThresholdModeConfig - - remoteCosigners := make([]pcosigner.ICosigner, 0, len(thresholdCfg.Cosigners)-1) + // NOTE: Shouldnt this be a list of concrete type instead of interface type? + remoteCosigners := make([]node.ICosigner, 0, len(thresholdCfg.Cosigners)-1) var p2pListen string diff --git a/pkg/multiresolver/multi_test.go b/pkg/multiresolver/multi_test.go index 650e89e6..888237fe 100644 --- a/pkg/multiresolver/multi_test.go +++ b/pkg/multiresolver/multi_test.go @@ -9,8 +9,6 @@ import ( "testing" "time" - "github.com/strangelove-ventures/horcrux/pkg/pcosigner" - "github.com/strangelove-ventures/horcrux/pkg/node" grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" @@ -37,7 +35,7 @@ func createListener(nodeID string, homedir string) (string, func(), error) { nil) // Need to set pointers to avoid nil pointers. - var cosigners []pcosigner.ICosigner + var cosigners []node.ICosigner var timeDuration time.Duration thresholdvalidator := node.NewThresholdValidator(nil, nil, 0, timeDuration, 0, nil, cosigners, nil) s.SetThresholdValidator(thresholdvalidator) diff --git a/pkg/node/grpc_server.go b/pkg/node/grpc_server.go index 5c7c1f07..13db2a01 100644 --- a/pkg/node/grpc_server.go +++ b/pkg/node/grpc_server.go @@ -16,26 +16,28 @@ import ( var _ proto.ICosignerGRPCServerServer = &GRPCServer{} type GRPCServer struct { - cosigner *pcosigner.LocalCosigner - thresholdValidator *ThresholdValidator - raftStore *RaftStore - // Promoted Fields + cosigner *pcosigner.LocalCosigner // The "node's" LocalCosigner + thresholdValidator *ThresholdValidator // The "node's" ThresholdValidator + raftStore *RaftStore // The "node's" RaftStore + // Promoted Fields is embedded to have forward compatiblitity proto.UnimplementedICosignerGRPCServerServer } +// NewGRPCServer returns a new GRPCServer. func NewGRPCServer( cosigner *pcosigner.LocalCosigner, thresholdValidator *ThresholdValidator, raftStore *RaftStore, ) *GRPCServer { return &GRPCServer{ + // TODO: This is a hack to get around the fact that the cosigner is not a? cosigner: cosigner, thresholdValidator: thresholdValidator, raftStore: raftStore, } } -// SignBlock implements the CosignerGRPCServer interface. +// SignBlock "pseudo-implements" the ICosignerGRPCServer interface in pkg/proto/cosigner_grpc_server_grpc.pb.go func (rpc *GRPCServer) SignBlock( _ context.Context, req *proto.CosignerGRPCSignBlockRequest, @@ -47,6 +49,7 @@ func (rpc *GRPCServer) SignBlock( SignBytes: req.Block.GetSignBytes(), Timestamp: time.Unix(0, req.Block.GetTimestamp()), } + // this res, _, err := rpc.thresholdValidator.SignBlock(req.ChainID, block) if err != nil { return nil, err @@ -60,12 +63,13 @@ func (rpc *GRPCServer) SetNoncesAndSign( _ context.Context, req *proto.CosignerGRPCSetNoncesAndSignRequest, ) (*proto.CosignerGRPCSetNoncesAndSignResponse, error) { - res, err := rpc.cosigner.SetNoncesAndSign(pcosigner.CosignerSetNoncesAndSignRequest{ - ChainID: req.ChainID, - Nonces: pcosigner.CosignerNoncesFromProto(req.GetNonces()), - HRST: types.HRSTKeyFromProto(req.GetHrst()), - SignBytes: req.GetSignBytes(), - }) + res, err := rpc.cosigner.SetNoncesAndSign( + pcosigner.CosignerSetNoncesAndSignRequest{ + ChainID: req.ChainID, + Nonces: pcosigner.CosignerNoncesFromProto(req.GetNonces()), + HRST: types.HRSTKeyFromProto(req.GetHrst()), + SignBytes: req.GetSignBytes(), + }) if err != nil { rpc.raftStore.logger.Error( "Failed to sign with shard", @@ -91,7 +95,7 @@ func (rpc *GRPCServer) SetNoncesAndSign( }, nil } -// GetNonces implements the UnimplementedCosignerGRPCServer interface. +// GetNonces implements the ICosignerGRPCServer interface. func (rpc *GRPCServer) GetNonces( _ context.Context, req *proto.CosignerGRPCGetNoncesRequest, @@ -108,6 +112,7 @@ func (rpc *GRPCServer) GetNonces( }, nil } +// TransferLeadership pseudo-implements the ICosignerGRPCServer interface in pkg/proto/cosigner_grpc_server_grpc.pb.go func (rpc *GRPCServer) TransferLeadership( _ context.Context, req *proto.CosignerGRPCTransferLeadershipRequest, @@ -132,6 +137,8 @@ func (rpc *GRPCServer) TransferLeadership( return &proto.CosignerGRPCTransferLeadershipResponse{}, nil } +// GetLeader pseudo-implements the ICosignerGRPCServer interface in pkg/proto/cosigner_grpc_server_grpc.pb.go +// GetLeader gets the current raft cluster leader and send it as respons. func (rpc *GRPCServer) GetLeader( context.Context, *proto.CosignerGRPCGetLeaderRequest, diff --git a/pkg/node/icosigner.go b/pkg/node/icosigner.go new file mode 100644 index 00000000..105cfec5 --- /dev/null +++ b/pkg/node/icosigner.go @@ -0,0 +1,30 @@ +package node + +import ( + cometcrypto "github.com/cometbft/cometbft/crypto" + "github.com/strangelove-ventures/horcrux/pkg/pcosigner" + "github.com/strangelove-ventures/horcrux/pkg/types" +) + +// ICosigner interface is a set of methods for an m-of-n threshold signature. +// This interface abstracts the underlying key storage and management +type ICosigner interface { + // GetID should return the id number of the cosigner + // The ID is the shamir index: 1, 2, etc... + GetID() int + + // GetAddress gets the P2P URL (GRPC and Raft) + GetAddress() string + + // GetPubKey gets the combined public key (permament) + // Not used by Remote Cosigner + GetPubKey(chainID string) (cometcrypto.PubKey, error) + + VerifySignature(chainID string, payload, signature []byte) bool + + // GetNonces requests nonce frpm the peer cosigners + GetNonces(chainID string, hrst types.HRSTKey) (*pcosigner.CosignerNoncesResponse, error) + + // Sign the requested bytes + SetNoncesAndSign(req pcosigner.CosignerSetNoncesAndSignRequest) (*pcosigner.CosignerSignResponse, error) +} diff --git a/pkg/node/raft_store.go b/pkg/node/raft_store.go index b194e64f..527ec0c2 100644 --- a/pkg/node/raft_store.go +++ b/pkg/node/raft_store.go @@ -53,7 +53,6 @@ type RaftStore struct { RaftDir string RaftBind string RaftTimeout time.Duration - // Cosigners []Cosigner mu sync.Mutex m map[string]string // The key-value store for the system. @@ -61,7 +60,7 @@ type RaftStore struct { raft *raft.Raft // The consensus mechanism logger log.Logger - // cosigner *LocalCosigner + thresholdValidator *ThresholdValidator } diff --git a/pkg/node/threshold_validator.go b/pkg/node/threshold_validator.go index 9bf23ff9..274bba40 100644 --- a/pkg/node/threshold_validator.go +++ b/pkg/node/threshold_validator.go @@ -43,7 +43,7 @@ type ThresholdValidator struct { myCosigner *pcosigner.LocalCosigner // peer cosigners - peerCosigners []pcosigner.ICosigner + peerCosigners []ICosigner leader ILeader @@ -73,7 +73,7 @@ func NewThresholdValidator( grpcTimeout time.Duration, maxWaitForSameBlockAttempts int, myCosigner *pcosigner.LocalCosigner, - peerCosigners []pcosigner.ICosigner, + peerCosigners []ICosigner, leader ILeader, ) *ThresholdValidator { return &ThresholdValidator{ @@ -369,10 +369,10 @@ func newSameBlockError(chainID string, hrs types.HRSKey) *SameBlockError { func (pv *ThresholdValidator) waitForPeerNonces( chainID string, - peer pcosigner.ICosigner, + peer ICosigner, hrst types.HRSTKey, wg *sync.WaitGroup, - nonces map[pcosigner.ICosigner][]pcosigner.CosignerNonce, + nonces map[ICosigner][]pcosigner.CosignerNonce, thresholdPeersMutex *sync.Mutex, ) { peerStartTime := time.Now() @@ -398,9 +398,9 @@ func (pv *ThresholdValidator) waitForPeerNonces( } func (pv *ThresholdValidator) waitForPeerSetNoncesAndSign( chainID string, - peer pcosigner.ICosigner, + peer ICosigner, hrst types.HRSTKey, - noncesMap map[pcosigner.ICosigner][]pcosigner.CosignerNonce, + noncesMap map[ICosigner][]pcosigner.CosignerNonce, signBytes []byte, shareSignatures *[][]byte, shareSignaturesMutex *sync.Mutex, @@ -649,14 +649,18 @@ func (pv *ThresholdValidator) SignBlock(chainID string, block *Block) ([]byte, t // Used to track how close we are to threshold // Here the actual signing process starts from a cryptological perspective - nonces := make(map[pcosigner.ICosigner][]pcosigner.CosignerNonce) + nonces := make(map[ICosigner][]pcosigner.CosignerNonce) thresholdPeersMutex := sync.Mutex{} + // From each cosigner peer we are requesting the nonce. + // This is done asynchronously. + // pv.waitForPeersNonces uses GRPC to get the nonce from the peer. for _, c := range pv.peerCosigners { go pv.waitForPeerNonces(chainID, c, hrst, &getEphemeralWaitGroup, nonces, &thresholdPeersMutex) } + // Requesting a nonce from our own cosigner (a.k.a. the local cosigner) myNonces, err := pv.myCosigner.GetNonces(chainID, hrst) if err != nil { pv.notifyBlockSignError(chainID, block.HRSKey()) @@ -738,7 +742,7 @@ func (pv *ThresholdValidator) SignBlock(chainID string, block *Block) ([]byte, t return nil, stamp, errors.New("not enough co-signers") } - // assemble into final signature + // assemble the partial signatures into a valid signature signature, err := pv.myCosigner.CombineSignatures(chainID, shareSigs) if err != nil { pv.notifyBlockSignError(chainID, block.HRSKey()) diff --git a/pkg/node/threshold_validator_test.go b/pkg/node/threshold_validator_test.go index 62e928ad..8032b71b 100644 --- a/pkg/node/threshold_validator_test.go +++ b/pkg/node/threshold_validator_test.go @@ -73,7 +73,7 @@ func loadKeyForLocalCosigner( func testThresholdValidator(t *testing.T, threshold, total uint8) { cosigners, pubKey := getTestLocalCosigners(t, threshold, total) - thresholdCosigners := make([]pcosigner.ICosigner, 0, threshold-1) + thresholdCosigners := make([]ICosigner, 0, threshold-1) for i, cosigner := range cosigners { require.Equal(t, i+1, cosigner.GetID()) @@ -279,6 +279,7 @@ func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*pcosigner.L privateKey := cometcryptoed25519.GenPrivKey() privKeyBytes := privateKey[:] + // DealShares splits the secret by using Shamir Secret Sharing (Note its not verifiable secret sharing) privShards := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) tmpDir := t.TempDir() @@ -340,7 +341,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) var leader *ThresholdValidator leaders := make([]*MockLeader, total) for i, cosigner := range cosigners { - peers := make([]pcosigner.ICosigner, 0, len(cosigners)-1) + peers := make([]ICosigner, 0, len(cosigners)-1) for j, otherCosigner := range cosigners { if i != j { peers = append(peers, otherCosigner) diff --git a/pkg/pcosigner/cosigner.go b/pkg/pcosigner/cosigner.go index 1eb73c67..997c3363 100644 --- a/pkg/pcosigner/cosigner.go +++ b/pkg/pcosigner/cosigner.go @@ -5,32 +5,9 @@ import ( "github.com/strangelove-ventures/horcrux/pkg/types" - cometcrypto "github.com/cometbft/cometbft/crypto" "github.com/strangelove-ventures/horcrux/pkg/proto" ) -// ICosigner interface is a set of methods for an m-of-n threshold signature. -// This interface abstracts the underlying key storage and management -type ICosigner interface { - // Get the ID of the cosigner - // The ID is the shamir index: 1, 2, etc... - GetID() int - - // GetAddress gets the P2P URL (GRPC and Raft) - GetAddress() string - - // Get the combined public key - GetPubKey(chainID string) (cometcrypto.PubKey, error) - - VerifySignature(chainID string, payload, signature []byte) bool - - // Get nonces for all cosigner shards - GetNonces(chainID string, hrst types.HRSTKey) (*CosignerNoncesResponse, error) - - // Sign the requested bytes - SetNoncesAndSign(req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) -} - type CosignerSignBlockResponse struct { Signature []byte } diff --git a/pkg/pcosigner/cosigner_key_shares.go b/pkg/pcosigner/cosigner_key_shares.go index 6502ece4..6b817661 100644 --- a/pkg/pcosigner/cosigner_key_shares.go +++ b/pkg/pcosigner/cosigner_key_shares.go @@ -24,8 +24,10 @@ func CreateCosignerEd25519ShardsFromFile(priv string, threshold, shards uint8) ( } // CreateCosignerEd25519Shards creates CosignerEd25519Key objects from a privval.FilePVKey +// by splitting the secret using Shamir secret sharing. func CreateCosignerEd25519Shards(pv privval.FilePVKey, threshold, shards uint8) []CosignerEd25519Key { - privShards := tsed25519.DealShares(tsed25519.ExpandSecret(pv.PrivKey.Bytes()[:32]), threshold, shards) + // tsed25519.DealShares splits the secret using Shamir Secret Sharing (Note its: no verifiable secret sharing) + privShards := tsed25519.DealShares(tsed25519.ExpandSecret(pv.PrivKey.Bytes()[:32]), threshold, shards) // privshards is shamir shares out := make([]CosignerEd25519Key, shards) for i, shard := range privShards { out[i] = CosignerEd25519Key{ diff --git a/pkg/pcosigner/cosigner_signer.go b/pkg/pcosigner/cosigner_signer.go index 67d1dfec..b4730833 100644 --- a/pkg/pcosigner/cosigner_signer.go +++ b/pkg/pcosigner/cosigner_signer.go @@ -1,14 +1,15 @@ package pcosigner -// Interface for the local signer whether it's a soft sign or HSM +// IThresholdSigner is interface for the cosigner_signer whether it's a soft sign or HSM type IThresholdSigner interface { - // GetPubKey returns the public key bytes for the combination of all cosigners. + // GetPubKey returns the persistent public key GetPubKey() []byte // GenerateNonces deals nonces for all cosigners. GenerateNonces() (Nonces, error) - // Sign signs a byte payload with the provided nonces. + // Sign signs a byte payload using the a list of nonces. + // Sign returns the partial signature and an error if any. Sign(nonces []Nonce, payload []byte) ([]byte, error) // CombineSignatures combines multiple partial signatures to a full signature. @@ -17,7 +18,11 @@ type IThresholdSigner interface { // Nonces contains the ephemeral information generated by one cosigner for all other cosigners. type Nonces struct { + // PubKey is the public key for the generated nounces by cosigner PubKey []byte + + // Shares is the list nonces of size n generated by the cosigner. + // Shares[i] is the nonce for cosigner i. Shares [][]byte } @@ -28,7 +33,7 @@ type Nonce struct { PubKey []byte } -// PartialSignature contains the signature and identifier for a piece of the combined signature. +// PartialSignature contains the partial signature and identifier (shamir id) of the signer. type PartialSignature struct { ID int Signature []byte diff --git a/pkg/pcosigner/cosigner_signer_soft.go b/pkg/pcosigner/cosigner_signer_soft.go index 3faaf331..f2bef489 100644 --- a/pkg/pcosigner/cosigner_signer_soft.go +++ b/pkg/pcosigner/cosigner_signer_soft.go @@ -12,11 +12,13 @@ import ( var _ IThresholdSigner = &ThresholdSignerSoft{} +// ThresholdSignerSoft is a soft implementation of the IThresholdSigner interface. type ThresholdSignerSoft struct { - privateKeyShard []byte - pubKey []byte - threshold uint8 - total uint8 + privateKeyShard []byte // privateKeyShard is our persistent private key shard + pubKey []byte // pubKey is our persistent public key + threshold uint8 // threshold is the number of t cosigners required to sign, t + total uint8 // total is the total number n of cosigners + } func NewThresholdSignerSoft(config *RuntimeConfig, id int, chainID string) (*ThresholdSignerSoft, error) { @@ -44,6 +46,7 @@ func NewThresholdSignerSoft(config *RuntimeConfig, id int, chainID string) (*Thr return &s, nil } +// GetPubkey implements the IThresholdSigner interface func (s *ThresholdSignerSoft) GetPubKey() []byte { return s.pubKey } @@ -59,6 +62,7 @@ func (s *ThresholdSignerSoft) Sign(nonces []Nonce, payload []byte) ([]byte, erro return append(noncePub, sig...), nil } +// Note: sumNonces sums the "working secret" func (s *ThresholdSignerSoft) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed25519.Element, error) { shareParts := make([]tsed25519.Scalar, len(nonces)) publicKeys := make([]tsed25519.Element, len(nonces)) @@ -85,7 +89,7 @@ func (s *ThresholdSignerSoft) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed2 return nonceShare, noncePub, nil } -// GenerateNonces deals nonces (A Pubkey and t of n shares) for all cosigners. +// GenerateNonces deals nonces (A Pubkey and t of n shares) for all the cosigners. func (s *ThresholdSignerSoft) GenerateNonces() (Nonces, error) { secret := make([]byte, 32) if _, err := rand.Read(secret); err != nil { @@ -97,6 +101,7 @@ func (s *ThresholdSignerSoft) GenerateNonces() (Nonces, error) { Shares: make([][]byte, s.total), } + // Splits the secret with Shamir secret sharing shares := tsed25519.DealShares(secret, s.threshold, s.total) for i, s := range shares { diff --git a/pkg/pcosigner/cosigner_signer_soft_test.go b/pkg/pcosigner/cosigner_signer_soft_test.go index 3ec31387..a61b8c31 100644 --- a/pkg/pcosigner/cosigner_signer_soft_test.go +++ b/pkg/pcosigner/cosigner_signer_soft_test.go @@ -1,8 +1,18 @@ package pcosigner import ( + "crypto/ed25519" + "crypto/rand" + "encoding/hex" + "fmt" "reflect" "testing" + "time" + + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + comet "github.com/cometbft/cometbft/types" + "github.com/stretchr/testify/require" + tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" ) func TestThresholdSignerSoft_GenerateNonces(t *testing.T) { @@ -39,3 +49,90 @@ func TestThresholdSignerSoft_GenerateNonces(t *testing.T) { }) } } + +func TestSignthreshold25519(test *testing.T) { + // pack a vote into sign bytes + var vote cometproto.Vote + vote.Height = 1 + vote.Round = 0 + vote.Type = cometproto.PrevoteType + vote.Timestamp = time.Now() + + message := comet.VoteSignBytes("chain-id", &vote) + + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + require.NoError(test, err) + + // persistent_shares is the privateKey split into 3 shamir parts + persistent_shares := tsed25519.DealShares(tsed25519.ExpandSecret(privateKey.Seed()), 2, 3) + + // each player generates secret Ri + r1 := make([]byte, 32) + _, err = rand.Read(r1) + require.NoError(test, err) + + r2 := make([]byte, 32) + _, err = rand.Read(r2) + require.NoError(test, err) + + r3 := make([]byte, 32) + _, err = rand.Read(r3) + require.NoError(test, err) + + // each player split secret per t,n, Rij by Shamir secret sharing + shares1 := tsed25519.DealShares(r1, 2, 3) + shares2 := tsed25519.DealShares(r2, 2, 3) + shares3 := tsed25519.DealShares(r3, 2, 3) + + pub1 := tsed25519.ScalarMultiplyBase(r1) + pub2 := tsed25519.ScalarMultiplyBase(r2) + pub3 := tsed25519.ScalarMultiplyBase(r3) + + // B=B1+B2+...Bn + ephPublicKey := tsed25519.AddElements([]tsed25519.Element{pub1, pub2, pub3}) + + // Double check Pubkey + persistent_shares_pub1 := tsed25519.ScalarMultiplyBase(persistent_shares[0]) + persistent_shares_pub2 := tsed25519.ScalarMultiplyBase(persistent_shares[1]) + persistent_shares_pub3 := tsed25519.ScalarMultiplyBase(persistent_shares[2]) + + // A=A1+A2+...An = A=s1⋅B+s2⋅B+...sn⋅B + publicKey_2 := tsed25519.AddElements([]tsed25519.Element{persistent_shares_pub1, persistent_shares_pub2, persistent_shares_pub3}) + //require.Equal(test, publicKey, publicKey_2) + + // each player sends s(i)_{j} to corresponding other player j (i.e. s(1)_{2} to player 2) + // each player sums all s(i)_{j}, i=1 ... n, j= self id to form their working secret + s1 := tsed25519.AddScalars([]tsed25519.Scalar{shares1[0], shares2[0], shares3[0]}) + s2 := tsed25519.AddScalars([]tsed25519.Scalar{shares1[1], shares2[1], shares3[1]}) + s3 := tsed25519.AddScalars([]tsed25519.Scalar{shares1[2], shares2[2], shares3[2]}) + + _, _ = fmt.Printf("public keys: %x\n", publicKey) + _, _ = fmt.Printf("public keys: %x\n", publicKey_2) + _, err = fmt.Printf("eph pub: %x\n", ephPublicKey) + if err != nil { + panic(err) + } + //fmt.Printf("eph secret: %x\n", ephemeralPublic) + + shareSig1 := tsed25519.SignWithShare(message, persistent_shares[0], s1, publicKey, ephPublicKey) + shareSig2 := tsed25519.SignWithShare(message, persistent_shares[1], s2, publicKey, ephPublicKey) + shareSig3 := tsed25519.SignWithShare(message, persistent_shares[2], s3, publicKey, ephPublicKey) + + { + combinedSig := tsed25519.CombineShares(3, []int{1, 2, 3}, [][]byte{shareSig1, shareSig2, shareSig3}) + signature := append(ephPublicKey, combinedSig...) + fmt.Println(hex.EncodeToString(signature)) + fmt.Println(ed25519.Verify(publicKey, message, signature[:])) + + if !ed25519.Verify(publicKey, message, signature[:]) { + test.Error("Invalid Signature for signer [1,2,3]") + } + } + { + combinedSig := tsed25519.CombineShares(3, []int{1, 2}, [][]byte{shareSig1, shareSig2}) + signature := append(ephPublicKey, combinedSig...) + if !ed25519.Verify(publicKey, message, signature[:]) { + test.Error("Invalid Signature for signer [1,2]") + } + } +} diff --git a/pkg/pcosigner/local_cosigner.go b/pkg/pcosigner/local_cosigner.go index d94ede16..fa88f466 100644 --- a/pkg/pcosigner/local_cosigner.go +++ b/pkg/pcosigner/local_cosigner.go @@ -17,17 +17,16 @@ import ( "golang.org/x/sync/errgroup" ) -var _ ICosigner = &LocalCosigner{} - -// LocalCosigner responds to sign requests. -// It maintains a high watermark to avoid double-signing. -// Signing is thread safe. +// LocalCosigner "responds" to sign requests from RemoteCosigner +// - LocalCosigner maintains a high watermark to avoid double-signing. +// - Signing is thread safe. +// - LocalCosigner implements the ICosigner interface type LocalCosigner struct { logger cometlog.Logger Config *RuntimeConfig security ICosignerSecurity - chainState sync.Map - address string + chainStateMap sync.Map // chainstate is a used for map[ChainID] -> *ChainState + address string // TODO: What address are you referring to? pendingDiskWG sync.WaitGroup } @@ -45,6 +44,7 @@ func NewLocalCosigner( } } +// ChainState type ChainState struct { // lastSignState stores the last sign state for an HRS we have fully signed // incremented whenever we are asked to sign an HRS @@ -94,7 +94,7 @@ type CosignerGetNonceRequest struct { Timestamp time.Time } -// Save updates the high watermark height/round/step (HRS) if it is greater +// SaveLastSignedState updates the high watermark height/round/step (HRS) if it is greater // than the current high watermark. A mutex is used to avoid concurrent state updates. // The disk write is scheduled in a separate goroutine which will perform an atomic write. // pendingDiskWG is used upon termination in pendingDiskWG to ensure all writes have completed. @@ -134,7 +134,7 @@ func (cosigner *LocalCosigner) GetAddress() string { } func (cosigner *LocalCosigner) getChainState(chainID string) (*ChainState, error) { - cs, ok := cosigner.chainState.Load(chainID) + cs, ok := cosigner.chainStateMap.Load(chainID) if !ok { return nil, fmt.Errorf("failed to load chain state for %s", chainID) } @@ -284,7 +284,7 @@ func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { return fmt.Errorf("chain id cannot be empty") } - if _, ok := cosigner.chainState.Load(chainID); ok { + if _, ok := cosigner.chainStateMap.Load(chainID); ok { return nil } // TODO: spew.Dump(cosigner.Config) @@ -300,7 +300,7 @@ func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { return err } - cosigner.chainState.Store(chainID, &ChainState{ + cosigner.chainStateMap.Store(chainID, &ChainState{ lastSignState: signState, nonces: make(map[types.HRSTKey][]Nonces), signer: signer, @@ -309,6 +309,8 @@ func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { return nil } +// GetNonces implements the ICosigner interface. +// // GetNonces returns the nonces for the given HRS func (cosigner *LocalCosigner) GetNonces( chainID string, diff --git a/pkg/pcosigner/remote_cosigner.go b/pkg/pcosigner/remote_cosigner.go index ea60e58b..41b31159 100644 --- a/pkg/pcosigner/remote_cosigner.go +++ b/pkg/pcosigner/remote_cosigner.go @@ -1,6 +1,6 @@ package pcosigner -// RemoteCosigner is a Cosigner implementation that uses gRPC make to request to other Cosigners +// RemoteCosigner is a Cosigner implementation that uses gRPC make to request from other Cosigners import ( "context" "fmt" @@ -15,7 +15,12 @@ import ( "google.golang.org/grpc/credentials/insecure" ) -// RemoteCosigner uses CosignerGRPC to request signing from a remote cosigner +// RemoteCosigner uses CosignerGRPC to request/query a signing process from a LocalCosigner +// +// - RemoteCosigner acts as a client(!) and requests via gRPC the other +// "node's" LocalCosigner to set the nonces and sign the payload and respons. +// - RemoteCosigner --> gRPC --> LocalCosigner +// - RemoteCosigner implements the Cosigner interface type RemoteCosigner struct { id int address string @@ -58,7 +63,7 @@ func (cosigner *RemoteCosigner) GetPubKey(_ string) (cometcrypto.PubKey, error) } // VerifySignature validates a signed payload against the public key. -// Implements Cosigner interface +// Implements ICosigner interface func (cosigner *RemoteCosigner) VerifySignature(_ string, _, _ []byte) bool { return false } @@ -84,6 +89,7 @@ func (cosigner *RemoteCosigner) GetNonces( chainID string, req types.HRSTKey, ) (*CosignerNoncesResponse, error) { + client, conn, err := cosigner.getGRPCClient() if err != nil { return nil, err @@ -91,10 +97,13 @@ func (cosigner *RemoteCosigner) GetNonces( defer conn.Close() context, cancelFunc := GetContext() defer cancelFunc() - res, err := client.GetNonces(context, &proto.CosignerGRPCGetNoncesRequest{ - ChainID: chainID, - Hrst: req.ToProto(), - }) + res, err := client.GetNonces( + context, + &proto.CosignerGRPCGetNoncesRequest{ + ChainID: chainID, + Hrst: req.ToProto(), + }, + ) if err != nil { return nil, err }