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 }