diff --git a/signer/config.go b/signer/config.go index 9962856e..7db96ff1 100644 --- a/signer/config.go +++ b/signer/config.go @@ -349,7 +349,7 @@ func ChainNodesFromFlag(nodes []string) (ChainNodes, error) { func PubKey(bech32BasePrefix string, pubKey crypto.PubKey) (string, error) { if bech32BasePrefix != "" { - pubkey, err := cryptocodec.FromTmPubKeyInterface(pubKey) + pubkey, err := cryptocodec.FromCmtPubKeyInterface(pubKey) if err != nil { return "", err } @@ -366,7 +366,7 @@ func PubKey(bech32BasePrefix string, pubKey crypto.PubKey) (string, error) { var pk *cryptotypes.PubKey registry.RegisterInterface("cosmos.crypto.PubKey", pk) registry.RegisterImplementations(pk, &ed25519.PubKey{}) - sdkPK, err := cryptocodec.FromTmPubKeyInterface(pubKey) + sdkPK, err := cryptocodec.FromCmtPubKeyInterface(pubKey) if err != nil { return "", err } diff --git a/signer/threshold_signer_bls.go b/signer/threshold_signer_bls.go new file mode 100644 index 00000000..12a8b567 --- /dev/null +++ b/signer/threshold_signer_bls.go @@ -0,0 +1,104 @@ +package signer + +import ( + "bytes" + "errors" + "fmt" + + "gitlab.com/unit410/edwards25519" + tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" +) + +var _ ThresholdSigner = &ThresholdSignerSoftBLS{} + +type ThresholdSignerSoftBLS struct { + privateKeyShard []byte + pubKey []byte + threshold uint8 + total uint8 +} + +func NewThresholdSignerSoftBLS(config *RuntimeConfig, id int, chainID string) (*ThresholdSignerSoftBLS, error) { + keyFile, err := config.KeyFileExistsCosigner(chainID) + if err != nil { + return nil, err + } + + key, err := LoadCosignerEd25519Key(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading cosigner key: %s", err) + } + + if key.ID != id { + return nil, fmt.Errorf("key shard ID (%d) in (%s) does not match cosigner ID (%d)", key.ID, keyFile, id) + } + + s := ThresholdSignerSoftBLS{ + privateKeyShard: key.PrivateShard, + pubKey: key.PubKey.Bytes(), + threshold: uint8(config.Config.ThresholdModeConfig.Threshold), + total: uint8(len(config.Config.ThresholdModeConfig.Cosigners)), + } + + return &s, nil +} + +func (s *ThresholdSignerSoftBLS) PubKey() []byte { + return s.pubKey +} + +func (s *ThresholdSignerSoftBLS) Sign(nonces []Nonce, payload []byte) ([]byte, error) { + nonceShare, noncePub, err := s.sumNonces(nonces) + if err != nil { + return nil, fmt.Errorf("failed to combine nonces: %w", err) + } + + sig := tsed25519.SignWithShare( + payload, s.privateKeyShard, nonceShare, s.pubKey, noncePub) + return append(noncePub, sig...), nil +} + +func (s *ThresholdSignerSoftBLS) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed25519.Element, error) { + shareParts := make([]tsed25519.Scalar, len(nonces)) + publicKeys := make([]tsed25519.Element, len(nonces)) + + for i, n := range nonces { + shareParts[i] = n.Share + publicKeys[i] = n.PubKey + } + + nonceShare := tsed25519.AddScalars(shareParts) + noncePub := tsed25519.AddElements(publicKeys) + + // check bounds for ephemeral share to avoid passing out of bounds valids to SignWithShare + if len(nonceShare) != 32 { + return nil, nil, errors.New("ephemeral share is out of bounds") + } + + var scalarBytes [32]byte + copy(scalarBytes[:], nonceShare) + if !edwards25519.ScMinimal(&scalarBytes) { + return nil, nil, errors.New("ephemeral share is out of bounds") + } + + return nonceShare, noncePub, nil +} + +func (s *ThresholdSignerSoftBLS) CombineSignatures(signatures []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 + if i == 0 { + ephPub = sig.Signature[:32] + } else if !bytes.Equal(sig.Signature[:32], ephPub) { + return nil, fmt.Errorf("ephemeral public keys do not match") + } + shareSigs[i] = sig.Signature[32:] + } + combinedSig := tsed25519.CombineShares(s.total, sigIds, shareSigs) + + return append(ephPub, combinedSig...), nil +}