diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3cfc0f1860..72afabc8e7 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -2,7 +2,7 @@ name: e2e # Runs the CI end-to-end test network on all pushes to main or release branches # and every pull request, but only if any Go files have been changed. on: - workflow_dispatch: # allow running workflow manually + workflow_dispatch: # allow running workflow manually pull_request: push: branches: @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/setup-go@v4 with: - go-version: '1.21' + go-version: "1.21" - uses: actions/checkout@v4 - uses: technote-space/get-diff-action@v6 with: diff --git a/crypto/batch/batch.go b/crypto/batch/batch.go new file mode 100644 index 0000000000..7587bc711a --- /dev/null +++ b/crypto/batch/batch.go @@ -0,0 +1,32 @@ +package batch + +import ( + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/sr25519" +) + +// CreateBatchVerifier checks if a key type implements the batch verifier interface. +// Currently only ed25519 & sr25519 supports batch verification. +func CreateBatchVerifier(pk crypto.PubKey) (crypto.BatchVerifier, bool) { + switch pk.Type() { + case ed25519.KeyType: + return ed25519.NewBatchVerifier(), true + case sr25519.KeyType: + return sr25519.NewBatchVerifier(), true + } + + // case where the key does not support batch verification + return nil, false +} + +// SupportsBatchVerifier checks if a key type implements the batch verifier +// interface. +func SupportsBatchVerifier(pk crypto.PubKey) bool { + switch pk.Type() { + case ed25519.KeyType, sr25519.KeyType: + return true + } + + return false +} diff --git a/crypto/crypto.go b/crypto/crypto.go index e9ea762357..92d9baccb7 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -40,3 +40,15 @@ type Symmetric interface { Encrypt(plaintext []byte, secret []byte) (ciphertext []byte) Decrypt(ciphertext []byte, secret []byte) (plaintext []byte, err error) } + +// If a new key type implements batch verification, +// the key type must be registered in github.com/tendermint/tendermint/crypto/batch +type BatchVerifier interface { + // Add appends an entry into the BatchVerifier. + Add(key PubKey, message, signature []byte) error + // Verify verifies all the entries in the BatchVerifier, and returns + // if every signature in the batch is valid, and a vector of bools + // indicating the verification status of each signature (in the order + // that signatures were added to the batch). + Verify() (bool, []bool) +} diff --git a/crypto/ed25519/bench_test.go b/crypto/ed25519/bench_test.go index 6d9513fc09..114e872735 100644 --- a/crypto/ed25519/bench_test.go +++ b/crypto/ed25519/bench_test.go @@ -1,9 +1,12 @@ package ed25519 import ( + "fmt" "io" "testing" + "github.com/stretchr/testify/require" + "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/internal/benchmarking" ) @@ -24,3 +27,42 @@ func BenchmarkVerification(b *testing.B) { priv := GenPrivKey() benchmarking.BenchmarkVerification(b, priv) } + +func BenchmarkVerifyBatch(b *testing.B) { + msg := []byte("BatchVerifyTest") + + for _, sigsCount := range []int{1, 8, 64, 1024} { + sigsCount := sigsCount + b.Run(fmt.Sprintf("sig-count-%d", sigsCount), func(b *testing.B) { + // Pre-generate all of the keys, and signatures, but do not + // benchmark key-generation and signing. + pubs := make([]crypto.PubKey, 0, sigsCount) + sigs := make([][]byte, 0, sigsCount) + for i := 0; i < sigsCount; i++ { + priv := GenPrivKey() + sig, _ := priv.Sign(msg) + pubs = append(pubs, priv.PubKey().(PubKey)) + sigs = append(sigs, sig) + } + b.ResetTimer() + + b.ReportAllocs() + // NOTE: dividing by n so that metrics are per-signature + for i := 0; i < b.N/sigsCount; i++ { + // The benchmark could just benchmark the Verify() + // routine, but there is non-trivial overhead associated + // with BatchVerifier.Add(), which should be included + // in the benchmark. + v := NewBatchVerifier() + for i := 0; i < sigsCount; i++ { + err := v.Add(pubs[i], msg, sigs[i]) + require.NoError(b, err) + } + + if ok, _ := v.Verify(); !ok { + b.Fatal("signature set failed batch verification") + } + } + }) + } +} diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go index 3ab6ab57dd..97caf69b01 100644 --- a/crypto/ed25519/ed25519.go +++ b/crypto/ed25519/ed25519.go @@ -3,10 +3,12 @@ package ed25519 import ( "bytes" "crypto/subtle" + "errors" "fmt" "io" - "golang.org/x/crypto/ed25519" + "github.com/oasisprotocol/curve25519-voi/primitives/ed25519" + "github.com/oasisprotocol/curve25519-voi/primitives/ed25519/extra/cache" "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/tmhash" @@ -15,7 +17,19 @@ import ( //------------------------------------- -var _ crypto.PrivKey = PrivKey{} +var ( + _ crypto.PrivKey = PrivKey{} + _ crypto.BatchVerifier = &BatchVerifier{} + + // curve25519-voi's Ed25519 implementation supports configurable + // verification behavior, and tendermint uses the ZIP-215 verification + // semantics. + verifyOptions = &ed25519.Options{ + Verify: ed25519.VerifyOptionsZIP_215, + } + + cachingVerifier = cache.NewVerifier(cache.NewLRUCache(cacheSize)) +) const ( PrivKeyName = "tendermint/PrivKeyEd25519" @@ -32,6 +46,14 @@ const ( SeedSize = 32 KeyType = "ed25519" + + // cacheSize is the number of public keys that will be cached in + // an expanded format for repeated signature verification. + // + // TODO/perf: Either this should exclude single verification, or be + // tuned to `> validatorSize + maxTxnsPerBlock` to avoid cache + // thrashing. + cacheSize = 4096 ) func init() { @@ -105,14 +127,12 @@ func GenPrivKey() PrivKey { // genPrivKey generates a new ed25519 private key using the provided reader. func genPrivKey(rand io.Reader) PrivKey { - seed := make([]byte, SeedSize) - - _, err := io.ReadFull(rand, seed) + _, priv, err := ed25519.GenerateKey(rand) if err != nil { panic(err) } - return PrivKey(ed25519.NewKeyFromSeed(seed)) + return PrivKey(priv) } // GenPrivKeyFromSecret hashes the secret with SHA2, and uses @@ -129,7 +149,7 @@ func GenPrivKeyFromSecret(secret []byte) PrivKey { var _ crypto.PubKey = PubKey{} -// PubKeyEd25519 implements crypto.PubKey for the Ed25519 signature scheme. +// PubKey implements crypto.PubKey for the Ed25519 signature scheme. type PubKey []byte // Address is the SHA256-20 of the raw pubkey bytes. @@ -151,7 +171,7 @@ func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool { return false } - return ed25519.Verify(ed25519.PublicKey(pubKey), msg, sig) + return cachingVerifier.VerifyWithOptions(ed25519.PublicKey(pubKey), msg, sig, verifyOptions) } func (pubKey PubKey) String() string { @@ -169,3 +189,40 @@ func (pubKey PubKey) Equals(other crypto.PubKey) bool { return false } + +//------------------------------------- + +// BatchVerifier implements batch verification for ed25519. +type BatchVerifier struct { + *ed25519.BatchVerifier +} + +func NewBatchVerifier() crypto.BatchVerifier { + return &BatchVerifier{ed25519.NewBatchVerifier()} +} + +func (b *BatchVerifier) Add(key crypto.PubKey, msg, signature []byte) error { + pkEd, ok := key.(PubKey) + if !ok { + return fmt.Errorf("pubkey is not Ed25519") + } + + pkBytes := pkEd.Bytes() + + if l := len(pkBytes); l != PubKeySize { + return fmt.Errorf("pubkey size is incorrect; expected: %d, got %d", PubKeySize, l) + } + + // check that the signature is the correct length + if len(signature) != SignatureSize { + return errors.New("invalid signature") + } + + cachingVerifier.AddWithOptions(b.BatchVerifier, ed25519.PublicKey(pkBytes), msg, signature, verifyOptions) + + return nil +} + +func (b *BatchVerifier) Verify() (bool, []bool) { + return b.BatchVerifier.Verify(crypto.CReader()) +} diff --git a/crypto/ed25519/ed25519_test.go b/crypto/ed25519/ed25519_test.go index b9c18da943..6569629092 100644 --- a/crypto/ed25519/ed25519_test.go +++ b/crypto/ed25519/ed25519_test.go @@ -11,7 +11,6 @@ import ( ) func TestSignAndValidateEd25519(t *testing.T) { - privKey := ed25519.GenPrivKey() pubKey := privKey.PubKey() @@ -28,3 +27,28 @@ func TestSignAndValidateEd25519(t *testing.T) { assert.False(t, pubKey.VerifySignature(msg, sig)) } + +func TestBatchSafe(t *testing.T) { + v := ed25519.NewBatchVerifier() + + for i := 0; i <= 38; i++ { + priv := ed25519.GenPrivKey() + pub := priv.PubKey() + + var msg []byte + if i%2 == 0 { + msg = []byte("easter") + } else { + msg = []byte("egg") + } + + sig, err := priv.Sign(msg) + require.NoError(t, err) + + err = v.Add(pub, msg, sig) + require.NoError(t, err) + } + + ok, _ := v.Verify() + require.True(t, ok) +} diff --git a/crypto/sr25519/batch.go b/crypto/sr25519/batch.go new file mode 100644 index 0000000000..6c07747d68 --- /dev/null +++ b/crypto/sr25519/batch.go @@ -0,0 +1,46 @@ +package sr25519 + +import ( + "fmt" + + "github.com/oasisprotocol/curve25519-voi/primitives/sr25519" + + "github.com/cometbft/cometbft/crypto" +) + +var _ crypto.BatchVerifier = &BatchVerifier{} + +// BatchVerifier implements batch verification for sr25519. +type BatchVerifier struct { + *sr25519.BatchVerifier +} + +func NewBatchVerifier() crypto.BatchVerifier { + return &BatchVerifier{sr25519.NewBatchVerifier()} +} + +func (b *BatchVerifier) Add(key crypto.PubKey, msg, signature []byte) error { + pk, ok := key.(PubKey) + if !ok { + return fmt.Errorf("sr25519: pubkey is not sr25519") + } + + var srpk sr25519.PublicKey + if err := srpk.UnmarshalBinary(pk); err != nil { + return fmt.Errorf("sr25519: invalid public key: %w", err) + } + + var sig sr25519.Signature + if err := sig.UnmarshalBinary(signature); err != nil { + return fmt.Errorf("sr25519: unable to decode signature: %w", err) + } + + st := signingCtx.NewTranscriptBytes(msg) + b.BatchVerifier.Add(&srpk, st, &sig) + + return nil +} + +func (b *BatchVerifier) Verify() (bool, []bool) { + return b.BatchVerifier.Verify(crypto.CReader()) +} diff --git a/crypto/sr25519/bench_test.go b/crypto/sr25519/bench_test.go index 741562301e..bee3f4f247 100644 --- a/crypto/sr25519/bench_test.go +++ b/crypto/sr25519/bench_test.go @@ -1,9 +1,12 @@ package sr25519 import ( + "fmt" "io" "testing" + "github.com/stretchr/testify/require" + "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/internal/benchmarking" ) @@ -24,3 +27,42 @@ func BenchmarkVerification(b *testing.B) { priv := GenPrivKey() benchmarking.BenchmarkVerification(b, priv) } + +func BenchmarkVerifyBatch(b *testing.B) { + msg := []byte("BatchVerifyTest") + + for _, sigsCount := range []int{1, 8, 64, 1024} { + sigsCount := sigsCount + b.Run(fmt.Sprintf("sig-count-%d", sigsCount), func(b *testing.B) { + // Pre-generate all of the keys, and signatures, but do not + // benchmark key-generation and signing. + pubs := make([]crypto.PubKey, 0, sigsCount) + sigs := make([][]byte, 0, sigsCount) + for i := 0; i < sigsCount; i++ { + priv := GenPrivKey() + sig, _ := priv.Sign(msg) + pubs = append(pubs, priv.PubKey().(PubKey)) + sigs = append(sigs, sig) + } + b.ResetTimer() + + b.ReportAllocs() + // NOTE: dividing by n so that metrics are per-signature + for i := 0; i < b.N/sigsCount; i++ { + // The benchmark could just benchmark the Verify() + // routine, but there is non-trivial overhead associated + // with BatchVerifier.Add(), which should be included + // in the benchmark. + v := NewBatchVerifier() + for i := 0; i < sigsCount; i++ { + err := v.Add(pubs[i], msg, sigs[i]) + require.NoError(b, err) + } + + if ok, _ := v.Verify(); !ok { + b.Fatal("signature set failed batch verification") + } + } + }) + } +} diff --git a/crypto/sr25519/encoding.go b/crypto/sr25519/encoding.go index d88258a23b..ab4ad54ffe 100644 --- a/crypto/sr25519/encoding.go +++ b/crypto/sr25519/encoding.go @@ -1,23 +1,13 @@ package sr25519 -import ( - "github.com/cometbft/cometbft/crypto" - cmtjson "github.com/cometbft/cometbft/libs/json" -) - -var _ crypto.PrivKey = PrivKey{} +import cmtjson "github.com/cometbft/cometbft/libs/json" const ( PrivKeyName = "tendermint/PrivKeySr25519" PubKeyName = "tendermint/PubKeySr25519" - - // SignatureSize is the size of an Edwards25519 signature. Namely the size of a compressed - // Sr25519 point, and a field element. Both of which are 32 bytes. - SignatureSize = 64 ) func init() { - cmtjson.RegisterType(PubKey{}, PubKeyName) cmtjson.RegisterType(PrivKey{}, PrivKeyName) } diff --git a/crypto/sr25519/privkey.go b/crypto/sr25519/privkey.go index 98d7e3c1b2..9bea7d3eeb 100644 --- a/crypto/sr25519/privkey.go +++ b/crypto/sr25519/privkey.go @@ -1,76 +1,126 @@ package sr25519 import ( - "crypto/subtle" + "encoding/json" "fmt" "io" + "github.com/oasisprotocol/curve25519-voi/primitives/sr25519" + "github.com/cometbft/cometbft/crypto" +) + +var ( + _ crypto.PrivKey = PrivKey{} - schnorrkel "github.com/ChainSafe/go-schnorrkel" + signingCtx = sr25519.NewSigningContext([]byte{}) ) -// PrivKeySize is the number of bytes in an Sr25519 private key. -const PrivKeySize = 32 +const ( + // PrivKeySize is the number of bytes in an Sr25519 private key. + PrivKeySize = 32 -// PrivKeySr25519 implements crypto.PrivKey. -type PrivKey []byte + KeyType = "sr25519" +) + +// PrivKey implements crypto.PrivKey. +type PrivKey struct { + msk sr25519.MiniSecretKey + kp *sr25519.KeyPair +} // Bytes returns the byte representation of the PrivKey. func (privKey PrivKey) Bytes() []byte { - return []byte(privKey) + if privKey.kp == nil { + return nil + } + return privKey.msk[:] } // Sign produces a signature on the provided message. func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { - var p [PrivKeySize]byte - copy(p[:], privKey) - miniSecretKey, err := schnorrkel.NewMiniSecretKeyFromRaw(p) - if err != nil { - return []byte{}, err + if privKey.kp == nil { + return nil, fmt.Errorf("sr25519: uninitialized private key") } - secretKey := miniSecretKey.ExpandEd25519() - signingContext := schnorrkel.NewSigningContext([]byte{}, msg) + st := signingCtx.NewTranscriptBytes(msg) + + sig, err := privKey.kp.Sign(crypto.CReader(), st) + if err != nil { + return nil, fmt.Errorf("sr25519: failed to sign message: %w", err) + } - sig, err := secretKey.Sign(signingContext) + sigBytes, err := sig.MarshalBinary() if err != nil { - return []byte{}, err + return nil, fmt.Errorf("sr25519: failed to serialize signature: %w", err) } - sigBytes := sig.Encode() - return sigBytes[:], nil + return sigBytes, nil } // PubKey gets the corresponding public key from the private key. func (privKey PrivKey) PubKey() crypto.PubKey { - var p [PrivKeySize]byte - copy(p[:], privKey) - miniSecretKey, err := schnorrkel.NewMiniSecretKeyFromRaw(p) - if err != nil { - panic(fmt.Sprintf("Invalid private key: %v", err)) + if privKey.kp == nil { + panic("sr25519: uninitialized private key") } - secretKey := miniSecretKey.ExpandEd25519() - pubkey, err := secretKey.Public() + b, err := privKey.kp.PublicKey().MarshalBinary() if err != nil { - panic(fmt.Sprintf("Could not generate public key: %v", err)) + panic("sr25519: failed to serialize public key: " + err.Error()) } - key := pubkey.Encode() - return PubKey(key[:]) + + return PubKey(b) } // Equals - you probably don't need to use this. // Runs in constant time based on length of the keys. func (privKey PrivKey) Equals(other crypto.PrivKey) bool { - if otherEd, ok := other.(PrivKey); ok { - return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1 + if otherSr, ok := other.(PrivKey); ok { + return privKey.msk.Equal(&otherSr.msk) } return false } func (privKey PrivKey) Type() string { - return keyType + return KeyType +} + +func (privKey PrivKey) MarshalJSON() ([]byte, error) { + var b []byte + + // Handle uninitialized private keys gracefully. + if privKey.kp != nil { + b = privKey.Bytes() + } + + return json.Marshal(b) +} + +func (privKey *PrivKey) UnmarshalJSON(data []byte) error { + for i := range privKey.msk { + privKey.msk[i] = 0 + } + privKey.kp = nil + + var b []byte + if err := json.Unmarshal(data, &b); err != nil { + return fmt.Errorf("sr25519: failed to deserialize JSON: %w", err) + } + if len(b) == 0 { + return nil + } + + msk, err := sr25519.NewMiniSecretKeyFromBytes(b) + if err != nil { + return err + } + + sk := msk.ExpandEd25519() + + privKey.msk = *msk + privKey.kp = sk.KeyPair() + + return nil } // GenPrivKey generates a new sr25519 private key. @@ -81,19 +131,18 @@ func GenPrivKey() PrivKey { } // genPrivKey generates a new sr25519 private key using the provided reader. -func genPrivKey(rand io.Reader) PrivKey { - var seed [64]byte - - out := make([]byte, 64) - _, err := io.ReadFull(rand, out) +func genPrivKey(rng io.Reader) PrivKey { + msk, err := sr25519.GenerateMiniSecretKey(rng) if err != nil { - panic(err) + panic("sr25519: failed to generate MiniSecretKey: " + err.Error()) } - copy(seed[:], out) + sk := msk.ExpandEd25519() - key := schnorrkel.NewMiniSecretKey(seed).ExpandEd25519().Encode() - return key[:] + return PrivKey{ + msk: *msk, + kp: sk.KeyPair(), + } } // GenPrivKeyFromSecret hashes the secret with SHA2, and uses @@ -102,9 +151,14 @@ func genPrivKey(rand io.Reader) PrivKey { // if it's derived from user input. func GenPrivKeyFromSecret(secret []byte) PrivKey { seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes. - var bz [PrivKeySize]byte - copy(bz[:], seed) - privKey, _ := schnorrkel.NewMiniSecretKeyFromRaw(bz) - key := privKey.ExpandEd25519().Encode() - return key[:] + + var privKey PrivKey + if err := privKey.msk.UnmarshalBinary(seed); err != nil { + panic("sr25519: failed to deserialize MiniSecretKey: " + err.Error()) + } + + sk := privKey.msk.ExpandEd25519() + privKey.kp = sk.KeyPair() + + return privKey } diff --git a/crypto/sr25519/pubkey.go b/crypto/sr25519/pubkey.go index 1e9b791811..c569861482 100644 --- a/crypto/sr25519/pubkey.go +++ b/crypto/sr25519/pubkey.go @@ -7,22 +7,27 @@ import ( "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/tmhash" - schnorrkel "github.com/ChainSafe/go-schnorrkel" + "github.com/oasisprotocol/curve25519-voi/primitives/sr25519" ) var _ crypto.PubKey = PubKey{} -// PubKeySize is the number of bytes in an Sr25519 public key. const ( + // PubKeySize is the number of bytes in an Sr25519 public key. PubKeySize = 32 - keyType = "sr25519" + + // SignatureSize is the size of a Sr25519 signature in bytes. + SignatureSize = 64 ) -// PubKeySr25519 implements crypto.PubKey for the Sr25519 signature scheme. +// PubKey implements crypto.PubKey for the Sr25519 signature scheme. type PubKey []byte // Address is the SHA256-20 of the raw pubkey bytes. func (pubKey PubKey) Address() crypto.Address { + if len(pubKey) != PubKeySize { + panic("pubkey is incorrect size") + } return crypto.Address(tmhash.SumTruncated(pubKey[:])) } @@ -31,48 +36,35 @@ func (pubKey PubKey) Bytes() []byte { return []byte(pubKey) } -func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool { - // make sure we use the same algorithm to sign - if len(sig) != SignatureSize { - return false +// Equals - checks that two public keys are the same time +// Runs in constant time based on length of the keys. +func (pubKey PubKey) Equals(other crypto.PubKey) bool { + if otherSr, ok := other.(PubKey); ok { + return bytes.Equal(pubKey[:], otherSr[:]) } - var sig64 [SignatureSize]byte - copy(sig64[:], sig) - - publicKey := &(schnorrkel.PublicKey{}) - var p [PubKeySize]byte - copy(p[:], pubKey) - err := publicKey.Decode(p) - if err != nil { + + return false +} + +func (pubKey PubKey) VerifySignature(msg []byte, sigBytes []byte) bool { + var srpk sr25519.PublicKey + if err := srpk.UnmarshalBinary(pubKey); err != nil { return false } - signingContext := schnorrkel.NewSigningContext([]byte{}, msg) - - signature := &(schnorrkel.Signature{}) - err = signature.Decode(sig64) - if err != nil { + var sig sr25519.Signature + if err := sig.UnmarshalBinary(sigBytes); err != nil { return false } - ok, err := publicKey.Verify(signature, signingContext) - return ok && err == nil + st := signingCtx.NewTranscriptBytes(msg) + return srpk.Verify(st, &sig) } func (pubKey PubKey) String() string { return fmt.Sprintf("PubKeySr25519{%X}", []byte(pubKey)) } -// Equals - checks that two public keys are the same time -// Runs in constant time based on length of the keys. -func (pubKey PubKey) Equals(other crypto.PubKey) bool { - if otherEd, ok := other.(PubKey); ok { - return bytes.Equal(pubKey[:], otherEd[:]) - } - return false -} - func (pubKey PubKey) Type() string { - return keyType - + return KeyType } diff --git a/crypto/sr25519/sr25519_test.go b/crypto/sr25519/sr25519_test.go index 75f988861e..b2437be797 100644 --- a/crypto/sr25519/sr25519_test.go +++ b/crypto/sr25519/sr25519_test.go @@ -1,6 +1,8 @@ package sr25519_test import ( + "encoding/base64" + "encoding/json" "testing" "github.com/stretchr/testify/assert" @@ -11,7 +13,6 @@ import ( ) func TestSignAndValidateSr25519(t *testing.T) { - privKey := sr25519.GenPrivKey() pubKey := privKey.PubKey() @@ -29,3 +30,69 @@ func TestSignAndValidateSr25519(t *testing.T) { assert.False(t, pubKey.VerifySignature(msg, sig)) } + +func TestBatchSafe(t *testing.T) { + v := sr25519.NewBatchVerifier() + vFail := sr25519.NewBatchVerifier() + for i := 0; i <= 38; i++ { + priv := sr25519.GenPrivKey() + pub := priv.PubKey() + + var msg []byte + if i%2 == 0 { + msg = []byte("easter") + } else { + msg = []byte("egg") + } + + sig, err := priv.Sign(msg) + require.NoError(t, err) + + err = v.Add(pub, msg, sig) + require.NoError(t, err) + + switch i % 2 { + case 0: + err = vFail.Add(pub, msg, sig) + case 1: + msg[2] ^= byte(0x01) + err = vFail.Add(pub, msg, sig) + } + require.NoError(t, err) + } + + ok, valid := v.Verify() + require.True(t, ok, "failed batch verification") + for i, ok := range valid { + require.Truef(t, ok, "sig[%d] should be marked valid", i) + } + + ok, valid = vFail.Verify() + require.False(t, ok, "succeeded batch verification (invalid batch)") + for i, ok := range valid { + expected := (i % 2) == 0 + require.Equalf(t, expected, ok, "sig[%d] should be %v", i, expected) + } +} + +func TestJSON(t *testing.T) { + privKey := sr25519.GenPrivKey() + + t.Run("PrivKey", func(t *testing.T) { + b, err := json.Marshal(privKey) + require.NoError(t, err) + + // b should be the base64 encoded MiniSecretKey, enclosed by doublequotes. + b64 := base64.StdEncoding.EncodeToString(privKey.Bytes()) + b64 = "\"" + b64 + "\"" + require.Equal(t, []byte(b64), b) + + var privKey2 sr25519.PrivKey + err = json.Unmarshal(b, &privKey2) + require.NoError(t, err) + require.Len(t, privKey2.Bytes(), sr25519.PrivKeySize) + require.EqualValues(t, privKey.Bytes(), privKey2.Bytes()) + }) + + // PubKeys are just []byte, so there is no special handling. +} diff --git a/go.mod b/go.mod index 29c1460a34..a089ee4f53 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ module github.com/cometbft/cometbft -go 1.20 +go 1.21 + +toolchain go1.21.5 require ( github.com/BurntSushi/toml v1.2.1 - github.com/ChainSafe/go-schnorrkel v1.0.0 github.com/adlio/schema v1.3.3 github.com/fortytw2/leaktest v1.3.0 github.com/go-kit/kit v0.12.0 @@ -15,7 +16,6 @@ require ( github.com/google/orderedcode v0.0.1 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.5.0 - github.com/gtank/merlin v0.1.1 github.com/informalsystems/tm-load-test v1.3.0 github.com/lib/pq v1.10.7 github.com/libp2p/go-buffer-pool v0.1.0 @@ -49,9 +49,10 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.2.1 github.com/btcsuite/btcd/btcutil v1.1.2 github.com/cometbft/cometbft-db v0.7.0 - github.com/cosmos/gogoproto v1.4.1 + github.com/cosmos/gogoproto v1.4.2 github.com/go-git/go-git/v5 v5.11.0 github.com/golang/protobuf v1.5.3 + github.com/oasisprotocol/curve25519-voi v0.0.0-20220708102147-0a8a51822cae github.com/vektra/mockery/v2 v2.14.0 golang.org/x/sync v0.3.0 gonum.org/v1/gonum v0.8.2 @@ -93,7 +94,6 @@ require ( github.com/containerd/containerd v1.6.8 // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/containerd/typeurl v1.0.2 // indirect - github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect @@ -124,7 +124,7 @@ require ( github.com/go-critic/go-critic v0.6.5 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-toolsmith/astcast v1.0.0 // indirect github.com/go-toolsmith/astcopy v1.0.2 // indirect @@ -150,6 +150,7 @@ require ( github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect @@ -157,7 +158,6 @@ require ( github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect - github.com/gtank/ristretto255 v0.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect @@ -177,7 +177,7 @@ require ( github.com/kisielk/errcheck v1.6.2 // indirect github.com/kisielk/gotool v1.0.0 // indirect github.com/kkHAIKE/contextcheck v1.1.3 // indirect - github.com/klauspost/compress v1.15.11 // indirect + github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/kulti/thelper v0.6.3 // indirect github.com/kunwardeep/paralleltest v1.0.6 // indirect @@ -196,7 +196,6 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect github.com/mgechev/revive v1.2.4 // indirect - github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/buildkit v0.10.4 // indirect @@ -209,6 +208,7 @@ require ( github.com/nishanths/predeclared v0.2.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.4 // indirect + github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/opencontainers/runc v1.1.3 // indirect @@ -268,7 +268,7 @@ require ( github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect gitlab.com/bosi/decorder v0.2.3 // indirect - go.etcd.io/bbolt v1.3.6 // indirect + go.etcd.io/bbolt v1.3.8 // indirect go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.3 // indirect go.opentelemetry.io/otel v1.11.0 // indirect @@ -276,12 +276,13 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.23.0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect diff --git a/go.sum b/go.sum index b9559110cf..88a83ae85e 100644 --- a/go.sum +++ b/go.sum @@ -27,7 +27,9 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= +cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -55,9 +57,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= -github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts= @@ -78,6 +79,7 @@ github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -92,13 +94,16 @@ github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cv github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/ashanbrown/forbidigo v1.3.0 h1:VkYIwb/xxdireGAdJNZoo24O4lmnEWkactplBlWTShc= github.com/ashanbrown/forbidigo v1.3.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -147,6 +152,7 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -170,6 +176,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo= github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= @@ -184,10 +191,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= -github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= -github.com/cosmos/gogoproto v1.4.1 h1:WoyH+0/jbCTzpKNvyav5FL1ZTWsp1im1MxEpJEzKUB8= -github.com/cosmos/gogoproto v1.4.1/go.mod h1:Ac9lzL4vFpBMcptJROQ6dQ4M3pOEK5Z/l0Q9p+LoCr4= +github.com/cosmos/gogoproto v1.4.2 h1:UeGRcmFW41l0G0MiefWhkPEVEwvu78SZsHBvI78dAYw= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -217,6 +222,7 @@ github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0 github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU= github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c= github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= +github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= @@ -224,6 +230,7 @@ github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KP github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= +github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64= @@ -236,6 +243,7 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -246,6 +254,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= @@ -267,6 +276,7 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -274,6 +284,7 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-critic/go-critic v0.6.5 h1:fDaR/5GWURljXwF8Eh31T2GZNz9X4jeboS912mWF8Uo= @@ -283,6 +294,7 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmS github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -302,14 +314,17 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.2 h1:YnWf5Rnh1hUudj11kei53kI57quN/VH6Hp1n+erozn0= @@ -342,7 +357,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -432,8 +449,11 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -459,15 +479,11 @@ github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3 github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY= +github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= -github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= -github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -486,6 +502,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -538,8 +555,8 @@ github.com/kkHAIKE/contextcheck v1.1.3 h1:l4pNvrb8JSwRd51ojtcOxOeHJzHek+MtOyXbaR github.com/kkHAIKE/contextcheck v1.1.3/go.mod h1:PG/cwd6c0705/LM0KTr1acO2gORUxkSVWyLJOFW5qoo= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= -github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -549,6 +566,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -595,6 +613,7 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -602,9 +621,6 @@ github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwg github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/revive v1.2.4 h1:+2Hd/S8oO2H0Ikq2+egtNwQsVhAeELHjxjIUFX5ajLI= github.com/mgechev/revive v1.2.4/go.mod h1:iAWlQishqCuj4yhV24FTnKSXGpbAA+0SckXB8GQMX/Q= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= -github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -641,6 +657,8 @@ github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3L github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasisprotocol/curve25519-voi v0.0.0-20220708102147-0a8a51822cae h1:FatpGJD2jmJfhZiFDElaC0QhZUDQnxUeAwTGkfAHN3I= +github.com/oasisprotocol/curve25519-voi v0.0.0-20220708102147-0a8a51822cae/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -649,12 +667,14 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= @@ -667,6 +687,7 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= +github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= @@ -746,6 +767,7 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -879,8 +901,11 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= @@ -895,8 +920,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -916,6 +941,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= @@ -929,7 +955,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -952,8 +977,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 h1:Ic/qN6TEifvObMGQy72k0n1LlJr7DjWWEi+MOsDOiSk= golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= @@ -1050,6 +1075,7 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1110,7 +1136,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1171,7 +1196,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1379,6 +1405,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= diff --git a/p2p/conn/evil_secret_connection_test.go b/p2p/conn/evil_secret_connection_test.go index c6471249e7..6cb3a9b71d 100644 --- a/p2p/conn/evil_secret_connection_test.go +++ b/p2p/conn/evil_secret_connection_test.go @@ -7,7 +7,7 @@ import ( "testing" gogotypes "github.com/cosmos/gogoproto/types" - "github.com/gtank/merlin" + "github.com/oasisprotocol/curve25519-voi/primitives/merlin" "github.com/stretchr/testify/assert" "golang.org/x/crypto/chacha20poly1305" @@ -208,9 +208,7 @@ func (c *evilConn) signChallenge() []byte { const challengeSize = 32 var challenge [challengeSize]byte - challengeSlice := transcript.ExtractBytes(labelSecretConnectionMac, challengeSize) - - copy(challenge[:], challengeSlice[0:challengeSize]) + transcript.ExtractBytes(challenge[:], labelSecretConnectionMac) sendAead, err := chacha20poly1305.New(sendSecret[:]) if err != nil { diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index a6de1df07f..942220240b 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -14,8 +14,8 @@ import ( "time" gogotypes "github.com/cosmos/gogoproto/types" - "github.com/gtank/merlin" pool "github.com/libp2p/go-buffer-pool" + "github.com/oasisprotocol/curve25519-voi/primitives/merlin" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/hkdf" @@ -38,16 +38,16 @@ const ( aeadSizeOverhead = 16 // overhead of poly 1305 authentication tag aeadKeySize = chacha20poly1305.KeySize aeadNonceSize = chacha20poly1305.NonceSize + + labelEphemeralLowerPublicKey = "EPHEMERAL_LOWER_PUBLIC_KEY" + labelEphemeralUpperPublicKey = "EPHEMERAL_UPPER_PUBLIC_KEY" + labelDHSecret = "DH_SECRET" + labelSecretConnectionMac = "SECRET_CONNECTION_MAC" ) var ( ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer") - labelEphemeralLowerPublicKey = []byte("EPHEMERAL_LOWER_PUBLIC_KEY") - labelEphemeralUpperPublicKey = []byte("EPHEMERAL_UPPER_PUBLIC_KEY") - labelDHSecret = []byte("DH_SECRET") - labelSecretConnectionMac = []byte("SECRET_CONNECTION_MAC") - secretConnKeyAndChallengeGen = []byte("TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN") ) @@ -132,9 +132,7 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (* const challengeSize = 32 var challenge [challengeSize]byte - challengeSlice := transcript.ExtractBytes(labelSecretConnectionMac, challengeSize) - - copy(challenge[:], challengeSlice[0:challengeSize]) + transcript.ExtractBytes(challenge[:], labelSecretConnectionMac) sendAead, err := chacha20poly1305.New(sendSecret[:]) if err != nil { diff --git a/types/validation.go b/types/validation.go index f38809dbc2..338b62cd84 100644 --- a/types/validation.go +++ b/types/validation.go @@ -1,30 +1,132 @@ package types import ( + "errors" "fmt" - "time" + "github.com/cometbft/cometbft/crypto/batch" "github.com/cometbft/cometbft/crypto/tmhash" - cmttime "github.com/cometbft/cometbft/types/time" + cmtmath "github.com/cometbft/cometbft/libs/math" ) -// ValidateTime does a basic time validation ensuring time does not drift too -// much: +/- one year. -// TODO: reduce this to eg 1 day -// NOTE: DO NOT USE in ValidateBasic methods in this package. This function -// can only be used for real time validation, like on proposals and votes -// in the consensus. If consensus is stuck, and rounds increase for more than a day, -// having only a 1-day band here could break things... -// Can't use for validating blocks because we may be syncing years worth of history. -func ValidateTime(t time.Time) error { - var ( - now = cmttime.Now() - oneYear = 8766 * time.Hour - ) - if t.Before(now.Add(-oneYear)) || t.After(now.Add(oneYear)) { - return fmt.Errorf("time drifted too much. Expected: -1 < %v < 1 year", now) +const batchVerifyThreshold = 2 + +func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool { + return len(commit.Signatures) >= batchVerifyThreshold && batch.SupportsBatchVerifier(vals.GetProposer().PubKey) +} + +// VerifyCommit verifies +2/3 of the set had signed the given commit. +// +// It checks all the signatures! While it's safe to exit as soon as we have +// 2/3+ signatures, doing so would impact incentivization logic in the ABCI +// application that depends on the LastCommitInfo sent in BeginBlock, which +// includes which validators signed. For instance, Gaia incentivizes proposers +// with a bonus for including more than +2/3 of the signatures. +func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID, + height int64, commit *Commit) error { + // run a basic validation of the arguments + if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil { + return err } - return nil + + // calculate voting power needed. Note that total voting power is capped to + // 1/8th of max int64 so this operation should never overflow + votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 + + // ignore all absent signatures + ignore := func(c CommitSig) bool { return c.Absent() } + + // only count the signatures that are for the block + count := func(c CommitSig) bool { return c.ForBlock() } + + // attempt to batch verify + if shouldBatchVerify(vals, commit) { + return verifyCommitBatch(chainID, vals, commit, + votingPowerNeeded, ignore, count, true, true) + } + + // if verification failed or is not supported then fallback to single verification + return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded, + ignore, count, true, true) +} + +// LIGHT CLIENT VERIFICATION METHODS + +// VerifyCommitLight verifies +2/3 of the set had signed the given commit. +// +// This method is primarily used by the light client and does not check all the +// signatures. +func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID, + height int64, commit *Commit) error { + // run a basic validation of the arguments + if err := verifyBasicValsAndCommit(vals, commit, height, blockID); err != nil { + return err + } + + // calculate voting power needed + votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 + + // ignore all commit signatures that are not for the block + ignore := func(c CommitSig) bool { return !c.ForBlock() } + + // count all the remaining signatures + count := func(c CommitSig) bool { return true } + + // attempt to batch verify + if shouldBatchVerify(vals, commit) { + return verifyCommitBatch(chainID, vals, commit, + votingPowerNeeded, ignore, count, false, true) + } + + // if verification failed or is not supported then fallback to single verification + return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded, + ignore, count, false, true) +} + +// VerifyCommitLightTrusting verifies that trustLevel of the validator set signed +// this commit. +// +// NOTE the given validators do not necessarily correspond to the validator set +// for this commit, but there may be some intersection. +// +// This method is primarily used by the light client and does not check all the +// signatures. +func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commit, trustLevel cmtmath.Fraction) error { + // sanity checks + if vals == nil { + return errors.New("nil validator set") + } + if trustLevel.Denominator == 0 { + return errors.New("trustLevel has zero Denominator") + } + if commit == nil { + return errors.New("nil commit") + } + + // safely calculate voting power needed. + totalVotingPowerMulByNumerator, overflow := safeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator)) + if overflow { + return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator") + } + votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator) + + // ignore all commit signatures that are not for the block + ignore := func(c CommitSig) bool { return !c.ForBlock() } + + // count all the remaining signatures + count := func(c CommitSig) bool { return true } + + // attempt to batch verify commit. As the validator set doesn't necessarily + // correspond with the validator set that signed the block we need to look + // up by address rather than index. + if shouldBatchVerify(vals, commit) { + return verifyCommitBatch(chainID, vals, commit, + votingPowerNeeded, ignore, count, false, false) + } + + // attempt with single verification + return verifyCommitSingle(chainID, vals, commit, votingPowerNeeded, + ignore, count, false, false) } // ValidateHash returns an error if the hash is not empty, but its @@ -38,3 +140,218 @@ func ValidateHash(h []byte) error { } return nil } + +// Batch verification + +// verifyCommitBatch batch verifies commits. This routine is equivalent +// to verifyCommitSingle in behavior, just faster iff every signature in the +// batch is valid. +// +// Note: The caller is responsible for checking to see if this routine is +// usable via `shouldVerifyBatch(vals, commit)`. +func verifyCommitBatch( + chainID string, + vals *ValidatorSet, + commit *Commit, + votingPowerNeeded int64, + ignoreSig func(CommitSig) bool, + countSig func(CommitSig) bool, + countAllSignatures bool, + lookUpByIndex bool, +) error { + var ( + val *Validator + valIdx int32 + seenVals = make(map[int32]int, len(commit.Signatures)) + batchSigIdxs = make([]int, 0, len(commit.Signatures)) + talliedVotingPower int64 + ) + // attempt to create a batch verifier + bv, ok := batch.CreateBatchVerifier(vals.GetProposer().PubKey) + // re-check if batch verification is supported + if !ok || len(commit.Signatures) < batchVerifyThreshold { + // This should *NEVER* happen. + return fmt.Errorf("unsupported signature algorithm or insufficient signatures for batch verification") + } + + for idx, commitSig := range commit.Signatures { + // skip over signatures that should be ignored + if ignoreSig(commitSig) { + continue + } + + // If the vals and commit have a 1-to-1 correspondance we can retrieve + // them by index else we need to retrieve them by address + if lookUpByIndex { + val = vals.Validators[idx] + } else { + valIdx, val = vals.GetByAddress(commitSig.ValidatorAddress) + + // if the signature doesn't belong to anyone in the validator set + // then we just skip over it + if val == nil { + continue + } + + // because we are getting validators by address we need to make sure + // that the same validator doesn't commit twice + if firstIndex, ok := seenVals[valIdx]; ok { + secondIndex := idx + return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex) + } + seenVals[valIdx] = idx + } + + // Validate signature. + voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) + + // add the key, sig and message to the verifier + if err := bv.Add(val.PubKey, voteSignBytes, commitSig.Signature); err != nil { + return err + } + batchSigIdxs = append(batchSigIdxs, idx) + + // If this signature counts then add the voting power of the validator + // to the tally + if countSig(commitSig) { + talliedVotingPower += val.VotingPower + } + + // if we don't need to verify all signatures and already have sufficient + // voting power we can break from batching and verify all the signatures + if !countAllSignatures && talliedVotingPower > votingPowerNeeded { + break + } + } + + // ensure that we have batched together enough signatures to exceed the + // voting power needed else there is no need to even verify + if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { + return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} + } + + // attempt to verify the batch. + ok, validSigs := bv.Verify() + if ok { + // success + return nil + } + + // one or more of the signatures is invalid, find and return the first + // invalid signature. + for i, ok := range validSigs { + if !ok { + // go back from the batch index to the commit.Signatures index + idx := batchSigIdxs[i] + sig := commit.Signatures[idx] + return fmt.Errorf("wrong signature (#%d): %X", idx, sig) + } + } + + // execution reaching here is a bug, and one of the following has + // happened: + // * non-zero tallied voting power, empty batch (impossible?) + // * bv.Verify() returned `false, []bool{true, ..., true}` (BUG) + return fmt.Errorf("BUG: batch verification failed with no invalid signatures") +} + +// Single Verification + +// verifyCommitSingle single verifies commits. +// If a key does not support batch verification, or batch verification fails this will be used +// This method is used to check all the signatures included in a commit. +// It is used in consensus for validating a block LastCommit. +// CONTRACT: both commit and validator set should have passed validate basic +func verifyCommitSingle( + chainID string, + vals *ValidatorSet, + commit *Commit, + votingPowerNeeded int64, + ignoreSig func(CommitSig) bool, + countSig func(CommitSig) bool, + countAllSignatures bool, + lookUpByIndex bool, +) error { + var ( + val *Validator + valIdx int32 + seenVals = make(map[int32]int, len(commit.Signatures)) + talliedVotingPower int64 + voteSignBytes []byte + ) + for idx, commitSig := range commit.Signatures { + if ignoreSig(commitSig) { + continue + } + + // If the vals and commit have a 1-to-1 correspondance we can retrieve + // them by index else we need to retrieve them by address + if lookUpByIndex { + val = vals.Validators[idx] + } else { + valIdx, val = vals.GetByAddress(commitSig.ValidatorAddress) + + // if the signature doesn't belong to anyone in the validator set + // then we just skip over it + if val == nil { + continue + } + + // because we are getting validators by address we need to make sure + // that the same validator doesn't commit twice + if firstIndex, ok := seenVals[valIdx]; ok { + secondIndex := idx + return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex) + } + seenVals[valIdx] = idx + } + + voteSignBytes = commit.VoteSignBytes(chainID, int32(idx)) + + if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + + // If this signature counts then add the voting power of the validator + // to the tally + if countSig(commitSig) { + talliedVotingPower += val.VotingPower + } + + // check if we have enough signatures and can thus exit early + if !countAllSignatures && talliedVotingPower > votingPowerNeeded { + return nil + } + } + + if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { + return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} + } + + return nil +} + +func verifyBasicValsAndCommit(vals *ValidatorSet, commit *Commit, height int64, blockID BlockID) error { + if vals == nil { + return errors.New("nil validator set") + } + + if commit == nil { + return errors.New("nil commit") + } + + if vals.Size() != len(commit.Signatures) { + return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures)) + } + + // Validate Height and BlockID. + if height != commit.Height { + return NewErrInvalidCommitHeight(height, commit.Height) + } + if !blockID.Equals(commit.BlockID) { + return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v", + blockID, commit.BlockID) + } + + return nil +} diff --git a/types/validation_test.go b/types/validation_test.go new file mode 100644 index 0000000000..01854cb8da --- /dev/null +++ b/types/validation_test.go @@ -0,0 +1,261 @@ +package types + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + tmmath "github.com/cometbft/cometbft/libs/math" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" +) + +// Check VerifyCommit, VerifyCommitLight and VerifyCommitLightTrusting basic +// verification. +func TestValidatorSet_VerifyCommit_All(t *testing.T) { + var ( + round = int32(0) + height = int64(100) + + blockID = makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) + chainID = "Lalande21185" + trustLevel = tmmath.Fraction{Numerator: 2, Denominator: 3} + ) + + testCases := []struct { + description string + // vote chainID + chainID string + // vote blockID + blockID BlockID + valSize int + + // height of the commit + height int64 + + // votes + blockVotes int + nilVotes int + absentVotes int + + expErr bool + }{ + {"good (batch verification)", chainID, blockID, 3, height, 3, 0, 0, false}, + {"good (single verification)", chainID, blockID, 1, height, 1, 0, 0, false}, + + {"wrong signature (#0)", "EpsilonEridani", blockID, 2, height, 2, 0, 0, true}, + {"wrong block ID", chainID, makeBlockIDRandom(), 2, height, 2, 0, 0, true}, + {"wrong height", chainID, blockID, 1, height - 1, 1, 0, 0, true}, + + {"wrong set size: 4 vs 3", chainID, blockID, 4, height, 3, 0, 0, true}, + {"wrong set size: 1 vs 2", chainID, blockID, 1, height, 2, 0, 0, true}, + + {"insufficient voting power: got 30, needed more than 66", chainID, blockID, 10, height, 3, 2, 5, true}, + {"insufficient voting power: got 0, needed more than 6", chainID, blockID, 1, height, 0, 0, 1, true}, + {"insufficient voting power: got 60, needed more than 60", chainID, blockID, 9, height, 6, 3, 0, true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.description, func(t *testing.T) { + _, valSet, vals := randVoteSet(tc.height, round, tmproto.PrecommitType, tc.valSize, 10) + totalVotes := tc.blockVotes + tc.absentVotes + tc.nilVotes + sigs := make([]CommitSig, totalVotes) + vi := 0 + // add absent sigs first + for i := 0; i < tc.absentVotes; i++ { + sigs[vi] = NewCommitSigAbsent() + vi++ + } + for i := 0; i < tc.blockVotes+tc.nilVotes; i++ { + + pubKey, err := vals[vi%len(vals)].GetPubKey() + require.NoError(t, err) + vote := &Vote{ + ValidatorAddress: pubKey.Address(), + ValidatorIndex: int32(vi), + Height: tc.height, + Round: round, + Type: tmproto.PrecommitType, + BlockID: tc.blockID, + Timestamp: time.Now(), + } + if i >= tc.blockVotes { + vote.BlockID = BlockID{} + } + + v := vote.ToProto() + + require.NoError(t, vals[vi%len(vals)].SignVote(tc.chainID, v)) + vote.Signature = v.Signature + + sigs[vi] = vote.CommitSig() + + vi++ + } + commit := NewCommit(tc.height, round, tc.blockID, sigs) + + err := valSet.VerifyCommit(chainID, blockID, height, commit) + if tc.expErr { + if assert.Error(t, err, "VerifyCommit") { + assert.Contains(t, err.Error(), tc.description, "VerifyCommit") + } + } else { + assert.NoError(t, err, "VerifyCommit") + } + + err = valSet.VerifyCommitLight(chainID, blockID, height, commit) + if tc.expErr { + if assert.Error(t, err, "VerifyCommitLight") { + assert.Contains(t, err.Error(), tc.description, "VerifyCommitLight") + } + } else { + assert.NoError(t, err, "VerifyCommitLight") + } + + // only a subsection of the tests apply to VerifyCommitLightTrusting + if totalVotes != tc.valSize || !tc.blockID.Equals(blockID) || tc.height != height { + tc.expErr = false + } + err = valSet.VerifyCommitLightTrusting(chainID, commit, trustLevel) + if tc.expErr { + if assert.Error(t, err, "VerifyCommitLightTrusting") { + assert.Contains(t, err.Error(), tc.description, "VerifyCommitLightTrusting") + } + } else { + assert.NoError(t, err, "VerifyCommitLightTrusting") + } + }) + } +} + +func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) { + var ( + chainID = "test_chain_id" + h = int64(3) + blockID = makeBlockIDRandom() + ) + + voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) + commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) + require.NoError(t, err) + require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit)) + + // malleate 4th signature + vote := voteSet.GetByIndex(3) + v := vote.ToProto() + err = vals[3].SignVote("CentaurusA", v) + require.NoError(t, err) + vote.Signature = v.Signature + commit.Signatures[3] = vote.CommitSig() + + err = valSet.VerifyCommit(chainID, blockID, h, commit) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "wrong signature (#3)") + } +} + +func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSigned(t *testing.T) { + var ( + chainID = "test_chain_id" + h = int64(3) + blockID = makeBlockIDRandom() + ) + + voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) + commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) + require.NoError(t, err) + require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit)) + + // malleate 4th signature (3 signatures are enough for 2/3+) + vote := voteSet.GetByIndex(3) + v := vote.ToProto() + err = vals[3].SignVote("CentaurusA", v) + require.NoError(t, err) + vote.Signature = v.Signature + commit.Signatures[3] = vote.CommitSig() + + err = valSet.VerifyCommitLight(chainID, blockID, h, commit) + assert.NoError(t, err) +} + +func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) { + var ( + chainID = "test_chain_id" + h = int64(3) + blockID = makeBlockIDRandom() + ) + + voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) + commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) + require.NoError(t, err) + require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit)) + + // malleate 3rd signature (2 signatures are enough for 1/3+ trust level) + vote := voteSet.GetByIndex(2) + v := vote.ToProto() + err = vals[2].SignVote("CentaurusA", v) + require.NoError(t, err) + vote.Signature = v.Signature + commit.Signatures[2] = vote.CommitSig() + + err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3}) + assert.NoError(t, err) +} + +func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) { + var ( + blockID = makeBlockIDRandom() + voteSet, originalValset, vals = randVoteSet(1, 1, tmproto.PrecommitType, 6, 1) + commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) + newValSet, _ = RandValidatorSet(2, 1) + ) + require.NoError(t, err) + + testCases := []struct { + valSet *ValidatorSet + err bool + }{ + // good + 0: { + valSet: originalValset, + err: false, + }, + // bad - no overlap between validator sets + 1: { + valSet: newValSet, + err: true, + }, + // good - first two are different but the rest of the same -> >1/3 + 2: { + valSet: NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)), + err: false, + }, + } + + for _, tc := range testCases { + err = tc.valSet.VerifyCommitLightTrusting("test_chain_id", commit, + tmmath.Fraction{Numerator: 1, Denominator: 3}) + if tc.err { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + } +} + +func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) { + var ( + blockID = makeBlockIDRandom() + voteSet, valSet, vals = randVoteSet(1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower) + commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) + ) + require.NoError(t, err) + + err = valSet.VerifyCommitLightTrusting("test_chain_id", commit, + tmmath.Fraction{Numerator: 25, Denominator: 55}) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "int64 overflow") + } +} diff --git a/types/validator_set.go b/types/validator_set.go index 019834ac1d..4b509d605a 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -657,172 +657,25 @@ func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error { return vals.updateWithChangeSet(changes, true) } -// VerifyCommit verifies +2/3 of the set had signed the given commit. -// -// It checks all the signatures! While it's safe to exit as soon as we have -// 2/3+ signatures, doing so would impact incentivization logic in the ABCI -// application that depends on the LastCommitInfo sent in BeginBlock, which -// includes which validators signed. For instance, Gaia incentivizes proposers -// with a bonus for including more than +2/3 of the signatures. +// VerifyCommit verifies +2/3 of the set had signed the given commit and all +// other signatures are valid func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height int64, commit *Commit) error { - - if vals.Size() != len(commit.Signatures) { - return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures)) - } - - // Validate Height and BlockID. - if height != commit.Height { - return NewErrInvalidCommitHeight(height, commit.Height) - } - if !blockID.Equals(commit.BlockID) { - return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v", - blockID, commit.BlockID) - } - - talliedVotingPower := int64(0) - votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 - for idx, commitSig := range commit.Signatures { - if commitSig.Absent() { - continue // OK, some signatures can be absent. - } - - // The vals and commit have a 1-to-1 correspondance. - // This means we don't need the validator address or to do any lookup. - val := vals.Validators[idx] - - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) - if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - // Good! - if commitSig.ForBlock() { - talliedVotingPower += val.VotingPower - } - // else { - // It's OK. We include stray signatures (~votes for nil) to measure - // validator availability. - // } - } - - if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { - return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} - } - - return nil + return VerifyCommit(chainID, vals, blockID, height, commit) } // LIGHT CLIENT VERIFICATION METHODS // VerifyCommitLight verifies +2/3 of the set had signed the given commit. -// -// This method is primarily used by the light client and does not check all the -// signatures. func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID, height int64, commit *Commit) error { - - if vals.Size() != len(commit.Signatures) { - return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures)) - } - - // Validate Height and BlockID. - if height != commit.Height { - return NewErrInvalidCommitHeight(height, commit.Height) - } - if !blockID.Equals(commit.BlockID) { - return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v", - blockID, commit.BlockID) - } - - talliedVotingPower := int64(0) - votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 - for idx, commitSig := range commit.Signatures { - // No need to verify absent or nil votes. - if !commitSig.ForBlock() { - continue - } - - // The vals and commit have a 1-to-1 correspondance. - // This means we don't need the validator address or to do any lookup. - val := vals.Validators[idx] - - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) - if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - - talliedVotingPower += val.VotingPower - - // return as soon as +2/3 of the signatures are verified - if talliedVotingPower > votingPowerNeeded { - return nil - } - } - - return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} + return VerifyCommitLight(chainID, vals, blockID, height, commit) } // VerifyCommitLightTrusting verifies that trustLevel of the validator set signed // this commit. -// -// NOTE the given validators do not necessarily correspond to the validator set -// for this commit, but there may be some intersection. -// -// This method is primarily used by the light client and does not check all the -// signatures. func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *Commit, trustLevel cmtmath.Fraction) error { - // sanity check - if trustLevel.Denominator == 0 { - return errors.New("trustLevel has zero Denominator") - } - - var ( - talliedVotingPower int64 - seenVals = make(map[int32]int, len(commit.Signatures)) // validator index -> commit index - ) - - // Safely calculate voting power needed. - totalVotingPowerMulByNumerator, overflow := safeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator)) - if overflow { - return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator") - } - votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator) - - for idx, commitSig := range commit.Signatures { - // No need to verify absent or nil votes. - if !commitSig.ForBlock() { - continue - } - - // We don't know the validators that committed this block, so we have to - // check for each vote if its validator is already known. - valIdx, val := vals.GetByAddress(commitSig.ValidatorAddress) - - if val != nil { - // check for double vote of validator on the same commit - if firstIndex, ok := seenVals[valIdx]; ok { - secondIndex := idx - return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex) - } - seenVals[valIdx] = idx - - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) - if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - - talliedVotingPower += val.VotingPower - - if talliedVotingPower > votingPowerNeeded { - return nil - } - } - } - - return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} + return VerifyCommitLightTrusting(chainID, vals, commit, trustLevel) } // findPreviousProposer reverses the compare proposer priority function to find the validator diff --git a/types/validator_set_test.go b/types/validator_set_test.go index d2e734ff38..11f1ab2483 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -8,7 +8,6 @@ import ( "strings" "testing" "testing/quick" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -665,155 +664,6 @@ func TestSafeSubClip(t *testing.T) { //------------------------------------------------------------------- -// Check VerifyCommit, VerifyCommitLight and VerifyCommitLightTrusting basic -// verification. -func TestValidatorSet_VerifyCommit_All(t *testing.T) { - var ( - privKey = ed25519.GenPrivKey() - pubKey = privKey.PubKey() - v1 = NewValidator(pubKey, 1000) - vset = NewValidatorSet([]*Validator{v1}) - - chainID = "Lalande21185" - ) - - vote := examplePrecommit() - vote.ValidatorAddress = pubKey.Address() - v := vote.ToProto() - sig, err := privKey.Sign(VoteSignBytes(chainID, v)) - require.NoError(t, err) - vote.Signature = sig - - commit := NewCommit(vote.Height, vote.Round, vote.BlockID, []CommitSig{vote.CommitSig()}) - - vote2 := *vote - sig2, err := privKey.Sign(VoteSignBytes("EpsilonEridani", v)) - require.NoError(t, err) - vote2.Signature = sig2 - - testCases := []struct { - description string - chainID string - blockID BlockID - height int64 - commit *Commit - expErr bool - }{ - {"good", chainID, vote.BlockID, vote.Height, commit, false}, - - {"wrong signature (#0)", "EpsilonEridani", vote.BlockID, vote.Height, commit, true}, - {"wrong block ID", chainID, makeBlockIDRandom(), vote.Height, commit, true}, - {"wrong height", chainID, vote.BlockID, vote.Height - 1, commit, true}, - - {"wrong set size: 1 vs 0", chainID, vote.BlockID, vote.Height, - NewCommit(vote.Height, vote.Round, vote.BlockID, []CommitSig{}), true}, - - {"wrong set size: 1 vs 2", chainID, vote.BlockID, vote.Height, - NewCommit(vote.Height, vote.Round, vote.BlockID, - []CommitSig{vote.CommitSig(), {BlockIDFlag: BlockIDFlagAbsent}}), true}, - - {"insufficient voting power: got 0, needed more than 666", chainID, vote.BlockID, vote.Height, - NewCommit(vote.Height, vote.Round, vote.BlockID, []CommitSig{{BlockIDFlag: BlockIDFlagAbsent}}), true}, - - {"wrong signature (#0)", chainID, vote.BlockID, vote.Height, - NewCommit(vote.Height, vote.Round, vote.BlockID, []CommitSig{vote2.CommitSig()}), true}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.description, func(t *testing.T) { - err := vset.VerifyCommit(tc.chainID, tc.blockID, tc.height, tc.commit) - if tc.expErr { - if assert.Error(t, err, "VerifyCommit") { - assert.Contains(t, err.Error(), tc.description, "VerifyCommit") - } - } else { - assert.NoError(t, err, "VerifyCommit") - } - - err = vset.VerifyCommitLight(tc.chainID, tc.blockID, tc.height, tc.commit) - if tc.expErr { - if assert.Error(t, err, "VerifyCommitLight") { - assert.Contains(t, err.Error(), tc.description, "VerifyCommitLight") - } - } else { - assert.NoError(t, err, "VerifyCommitLight") - } - }) - } -} - -func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) { - var ( - chainID = "test_chain_id" - h = int64(3) - blockID = makeBlockIDRandom() - ) - - voteSet, valSet, vals := randVoteSet(h, 0, cmtproto.PrecommitType, 4, 10) - commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) - require.NoError(t, err) - - // malleate 4th signature - vote := voteSet.GetByIndex(3) - v := vote.ToProto() - err = vals[3].SignVote("CentaurusA", v) - require.NoError(t, err) - vote.Signature = v.Signature - commit.Signatures[3] = vote.CommitSig() - - err = valSet.VerifyCommit(chainID, blockID, h, commit) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "wrong signature (#3)") - } -} - -func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSigned(t *testing.T) { - var ( - chainID = "test_chain_id" - h = int64(3) - blockID = makeBlockIDRandom() - ) - - voteSet, valSet, vals := randVoteSet(h, 0, cmtproto.PrecommitType, 4, 10) - commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) - require.NoError(t, err) - - // malleate 4th signature (3 signatures are enough for 2/3+) - vote := voteSet.GetByIndex(3) - v := vote.ToProto() - err = vals[3].SignVote("CentaurusA", v) - require.NoError(t, err) - vote.Signature = v.Signature - commit.Signatures[3] = vote.CommitSig() - - err = valSet.VerifyCommitLight(chainID, blockID, h, commit) - assert.NoError(t, err) -} - -func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) { - var ( - chainID = "test_chain_id" - h = int64(3) - blockID = makeBlockIDRandom() - ) - - voteSet, valSet, vals := randVoteSet(h, 0, cmtproto.PrecommitType, 4, 10) - commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) - require.NoError(t, err) - - // malleate 3rd signature (2 signatures are enough for 1/3+ trust level) - vote := voteSet.GetByIndex(2) - v := vote.ToProto() - err = vals[2].SignVote("CentaurusA", v) - require.NoError(t, err) - vote.Signature = v.Signature - commit.Signatures[2] = vote.CommitSig() - - err = valSet.VerifyCommitLightTrusting(chainID, commit, cmtmath.Fraction{Numerator: 1, Denominator: 3}) - assert.NoError(t, err) -} - func TestEmptySet(t *testing.T) { var valList []*Validator @@ -1517,62 +1367,6 @@ func TestValSetUpdateOverflowRelated(t *testing.T) { } } -func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) { - var ( - blockID = makeBlockIDRandom() - voteSet, originalValset, vals = randVoteSet(1, 1, cmtproto.PrecommitType, 6, 1) - commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) - newValSet, _ = RandValidatorSet(2, 1) - ) - require.NoError(t, err) - - testCases := []struct { - valSet *ValidatorSet - err bool - }{ - // good - 0: { - valSet: originalValset, - err: false, - }, - // bad - no overlap between validator sets - 1: { - valSet: newValSet, - err: true, - }, - // good - first two are different but the rest of the same -> >1/3 - 2: { - valSet: NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)), - err: false, - }, - } - - for _, tc := range testCases { - err = tc.valSet.VerifyCommitLightTrusting("test_chain_id", commit, - cmtmath.Fraction{Numerator: 1, Denominator: 3}) - if tc.err { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - } -} - -func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) { - var ( - blockID = makeBlockIDRandom() - voteSet, valSet, vals = randVoteSet(1, 1, cmtproto.PrecommitType, 1, MaxTotalVotingPower) - commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) - ) - require.NoError(t, err) - - err = valSet.VerifyCommitLightTrusting("test_chain_id", commit, - cmtmath.Fraction{Numerator: 25, Denominator: 55}) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "int64 overflow") - } -} - func TestSafeMul(t *testing.T) { testCases := []struct { a int64