tendermint/blocks/block_part_set.go

159 lines
3.5 KiB
Go

package blocks
import (
"bytes"
"errors"
"sync"
"github.com/tendermint/tendermint/merkle"
)
// A collection of block parts.
// Doesn't do any validation.
type BlockPartSet struct {
mtx sync.Mutex
height uint32
total uint16 // total number of parts
numParts uint16 // number of parts in this set
parts []*BlockPart
_block *Block // cache
}
var (
ErrInvalidBlockPartConflict = errors.New("Invalid block part conflict") // Signer signed conflicting parts
)
// parts may be nil if the parts aren't in hand.
func NewBlockPartSet(height uint32, parts []*BlockPart) *BlockPartSet {
bps := &BlockPartSet{
height: height,
parts: parts,
numParts: uint16(len(parts)),
}
if len(parts) > 0 {
bps.total = parts[0].Total
}
return bps
}
func (bps *BlockPartSet) Height() uint32 {
return bps.height
}
func (bps *BlockPartSet) BlockParts() []*BlockPart {
bps.mtx.Lock()
defer bps.mtx.Unlock()
return bps.parts
}
func (bps *BlockPartSet) BitArray() []byte {
bps.mtx.Lock()
defer bps.mtx.Unlock()
if bps.parts == nil {
return nil
}
bitArray := make([]byte, (len(bps.parts)+7)/8)
for i, part := range bps.parts {
if part != nil {
bitArray[i/8] |= 1 << uint(i%8)
}
}
return bitArray
}
// If the part isn't valid, returns an error.
// err can be ErrInvalidBlockPartConflict
// NOTE: Caller must check the signature before adding.
func (bps *BlockPartSet) AddBlockPart(part *BlockPart) (added bool, err error) {
bps.mtx.Lock()
defer bps.mtx.Unlock()
if bps.parts == nil {
// First received part for this round.
bps.parts = make([]*BlockPart, part.Total)
bps.total = uint16(part.Total)
bps.parts[int(part.Index)] = part
bps.numParts++
return true, nil
} else {
// Check part.Index and part.Total
if uint16(part.Index) >= bps.total {
return false, ErrInvalidBlockPartConflict
}
if uint16(part.Total) != bps.total {
return false, ErrInvalidBlockPartConflict
}
// Check for existing parts.
existing := bps.parts[part.Index]
if existing != nil {
if bytes.Equal(existing.Bytes, part.Bytes) {
// Ignore duplicate
return false, nil
} else {
return false, ErrInvalidBlockPartConflict
}
} else {
bps.parts[int(part.Index)] = part
bps.numParts++
return true, nil
}
}
}
func (bps *BlockPartSet) IsComplete() bool {
bps.mtx.Lock()
defer bps.mtx.Unlock()
return bps.total > 0 && bps.total == bps.numParts
}
func (bps *BlockPartSet) Block() *Block {
if !bps.IsComplete() {
return nil
}
bps.mtx.Lock()
defer bps.mtx.Unlock()
if bps._block == nil {
block, err := BlockPartsToBlock(bps.parts)
if err != nil {
panic(err)
}
bps._block = block
}
return bps._block
}
func (bps *BlockPartSet) Hash() []byte {
if !bps.IsComplete() {
panic("Cannot get hash of an incomplete BlockPartSet")
}
hashes := [][]byte{}
for _, part := range bps.parts {
partHash := part.Hash()
hashes = append(hashes, partHash)
}
return merkle.HashFromByteSlices(hashes)
}
// The proposal hash includes both the block hash
// as well as the BlockPartSet merkle hash.
func (bps *BlockPartSet) ProposalHash() []byte {
bpsHash := bps.Hash()
blockHash := bps.Block().Hash()
return merkle.HashFromByteSlices([][]byte{bpsHash, blockHash})
}
//-----------------------------------------------------------------------------
func BlockPartsToBlock(parts []*BlockPart) (*Block, error) {
blockBytes := []byte{}
for _, part := range parts {
blockBytes = append(blockBytes, part.Bytes...)
}
var n int64
var err error
block := ReadBlock(bytes.NewReader(blockBytes), &n, &err)
return block, err
}