package consensus import ( "fmt" "io" . "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/state" ) // Proof of lock. // +2/3 of validators' prevotes for a given blockhash (or nil) type POL struct { Height uint32 Round uint16 BlockHash []byte // Could be nil, which makes this a proof of unlock. BlockParts PartSetHeader // When BlockHash is nil, this is zero. Votes []Signature // Vote signatures for height/round/hash Commits []RoundSignature // Commit signatures for height/hash } func ReadPOL(r io.Reader, n *int64, err *error) *POL { return &POL{ Height: ReadUInt32(r, n, err), Round: ReadUInt16(r, n, err), BlockHash: ReadByteSlice(r, n, err), BlockParts: ReadPartSetHeader(r, n, err), Votes: ReadSignatures(r, n, err), Commits: ReadRoundSignatures(r, n, err), } } func (pol *POL) WriteTo(w io.Writer) (n int64, err error) { WriteUInt32(w, pol.Height, &n, &err) WriteUInt16(w, pol.Round, &n, &err) WriteByteSlice(w, pol.BlockHash, &n, &err) WriteBinary(w, pol.BlockParts, &n, &err) WriteSignatures(w, pol.Votes, &n, &err) WriteRoundSignatures(w, pol.Commits, &n, &err) return } // Returns whether +2/3 have voted/committed for BlockHash. func (pol *POL) Verify(vset *state.ValidatorSet) error { talliedVotingPower := uint64(0) voteDoc := BinaryBytes(&Vote{ Height: pol.Height, Round: pol.Round, Type: VoteTypePrevote, BlockHash: pol.BlockHash, BlockParts: pol.BlockParts, }) seenValidators := map[uint64]struct{}{} for _, sig := range pol.Votes { // Validate if _, seen := seenValidators[sig.SignerId]; seen { return Errorf("Duplicate validator for vote %v for POL %v", sig, pol) } _, val := vset.GetById(sig.SignerId) if val == nil { return Errorf("Invalid validator for vote %v for POL %v", sig, pol) } if !val.VerifyBytes(voteDoc, sig) { return Errorf("Invalid signature for vote %v for POL %v", sig, pol) } // Tally seenValidators[val.Id] = struct{}{} talliedVotingPower += val.VotingPower } for _, rsig := range pol.Commits { round := rsig.Round sig := rsig.Signature // Validate if _, seen := seenValidators[sig.SignerId]; seen { return Errorf("Duplicate validator for commit %v for POL %v", sig, pol) } _, val := vset.GetById(sig.SignerId) if val == nil { return Errorf("Invalid validator for commit %v for POL %v", sig, pol) } if round >= pol.Round { return Errorf("Invalid commit round %v for POL %v", round, pol) } commitDoc := BinaryBytes(&Vote{ Height: pol.Height, Round: round, Type: VoteTypeCommit, BlockHash: pol.BlockHash, BlockParts: pol.BlockParts, }) if !val.VerifyBytes(commitDoc, sig) { return Errorf("Invalid signature for commit %v for POL %v", sig, pol) } // Tally seenValidators[val.Id] = struct{}{} talliedVotingPower += val.VotingPower } if talliedVotingPower > vset.TotalVotingPower()*2/3 { return nil } else { return Errorf("Invalid POL, insufficient voting power %v, needed %v", talliedVotingPower, (vset.TotalVotingPower()*2/3 + 1)) } } func (pol *POL) Description() string { if pol == nil { return "nil-POL" } else { return fmt.Sprintf("POL{H:%v R:%v BH:%X}", pol.Height, pol.Round, Fingerprint(pol.BlockHash), pol.BlockParts) } }