## Description deprecation & removal of multisig. This key was only used in the sdk and now it has been added there Closes: #4715 Closes: #2163pull/4991/head
@ -1,233 +0,0 @@ | |||||
package bitarray | |||||
import ( | |||||
"bytes" | |||||
"encoding/binary" | |||||
"errors" | |||||
"fmt" | |||||
"regexp" | |||||
"strings" | |||||
) | |||||
// CompactBitArray is an implementation of a space efficient bit array. | |||||
// This is used to ensure that the encoded data takes up a minimal amount of | |||||
// space after amino encoding. | |||||
// This is not thread safe, and is not intended for concurrent usage. | |||||
type CompactBitArray struct { | |||||
ExtraBitsStored byte `json:"extra_bits"` // The number of extra bits in elems. | |||||
Elems []byte `json:"bits"` | |||||
} | |||||
// NewCompactBitArray returns a new compact bit array. | |||||
// It returns nil if the number of bits is zero. | |||||
func NewCompactBitArray(bits int) *CompactBitArray { | |||||
if bits <= 0 { | |||||
return nil | |||||
} | |||||
return &CompactBitArray{ | |||||
ExtraBitsStored: byte(bits % 8), | |||||
Elems: make([]byte, (bits+7)/8), | |||||
} | |||||
} | |||||
// Size returns the number of bits in the bitarray | |||||
func (bA *CompactBitArray) Size() int { | |||||
if bA == nil { | |||||
return 0 | |||||
} else if bA.ExtraBitsStored == byte(0) { | |||||
return len(bA.Elems) * 8 | |||||
} | |||||
// num_bits = 8*num_full_bytes + overflow_in_last_byte | |||||
// num_full_bytes = (len(bA.Elems)-1) | |||||
return (len(bA.Elems)-1)*8 + int(bA.ExtraBitsStored) | |||||
} | |||||
// GetIndex returns the bit at index i within the bit array. | |||||
// The behavior is undefined if i >= bA.Size() | |||||
func (bA *CompactBitArray) GetIndex(i int) bool { | |||||
if bA == nil { | |||||
return false | |||||
} | |||||
if i >= bA.Size() { | |||||
return false | |||||
} | |||||
return bA.Elems[i>>3]&(uint8(1)<<uint8(7-(i%8))) > 0 | |||||
} | |||||
// SetIndex sets the bit at index i within the bit array. | |||||
// The behavior is undefined if i >= bA.Size() | |||||
func (bA *CompactBitArray) SetIndex(i int, v bool) bool { | |||||
if bA == nil { | |||||
return false | |||||
} | |||||
if i >= bA.Size() { | |||||
return false | |||||
} | |||||
if v { | |||||
bA.Elems[i>>3] |= (uint8(1) << uint8(7-(i%8))) | |||||
} else { | |||||
bA.Elems[i>>3] &= ^(uint8(1) << uint8(7-(i%8))) | |||||
} | |||||
return true | |||||
} | |||||
// NumTrueBitsBefore returns the number of bits set to true before the | |||||
// given index. e.g. if bA = _XX__XX, NumOfTrueBitsBefore(4) = 2, since | |||||
// there are two bits set to true before index 4. | |||||
func (bA *CompactBitArray) NumTrueBitsBefore(index int) int { | |||||
numTrueValues := 0 | |||||
for i := 0; i < index; i++ { | |||||
if bA.GetIndex(i) { | |||||
numTrueValues++ | |||||
} | |||||
} | |||||
return numTrueValues | |||||
} | |||||
// Copy returns a copy of the provided bit array. | |||||
func (bA *CompactBitArray) Copy() *CompactBitArray { | |||||
if bA == nil { | |||||
return nil | |||||
} | |||||
c := make([]byte, len(bA.Elems)) | |||||
copy(c, bA.Elems) | |||||
return &CompactBitArray{ | |||||
ExtraBitsStored: bA.ExtraBitsStored, | |||||
Elems: c, | |||||
} | |||||
} | |||||
// String returns a string representation of CompactBitArray: BA{<bit-string>}, | |||||
// where <bit-string> is a sequence of 'x' (1) and '_' (0). | |||||
// The <bit-string> includes spaces and newlines to help people. | |||||
// For a simple sequence of 'x' and '_' characters with no spaces or newlines, | |||||
// see the MarshalJSON() method. | |||||
// Example: "BA{_x_}" or "nil-BitArray" for nil. | |||||
func (bA *CompactBitArray) String() string { | |||||
return bA.StringIndented("") | |||||
} | |||||
// StringIndented returns the same thing as String(), but applies the indent | |||||
// at every 10th bit, and twice at every 50th bit. | |||||
func (bA *CompactBitArray) StringIndented(indent string) string { | |||||
if bA == nil { | |||||
return "nil-BitArray" | |||||
} | |||||
lines := []string{} | |||||
bits := "" | |||||
size := bA.Size() | |||||
for i := 0; i < size; i++ { | |||||
if bA.GetIndex(i) { | |||||
bits += "x" | |||||
} else { | |||||
bits += "_" | |||||
} | |||||
if i%100 == 99 { | |||||
lines = append(lines, bits) | |||||
bits = "" | |||||
} | |||||
if i%10 == 9 { | |||||
bits += indent | |||||
} | |||||
if i%50 == 49 { | |||||
bits += indent | |||||
} | |||||
} | |||||
if len(bits) > 0 { | |||||
lines = append(lines, bits) | |||||
} | |||||
return fmt.Sprintf("BA{%v:%v}", size, strings.Join(lines, indent)) | |||||
} | |||||
// MarshalJSON implements json.Marshaler interface by marshaling bit array | |||||
// using a custom format: a string of '-' or 'x' where 'x' denotes the 1 bit. | |||||
func (bA *CompactBitArray) MarshalJSON() ([]byte, error) { | |||||
if bA == nil { | |||||
return []byte("null"), nil | |||||
} | |||||
bits := `"` | |||||
size := bA.Size() | |||||
for i := 0; i < size; i++ { | |||||
if bA.GetIndex(i) { | |||||
bits += `x` | |||||
} else { | |||||
bits += `_` | |||||
} | |||||
} | |||||
bits += `"` | |||||
return []byte(bits), nil | |||||
} | |||||
var bitArrayJSONRegexp = regexp.MustCompile(`\A"([_x]*)"\z`) | |||||
// UnmarshalJSON implements json.Unmarshaler interface by unmarshaling a custom | |||||
// JSON description. | |||||
func (bA *CompactBitArray) UnmarshalJSON(bz []byte) error { | |||||
b := string(bz) | |||||
if b == "null" { | |||||
// This is required e.g. for encoding/json when decoding | |||||
// into a pointer with pre-allocated BitArray. | |||||
bA.ExtraBitsStored = 0 | |||||
bA.Elems = nil | |||||
return nil | |||||
} | |||||
// Validate 'b'. | |||||
match := bitArrayJSONRegexp.FindStringSubmatch(b) | |||||
if match == nil { | |||||
return fmt.Errorf("bitArray in JSON should be a string of format %q but got %s", bitArrayJSONRegexp.String(), b) | |||||
} | |||||
bits := match[1] | |||||
// Construct new CompactBitArray and copy over. | |||||
numBits := len(bits) | |||||
bA2 := NewCompactBitArray(numBits) | |||||
for i := 0; i < numBits; i++ { | |||||
if bits[i] == 'x' { | |||||
bA2.SetIndex(i, true) | |||||
} | |||||
} | |||||
*bA = *bA2 | |||||
return nil | |||||
} | |||||
// CompactMarshal is a space efficient encoding for CompactBitArray. | |||||
// It is not amino compatible. | |||||
func (bA *CompactBitArray) CompactMarshal() []byte { | |||||
size := bA.Size() | |||||
if size <= 0 { | |||||
return []byte("null") | |||||
} | |||||
bz := make([]byte, 0, size/8) | |||||
// length prefix number of bits, not number of bytes. This difference | |||||
// takes 3-4 bits in encoding, as opposed to instead encoding the number of | |||||
// bytes (saving 3-4 bits) and including the offset as a full byte. | |||||
bz = appendUvarint(bz, uint64(size)) | |||||
bz = append(bz, bA.Elems...) | |||||
return bz | |||||
} | |||||
// CompactUnmarshal is a space efficient decoding for CompactBitArray. | |||||
// It is not amino compatible. | |||||
func CompactUnmarshal(bz []byte) (*CompactBitArray, error) { | |||||
if len(bz) < 2 { | |||||
return nil, errors.New("compact bit array: invalid compact unmarshal size") | |||||
} else if bytes.Equal(bz, []byte("null")) { | |||||
return NewCompactBitArray(0), nil | |||||
} | |||||
size, n := binary.Uvarint(bz) | |||||
bz = bz[n:] | |||||
if len(bz) != int(size+7)/8 { | |||||
return nil, errors.New("compact bit array: invalid compact unmarshal size") | |||||
} | |||||
bA := &CompactBitArray{byte(int(size % 8)), bz} | |||||
return bA, nil | |||||
} | |||||
func appendUvarint(b []byte, x uint64) []byte { | |||||
var a [binary.MaxVarintLen64]byte | |||||
n := binary.PutUvarint(a[:], x) | |||||
return append(b, a[:n]...) | |||||
} |
@ -1,202 +0,0 @@ | |||||
package bitarray | |||||
import ( | |||||
"encoding/json" | |||||
"math/rand" | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
"github.com/stretchr/testify/require" | |||||
tmrand "github.com/tendermint/tendermint/libs/rand" | |||||
) | |||||
func randCompactBitArray(bits int) (*CompactBitArray, []byte) { | |||||
numBytes := (bits + 7) / 8 | |||||
src := tmrand.Bytes((bits + 7) / 8) | |||||
bA := NewCompactBitArray(bits) | |||||
for i := 0; i < numBytes-1; i++ { | |||||
for j := uint8(0); j < 8; j++ { | |||||
bA.SetIndex(i*8+int(j), src[i]&(uint8(1)<<(8-j)) > 0) | |||||
} | |||||
} | |||||
// Set remaining bits | |||||
for i := uint8(0); i < 8-bA.ExtraBitsStored; i++ { | |||||
bA.SetIndex(numBytes*8+int(i), src[numBytes-1]&(uint8(1)<<(8-i)) > 0) | |||||
} | |||||
return bA, src | |||||
} | |||||
func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) { | |||||
bitList := []int{-127, -128, -1 << 31} | |||||
for _, bits := range bitList { | |||||
bA := NewCompactBitArray(bits) | |||||
require.Nil(t, bA) | |||||
} | |||||
} | |||||
func TestJSONMarshalUnmarshal(t *testing.T) { | |||||
bA1 := NewCompactBitArray(0) | |||||
bA2 := NewCompactBitArray(1) | |||||
bA3 := NewCompactBitArray(1) | |||||
bA3.SetIndex(0, true) | |||||
bA4 := NewCompactBitArray(5) | |||||
bA4.SetIndex(0, true) | |||||
bA4.SetIndex(1, true) | |||||
bA5 := NewCompactBitArray(9) | |||||
bA5.SetIndex(0, true) | |||||
bA5.SetIndex(1, true) | |||||
bA5.SetIndex(8, true) | |||||
bA6 := NewCompactBitArray(16) | |||||
bA6.SetIndex(0, true) | |||||
bA6.SetIndex(1, true) | |||||
bA6.SetIndex(8, false) | |||||
bA6.SetIndex(15, true) | |||||
testCases := []struct { | |||||
bA *CompactBitArray | |||||
marshalledBA string | |||||
}{ | |||||
{nil, `null`}, | |||||
{bA1, `null`}, | |||||
{bA2, `"_"`}, | |||||
{bA3, `"x"`}, | |||||
{bA4, `"xx___"`}, | |||||
{bA5, `"xx______x"`}, | |||||
{bA6, `"xx_____________x"`}, | |||||
} | |||||
for _, tc := range testCases { | |||||
tc := tc | |||||
t.Run(tc.bA.String(), func(t *testing.T) { | |||||
bz, err := json.Marshal(tc.bA) | |||||
require.NoError(t, err) | |||||
assert.Equal(t, tc.marshalledBA, string(bz)) | |||||
var unmarshalledBA *CompactBitArray | |||||
err = json.Unmarshal(bz, &unmarshalledBA) | |||||
require.NoError(t, err) | |||||
if tc.bA == nil { | |||||
require.Nil(t, unmarshalledBA) | |||||
} else { | |||||
require.NotNil(t, unmarshalledBA) | |||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) | |||||
if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) { | |||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) | |||||
} | |||||
} | |||||
}) | |||||
} | |||||
} | |||||
func TestCompactMarshalUnmarshal(t *testing.T) { | |||||
bA1 := NewCompactBitArray(0) | |||||
bA2 := NewCompactBitArray(1) | |||||
bA3 := NewCompactBitArray(1) | |||||
bA3.SetIndex(0, true) | |||||
bA4 := NewCompactBitArray(5) | |||||
bA4.SetIndex(0, true) | |||||
bA4.SetIndex(1, true) | |||||
bA5 := NewCompactBitArray(9) | |||||
bA5.SetIndex(0, true) | |||||
bA5.SetIndex(1, true) | |||||
bA5.SetIndex(8, true) | |||||
bA6 := NewCompactBitArray(16) | |||||
bA6.SetIndex(0, true) | |||||
bA6.SetIndex(1, true) | |||||
bA6.SetIndex(8, false) | |||||
bA6.SetIndex(15, true) | |||||
testCases := []struct { | |||||
bA *CompactBitArray | |||||
marshalledBA []byte | |||||
}{ | |||||
{nil, []byte("null")}, | |||||
{bA1, []byte("null")}, | |||||
{bA2, []byte{byte(1), byte(0)}}, | |||||
{bA3, []byte{byte(1), byte(128)}}, | |||||
{bA4, []byte{byte(5), byte(192)}}, | |||||
{bA5, []byte{byte(9), byte(192), byte(128)}}, | |||||
{bA6, []byte{byte(16), byte(192), byte(1)}}, | |||||
} | |||||
for _, tc := range testCases { | |||||
tc := tc | |||||
t.Run(tc.bA.String(), func(t *testing.T) { | |||||
bz := tc.bA.CompactMarshal() | |||||
assert.Equal(t, tc.marshalledBA, bz) | |||||
unmarshalledBA, err := CompactUnmarshal(bz) | |||||
require.NoError(t, err) | |||||
if tc.bA == nil { | |||||
require.Nil(t, unmarshalledBA) | |||||
} else { | |||||
require.NotNil(t, unmarshalledBA) | |||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) | |||||
if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) { | |||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) | |||||
} | |||||
} | |||||
}) | |||||
} | |||||
} | |||||
func TestCompactBitArrayNumOfTrueBitsBefore(t *testing.T) { | |||||
testCases := []struct { | |||||
marshalledBA string | |||||
bAIndex []int | |||||
trueValueIndex []int | |||||
}{ | |||||
{`"_____"`, []int{0, 1, 2, 3, 4}, []int{0, 0, 0, 0, 0}}, | |||||
{`"x"`, []int{0}, []int{0}}, | |||||
{`"_x"`, []int{1}, []int{0}}, | |||||
{`"x___xxxx"`, []int{0, 4, 5, 6, 7}, []int{0, 1, 2, 3, 4}}, | |||||
{`"__x_xx_x__x_x___"`, []int{2, 4, 5, 7, 10, 12}, []int{0, 1, 2, 3, 4, 5}}, | |||||
{`"______________xx"`, []int{14, 15}, []int{0, 1}}, | |||||
} | |||||
for tcIndex, tc := range testCases { | |||||
tc := tc | |||||
tcIndex := tcIndex | |||||
t.Run(tc.marshalledBA, func(t *testing.T) { | |||||
var bA *CompactBitArray | |||||
err := json.Unmarshal([]byte(tc.marshalledBA), &bA) | |||||
require.NoError(t, err) | |||||
for i := 0; i < len(tc.bAIndex); i++ { | |||||
require.Equal(t, tc.trueValueIndex[i], bA.NumTrueBitsBefore(tc.bAIndex[i]), "tc %d, i %d", tcIndex, i) | |||||
} | |||||
}) | |||||
} | |||||
} | |||||
func TestCompactBitArrayGetSetIndex(t *testing.T) { | |||||
r := rand.New(rand.NewSource(100)) | |||||
numTests := 10 | |||||
numBitsPerArr := 100 | |||||
for i := 0; i < numTests; i++ { | |||||
bits := r.Intn(1000) | |||||
bA, _ := randCompactBitArray(bits) | |||||
for j := 0; j < numBitsPerArr; j++ { | |||||
copy := bA.Copy() | |||||
index := r.Intn(bits) | |||||
val := (r.Int63() % 2) == 0 | |||||
bA.SetIndex(index, val) | |||||
require.Equal(t, val, bA.GetIndex(index), "bA.SetIndex(%d, %v) failed on bit array: %s", index, val, copy) | |||||
} | |||||
} | |||||
} |
@ -1,30 +0,0 @@ | |||||
package multisig | |||||
import ( | |||||
amino "github.com/tendermint/go-amino" | |||||
"github.com/tendermint/tendermint/crypto" | |||||
"github.com/tendermint/tendermint/crypto/ed25519" | |||||
"github.com/tendermint/tendermint/crypto/secp256k1" | |||||
"github.com/tendermint/tendermint/crypto/sr25519" | |||||
) | |||||
// TODO: Figure out API for others to either add their own pubkey types, or | |||||
// to make verify / marshal accept a cdc. | |||||
const ( | |||||
PubKeyAminoRoute = "tendermint/PubKeyMultisigThreshold" | |||||
) | |||||
var cdc = amino.NewCodec() | |||||
func init() { | |||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil) | |||||
cdc.RegisterConcrete(PubKey{}, | |||||
PubKeyAminoRoute, nil) | |||||
cdc.RegisterConcrete(ed25519.PubKey{}, | |||||
ed25519.PubKeyAminoName, nil) | |||||
cdc.RegisterConcrete(sr25519.PubKey{}, | |||||
sr25519.PubKeyAminoName, nil) | |||||
cdc.RegisterConcrete(secp256k1.PubKey{}, | |||||
secp256k1.PubKeyAminoName, nil) | |||||
} |
@ -1,77 +0,0 @@ | |||||
package multisig | |||||
import ( | |||||
"fmt" | |||||
"strings" | |||||
"github.com/tendermint/tendermint/crypto" | |||||
"github.com/tendermint/tendermint/crypto/multisig/bitarray" | |||||
) | |||||
// Multisignature is used to represent the signature object used in the multisigs. | |||||
// Sigs is a list of signatures, sorted by corresponding index. | |||||
type Multisignature struct { | |||||
BitArray *bitarray.CompactBitArray | |||||
Sigs [][]byte | |||||
} | |||||
// NewMultisig returns a new Multisignature of size n. | |||||
func NewMultisig(n int) *Multisignature { | |||||
// Default the signature list to have a capacity of two, since we can | |||||
// expect that most multisigs will require multiple signers. | |||||
return &Multisignature{bitarray.NewCompactBitArray(n), make([][]byte, 0, 2)} | |||||
} | |||||
// GetIndex returns the index of pk in keys. Returns -1 if not found | |||||
func getIndex(pk crypto.PubKey, keys []crypto.PubKey) int { | |||||
for i := 0; i < len(keys); i++ { | |||||
if pk.Equals(keys[i]) { | |||||
return i | |||||
} | |||||
} | |||||
return -1 | |||||
} | |||||
// AddSignature adds a signature to the multisig, at the corresponding index. | |||||
// If the signature already exists, replace it. | |||||
func (mSig *Multisignature) AddSignature(sig []byte, index int) { | |||||
newSigIndex := mSig.BitArray.NumTrueBitsBefore(index) | |||||
// Signature already exists, just replace the value there | |||||
if mSig.BitArray.GetIndex(index) { | |||||
mSig.Sigs[newSigIndex] = sig | |||||
return | |||||
} | |||||
mSig.BitArray.SetIndex(index, true) | |||||
// Optimization if the index is the greatest index | |||||
if newSigIndex == len(mSig.Sigs) { | |||||
mSig.Sigs = append(mSig.Sigs, sig) | |||||
return | |||||
} | |||||
// Expand slice by one with a dummy element, move all elements after i | |||||
// over by one, then place the new signature in that gap. | |||||
mSig.Sigs = append(mSig.Sigs, make([]byte, 0)) | |||||
copy(mSig.Sigs[newSigIndex+1:], mSig.Sigs[newSigIndex:]) | |||||
mSig.Sigs[newSigIndex] = sig | |||||
} | |||||
// AddSignatureFromPubKey adds a signature to the multisig, at the index in | |||||
// keys corresponding to the provided pubkey. | |||||
func (mSig *Multisignature) AddSignatureFromPubKey(sig []byte, pubkey crypto.PubKey, keys []crypto.PubKey) error { | |||||
index := getIndex(pubkey, keys) | |||||
if index == -1 { | |||||
keysStr := make([]string, len(keys)) | |||||
for i, k := range keys { | |||||
keysStr[i] = fmt.Sprintf("%X", k.Bytes()) | |||||
} | |||||
return fmt.Errorf("provided key %X doesn't exist in pubkeys: \n%s", pubkey.Bytes(), strings.Join(keysStr, "\n")) | |||||
} | |||||
mSig.AddSignature(sig, index) | |||||
return nil | |||||
} | |||||
// Marshal the multisignature with amino | |||||
func (mSig *Multisignature) Marshal() []byte { | |||||
return cdc.MustMarshalBinaryBare(mSig) | |||||
} |
@ -1,96 +0,0 @@ | |||||
package multisig | |||||
import ( | |||||
"github.com/tendermint/tendermint/crypto" | |||||
) | |||||
// PubKey implements a K of N threshold multisig. | |||||
type PubKey struct { | |||||
K uint `json:"threshold"` | |||||
PubKeys []crypto.PubKey `json:"pubkeys"` | |||||
} | |||||
var _ crypto.PubKey = PubKey{} | |||||
// NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold. | |||||
// Panics if len(pubkeys) < k or 0 >= k. | |||||
func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey { | |||||
if k <= 0 { | |||||
panic("threshold k of n multisignature: k <= 0") | |||||
} | |||||
if len(pubkeys) < k { | |||||
panic("threshold k of n multisignature: len(pubkeys) < k") | |||||
} | |||||
for _, pubkey := range pubkeys { | |||||
if pubkey == nil { | |||||
panic("nil pubkey") | |||||
} | |||||
} | |||||
return PubKey{uint(k), pubkeys} | |||||
} | |||||
// VerifyBytes expects sig to be an amino encoded version of a MultiSignature. | |||||
// Returns true iff the multisignature contains k or more signatures | |||||
// for the correct corresponding keys, | |||||
// and all signatures are valid. (Not just k of the signatures) | |||||
// The multisig uses a bitarray, so multiple signatures for the same key is not | |||||
// a concern. | |||||
func (pk PubKey) VerifyBytes(msg []byte, marshalledSig []byte) bool { | |||||
var sig Multisignature | |||||
err := cdc.UnmarshalBinaryBare(marshalledSig, &sig) | |||||
if err != nil { | |||||
return false | |||||
} | |||||
size := sig.BitArray.Size() | |||||
// ensure bit array is the correct size | |||||
if len(pk.PubKeys) != size { | |||||
return false | |||||
} | |||||
// ensure size of signature list | |||||
if len(sig.Sigs) < int(pk.K) || len(sig.Sigs) > size { | |||||
return false | |||||
} | |||||
// ensure at least k signatures are set | |||||
if sig.BitArray.NumTrueBitsBefore(size) < int(pk.K) { | |||||
return false | |||||
} | |||||
// index in the list of signatures which we are concerned with. | |||||
sigIndex := 0 | |||||
for i := 0; i < size; i++ { | |||||
if sig.BitArray.GetIndex(i) { | |||||
if !pk.PubKeys[i].VerifyBytes(msg, sig.Sigs[sigIndex]) { | |||||
return false | |||||
} | |||||
sigIndex++ | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
// Bytes returns the amino encoded version of the PubKey | |||||
func (pk PubKey) Bytes() []byte { | |||||
return cdc.MustMarshalBinaryBare(pk) | |||||
} | |||||
// Address returns tmhash(PubKey.Bytes()) | |||||
func (pk PubKey) Address() crypto.Address { | |||||
return crypto.AddressHash(pk.Bytes()) | |||||
} | |||||
// Equals returns true iff pk and other both have the same number of keys, and | |||||
// all constituent keys are the same, and in the same order. | |||||
func (pk PubKey) Equals(other crypto.PubKey) bool { | |||||
otherKey, sameType := other.(PubKey) | |||||
if !sameType { | |||||
return false | |||||
} | |||||
if pk.K != otherKey.K || len(pk.PubKeys) != len(otherKey.PubKeys) { | |||||
return false | |||||
} | |||||
for i := 0; i < len(pk.PubKeys); i++ { | |||||
if !pk.PubKeys[i].Equals(otherKey.PubKeys[i]) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} |
@ -1,181 +0,0 @@ | |||||
package multisig | |||||
import ( | |||||
"math/rand" | |||||
"testing" | |||||
"github.com/stretchr/testify/require" | |||||
"github.com/tendermint/tendermint/crypto" | |||||
"github.com/tendermint/tendermint/crypto/ed25519" | |||||
"github.com/tendermint/tendermint/crypto/secp256k1" | |||||
"github.com/tendermint/tendermint/crypto/sr25519" | |||||
) | |||||
// This tests multisig functionality, but it expects the first k signatures to be valid | |||||
// TODO: Adapt it to give more flexibility about first k signatures being valid | |||||
func TestThresholdMultisigValidCases(t *testing.T) { | |||||
pkSet1, sigSet1 := generatePubKeysAndSignatures(5, []byte{1, 2, 3, 4}) | |||||
cases := []struct { | |||||
msg []byte | |||||
k int | |||||
pubkeys []crypto.PubKey | |||||
signingIndices []int | |||||
// signatures should be the same size as signingIndices. | |||||
signatures [][]byte | |||||
passAfterKSignatures []bool | |||||
}{ | |||||
{ | |||||
msg: []byte{1, 2, 3, 4}, | |||||
k: 2, | |||||
pubkeys: pkSet1, | |||||
signingIndices: []int{0, 3, 1}, | |||||
signatures: sigSet1, | |||||
passAfterKSignatures: []bool{false}, | |||||
}, | |||||
} | |||||
for tcIndex, tc := range cases { | |||||
multisigKey := NewPubKeyMultisigThreshold(tc.k, tc.pubkeys) | |||||
multisignature := NewMultisig(len(tc.pubkeys)) | |||||
for i := 0; i < tc.k-1; i++ { | |||||
signingIndex := tc.signingIndices[i] | |||||
require.NoError( | |||||
t, | |||||
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), | |||||
) | |||||
require.False( | |||||
t, | |||||
multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), | |||||
"multisig passed when i < k, tc %d, i %d", tcIndex, i, | |||||
) | |||||
require.NoError( | |||||
t, | |||||
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), | |||||
) | |||||
require.Equal( | |||||
t, | |||||
i+1, | |||||
len(multisignature.Sigs), | |||||
"adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, | |||||
) | |||||
} | |||||
require.False( | |||||
t, | |||||
multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), | |||||
"multisig passed with k - 1 sigs, tc %d", tcIndex, | |||||
) | |||||
require.NoError( | |||||
t, | |||||
multisignature.AddSignatureFromPubKey( | |||||
tc.signatures[tc.signingIndices[tc.k]], | |||||
tc.pubkeys[tc.signingIndices[tc.k]], | |||||
tc.pubkeys, | |||||
), | |||||
) | |||||
require.True( | |||||
t, | |||||
multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), | |||||
"multisig failed after k good signatures, tc %d", tcIndex, | |||||
) | |||||
for i := tc.k + 1; i < len(tc.signingIndices); i++ { | |||||
signingIndex := tc.signingIndices[i] | |||||
require.NoError( | |||||
t, | |||||
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), | |||||
) | |||||
require.Equal( | |||||
t, | |||||
tc.passAfterKSignatures[i-tc.k-1], | |||||
multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), | |||||
"multisig didn't verify as expected after k sigs, tc %d, i %d", tcIndex, i, | |||||
) | |||||
require.NoError( | |||||
t, | |||||
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), | |||||
) | |||||
require.Equal( | |||||
t, | |||||
i+1, | |||||
len(multisignature.Sigs), | |||||
"adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, | |||||
) | |||||
} | |||||
} | |||||
} | |||||
// TODO: Fully replace this test with table driven tests | |||||
func TestThresholdMultisigDuplicateSignatures(t *testing.T) { | |||||
msg := []byte{1, 2, 3, 4, 5} | |||||
pubkeys, sigs := generatePubKeysAndSignatures(5, msg) | |||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) | |||||
multisignature := NewMultisig(5) | |||||
require.False(t, multisigKey.VerifyBytes(msg, multisignature.Marshal())) | |||||
multisignature.AddSignatureFromPubKey(sigs[0], pubkeys[0], pubkeys) | |||||
// Add second signature manually | |||||
multisignature.Sigs = append(multisignature.Sigs, sigs[0]) | |||||
require.False(t, multisigKey.VerifyBytes(msg, multisignature.Marshal())) | |||||
} | |||||
// TODO: Fully replace this test with table driven tests | |||||
func TestMultiSigPubKeyEquality(t *testing.T) { | |||||
msg := []byte{1, 2, 3, 4} | |||||
pubkeys, _ := generatePubKeysAndSignatures(5, msg) | |||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) | |||||
var unmarshalledMultisig PubKey | |||||
cdc.MustUnmarshalBinaryBare(multisigKey.Bytes(), &unmarshalledMultisig) | |||||
require.True(t, multisigKey.Equals(unmarshalledMultisig)) | |||||
// Ensure that reordering pubkeys is treated as a different pubkey | |||||
pubkeysCpy := make([]crypto.PubKey, 5) | |||||
copy(pubkeysCpy, pubkeys) | |||||
pubkeysCpy[4] = pubkeys[3] | |||||
pubkeysCpy[3] = pubkeys[4] | |||||
multisigKey2 := NewPubKeyMultisigThreshold(2, pubkeysCpy) | |||||
require.False(t, multisigKey.Equals(multisigKey2)) | |||||
} | |||||
func TestAddress(t *testing.T) { | |||||
msg := []byte{1, 2, 3, 4} | |||||
pubkeys, _ := generatePubKeysAndSignatures(5, msg) | |||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) | |||||
require.Len(t, multisigKey.Address().Bytes(), 20) | |||||
} | |||||
func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) { | |||||
msg := []byte{1, 2, 3, 4} | |||||
pubkeys, _ := generatePubKeysAndSignatures(5, msg) | |||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) | |||||
ab, err := cdc.MarshalBinaryLengthPrefixed(multisigKey) | |||||
require.NoError(t, err) | |||||
// like other crypto.Pubkey implementations (e.g. ed25519.PubKey), | |||||
// PubKey should be deserializable into a crypto.PubKey: | |||||
var pubKey crypto.PubKey | |||||
err = cdc.UnmarshalBinaryLengthPrefixed(ab, &pubKey) | |||||
require.NoError(t, err) | |||||
require.Equal(t, multisigKey, pubKey) | |||||
} | |||||
func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) { | |||||
pubkeys = make([]crypto.PubKey, n) | |||||
signatures = make([][]byte, n) | |||||
for i := 0; i < n; i++ { | |||||
var privkey crypto.PrivKey | |||||
switch rand.Int63() % 3 { | |||||
case 0: | |||||
privkey = ed25519.GenPrivKey() | |||||
case 1: | |||||
privkey = secp256k1.GenPrivKey() | |||||
case 2: | |||||
privkey = sr25519.GenPrivKey() | |||||
} | |||||
pubkeys[i] = privkey.PubKey() | |||||
signatures[i], _ = privkey.Sign(msg) | |||||
} | |||||
return | |||||
} |