mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 15:04:22 +00:00
share block parts when we're the proposer
This commit is contained in:
+6
-30
@@ -2,13 +2,11 @@ package blocks
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
"github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/merkle"
|
||||
)
|
||||
|
||||
@@ -16,14 +14,6 @@ const (
|
||||
defaultBlockPartSizeBytes = 4096
|
||||
)
|
||||
|
||||
func CalcBlockURI(height uint32, hash []byte) string {
|
||||
return fmt.Sprintf("%v://block/%v#%X",
|
||||
config.Config.Network,
|
||||
height,
|
||||
hash,
|
||||
)
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
Header
|
||||
Validation
|
||||
@@ -49,15 +39,10 @@ func (b *Block) WriteTo(w io.Writer) (n int64, err error) {
|
||||
}
|
||||
|
||||
func (b *Block) ValidateBasic() error {
|
||||
// Basic validation that doesn't involve context.
|
||||
// XXX
|
||||
// TODO Basic validation that doesn't involve context.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Block) URI() string {
|
||||
return CalcBlockURI(b.Height, b.Hash())
|
||||
}
|
||||
|
||||
func (b *Block) Hash() []byte {
|
||||
if b.hash != nil {
|
||||
return b.hash
|
||||
@@ -73,7 +58,8 @@ func (b *Block) Hash() []byte {
|
||||
}
|
||||
|
||||
// The returns parts must be signed afterwards.
|
||||
func (b *Block) ToBlockParts() (parts []*BlockPart) {
|
||||
func (b *Block) ToBlockPartSet() *BlockPartSet {
|
||||
var parts []*BlockPart
|
||||
blockBytes := BinaryBytes(b)
|
||||
total := (len(blockBytes) + defaultBlockPartSizeBytes - 1) / defaultBlockPartSizeBytes
|
||||
for i := 0; i < total; i++ {
|
||||
@@ -90,7 +76,7 @@ func (b *Block) ToBlockParts() (parts []*BlockPart) {
|
||||
}
|
||||
parts = append(parts, part)
|
||||
}
|
||||
return parts
|
||||
return NewBlockPartSet(b.Height, parts)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -133,18 +119,8 @@ func (bp *BlockPart) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (bp *BlockPart) URI() string {
|
||||
return fmt.Sprintf("%v://block/%v/%v[%v/%v]#%X\n",
|
||||
config.Config.Network,
|
||||
bp.Height,
|
||||
bp.Round,
|
||||
bp.Index,
|
||||
bp.Total,
|
||||
bp.BlockPartHash(),
|
||||
)
|
||||
}
|
||||
|
||||
func (bp *BlockPart) BlockPartHash() []byte {
|
||||
// Hash returns the hash of the block part data bytes.
|
||||
func (bp *BlockPart) Hash() []byte {
|
||||
if bp.hash != nil {
|
||||
return bp.hash
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
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
|
||||
}
|
||||
+13
-2
@@ -90,8 +90,19 @@ func (bs *BlockStore) LoadBlock(height uint32) *Block {
|
||||
if part0 == nil {
|
||||
return nil
|
||||
}
|
||||
// XXX implement
|
||||
panic("TODO: Not implemented")
|
||||
parts := []*BlockPart{part0}
|
||||
for i := uint16(1); i < part0.Total; i++ {
|
||||
part := bs.LoadBlockPart(height, i)
|
||||
if part == nil {
|
||||
Panicf("Failed to retrieve block part %v at height %v", i, height)
|
||||
}
|
||||
parts = append(parts, part)
|
||||
}
|
||||
block, err := BlockPartsToBlock(parts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
// NOTE: Assumes that parts as well as the block are valid. See StageBlockParts().
|
||||
|
||||
Reference in New Issue
Block a user