package lite import ( "time" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" ) // privKeys is a helper type for testing. // // It lets us simulate signing with many keys. The main use case is to create // a set, and call GenSignedHeader to get properly signed header for testing. // // You can set different weights of validators each time you call ToValidators, // and can optionally extend the validator set later with Extend. type privKeys []crypto.PrivKey // genPrivKeys produces an array of private keys to generate commits. func genPrivKeys(n int) privKeys { res := make(privKeys, n) for i := range res { res[i] = ed25519.GenPrivKey() } return res } // Change replaces the key at index i. func (pkz privKeys) Change(i int) privKeys { res := make(privKeys, len(pkz)) copy(res, pkz) res[i] = ed25519.GenPrivKey() return res } // Extend adds n more keys (to remove, just take a slice). func (pkz privKeys) Extend(n int) privKeys { extra := genPrivKeys(n) return append(pkz, extra...) } // GenSecpPrivKeys produces an array of secp256k1 private keys to generate commits. func GenSecpPrivKeys(n int) privKeys { res := make(privKeys, n) for i := range res { res[i] = secp256k1.GenPrivKey() } return res } // ExtendSecp adds n more secp256k1 keys (to remove, just take a slice). func (pkz privKeys) ExtendSecp(n int) privKeys { extra := GenSecpPrivKeys(n) return append(pkz, extra...) } // ToValidators produces a valset from the set of keys. // The first key has weight `init` and it increases by `inc` every step // so we can have all the same weight, or a simple linear distribution // (should be enough for testing). func (pkz privKeys) ToValidators(init, inc int64) *types.ValidatorSet { res := make([]*types.Validator, len(pkz)) for i, k := range pkz { res[i] = types.NewValidator(k.PubKey(), init+int64(i)*inc) } return types.NewValidatorSet(res) } // signHeader properly signs the header with all keys from first to last exclusive. func (pkz privKeys) signHeader(header *types.Header, first, last int) *types.Commit { commitSigs := make([]types.CommitSig, len(pkz)) for i := 0; i < len(pkz); i++ { commitSigs[i] = types.NewCommitSigAbsent() } // We need this list to keep the ordering. vset := pkz.ToValidators(1, 0) blockID := types.BlockID{ Hash: header.Hash(), PartsHeader: types.PartSetHeader{Total: 1, Hash: crypto.CRandBytes(32)}, } // Fill in the votes we want. for i := first; i < last && i < len(pkz); i++ { vote := makeVote(header, vset, pkz[i], blockID) commitSigs[vote.ValidatorIndex] = vote.CommitSig() } return types.NewCommit(header.Height, 1, blockID, commitSigs) } func makeVote(header *types.Header, valset *types.ValidatorSet, key crypto.PrivKey, blockID types.BlockID) *types.Vote { addr := key.PubKey().Address() idx, _ := valset.GetByAddress(addr) vote := &types.Vote{ ValidatorAddress: addr, ValidatorIndex: idx, Height: header.Height, Round: 1, Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, } // Sign it signBytes := vote.SignBytes(header.ChainID) // TODO Consider reworking makeVote API to return an error sig, err := key.Sign(signBytes) if err != nil { panic(err) } vote.Signature = sig return vote } func genHeader(chainID string, height int64, bTime time.Time, txs types.Txs, valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte) *types.Header { return &types.Header{ ChainID: chainID, Height: height, Time: bTime, // LastBlockID // LastCommitHash ValidatorsHash: valset.Hash(), NextValidatorsHash: nextValset.Hash(), DataHash: txs.Hash(), AppHash: appHash, ConsensusHash: consHash, LastResultsHash: resHash, } } // GenSignedHeader calls genHeader and signHeader and combines them into a SignedHeader. func (pkz privKeys) GenSignedHeader(chainID string, height int64, bTime time.Time, txs types.Txs, valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) *types.SignedHeader { header := genHeader(chainID, height, bTime, txs, valset, nextValset, appHash, consHash, resHash) return &types.SignedHeader{ Header: header, Commit: pkz.signHeader(header, first, last), } }