|
|
- package blocks
-
- import (
- "bytes"
- "crypto/sha256"
- "errors"
- "fmt"
- "io"
- "strings"
- "sync"
-
- . "github.com/tendermint/tendermint/binary"
- . "github.com/tendermint/tendermint/common"
- "github.com/tendermint/tendermint/merkle"
- )
-
- const (
- partSize = 4096 // 4KB
- )
-
- var (
- ErrPartSetUnexpectedIndex = errors.New("Error part set unexpected index")
- ErrPartSetInvalidTrail = errors.New("Error part set invalid trail")
- )
-
- type Part struct {
- Index uint16
- Trail [][]byte
- Bytes []byte
-
- // Cache
- hash []byte
- }
-
- func ReadPart(r io.Reader, n *int64, err *error) *Part {
- return &Part{
- Index: ReadUInt16(r, n, err),
- Trail: ReadByteSlices(r, n, err),
- Bytes: ReadByteSlice(r, n, err),
- }
- }
-
- func (part *Part) WriteTo(w io.Writer) (n int64, err error) {
- WriteUInt16(w, part.Index, &n, &err)
- WriteByteSlices(w, part.Trail, &n, &err)
- WriteByteSlice(w, part.Bytes, &n, &err)
- return
- }
-
- func (part *Part) Hash() []byte {
- if part.hash != nil {
- return part.hash
- } else {
- hasher := sha256.New()
- _, err := hasher.Write(part.Bytes)
- if err != nil {
- panic(err)
- }
- part.hash = hasher.Sum(nil)
- return part.hash
- }
- }
-
- func (part *Part) String() string {
- return part.StringWithIndent("")
- }
-
- func (part *Part) StringWithIndent(indent string) string {
- trailStrings := make([]string, len(part.Trail))
- for i, hash := range part.Trail {
- trailStrings[i] = fmt.Sprintf("%X", hash)
- }
- return fmt.Sprintf(`Part{
- %s Index: %v
- %s Trail:
- %s %v
- %s}`,
- indent, part.Index,
- indent,
- indent, strings.Join(trailStrings, "\n"+indent+" "),
- indent)
- }
-
- //-------------------------------------
-
- type PartSetHeader struct {
- Total uint16
- Hash []byte
- }
-
- func ReadPartSetHeader(r io.Reader, n *int64, err *error) PartSetHeader {
- return PartSetHeader{
- Total: ReadUInt16(r, n, err),
- Hash: ReadByteSlice(r, n, err),
- }
- }
-
- func (psh PartSetHeader) WriteTo(w io.Writer) (n int64, err error) {
- WriteUInt16(w, psh.Total, &n, &err)
- WriteByteSlice(w, psh.Hash, &n, &err)
- return
- }
-
- func (psh PartSetHeader) IsZero() bool {
- return psh.Total == 0
- }
-
- func (psh PartSetHeader) String() string {
- return fmt.Sprintf("PartSet{%X/%v}", psh.Hash, psh.Total)
- }
-
- func (psh PartSetHeader) Equals(other PartSetHeader) bool {
- return psh.Total == other.Total && bytes.Equal(psh.Hash, other.Hash)
- }
-
- //-------------------------------------
-
- type PartSet struct {
- total uint16
- hash []byte
-
- mtx sync.Mutex
- parts []*Part
- partsBitArray BitArray
- count uint16
- }
-
- // Returns an immutable, full PartSet.
- // TODO Name is confusing, Data/Header clash with Block.Data/Header
- func NewPartSetFromData(data []byte) *PartSet {
- // divide data into 4kb parts.
- total := (len(data) + partSize - 1) / partSize
- parts := make([]*Part, total)
- parts_ := make([]merkle.Hashable, total)
- partsBitArray := NewBitArray(uint(total))
- for i := 0; i < total; i++ {
- part := &Part{
- Index: uint16(i),
- Bytes: data[i*partSize : MinInt(len(data), (i+1)*partSize)],
- }
- parts[i] = part
- parts_[i] = part
- partsBitArray.SetIndex(uint(i), true)
- }
- // Compute merkle trails
- hashTree := merkle.HashTreeFromHashables(parts_)
- for i := 0; i < total; i++ {
- parts[i].Trail = merkle.HashTrailForIndex(hashTree, i)
- }
- return &PartSet{
- total: uint16(total),
- hash: hashTree[len(hashTree)/2],
- parts: parts,
- partsBitArray: partsBitArray,
- count: uint16(total),
- }
- }
-
- // Returns an empty PartSet ready to be populated.
- // TODO Name is confusing, Data/Header clash with Block.Data/Header
- func NewPartSetFromHeader(header PartSetHeader) *PartSet {
- return &PartSet{
- total: header.Total,
- hash: header.Hash,
- parts: make([]*Part, header.Total),
- partsBitArray: NewBitArray(uint(header.Total)),
- count: 0,
- }
- }
-
- func (ps *PartSet) Header() PartSetHeader {
- if ps == nil {
- return PartSetHeader{}
- } else {
- return PartSetHeader{
- Total: ps.total,
- Hash: ps.hash,
- }
- }
- }
-
- func (ps *PartSet) BitArray() BitArray {
- ps.mtx.Lock()
- defer ps.mtx.Unlock()
- return ps.partsBitArray.Copy()
- }
-
- func (ps *PartSet) Hash() []byte {
- if ps == nil {
- return nil
- }
- return ps.hash
- }
-
- func (ps *PartSet) HashesTo(hash []byte) bool {
- if ps == nil {
- return false
- }
- return bytes.Equal(ps.hash, hash)
- }
-
- func (ps *PartSet) Count() uint16 {
- if ps == nil {
- return 0
- }
- return ps.count
- }
-
- func (ps *PartSet) Total() uint16 {
- if ps == nil {
- return 0
- }
- return ps.total
- }
-
- func (ps *PartSet) AddPart(part *Part) (bool, error) {
- ps.mtx.Lock()
- defer ps.mtx.Unlock()
-
- // Invalid part index
- if part.Index >= ps.total {
- return false, ErrPartSetUnexpectedIndex
- }
-
- // If part already exists, return false.
- if ps.parts[part.Index] != nil {
- return false, nil
- }
-
- // Check hash trail
- if !merkle.VerifyHashTrailForIndex(int(part.Index), part.Hash(), part.Trail, ps.hash) {
- return false, ErrPartSetInvalidTrail
- }
-
- // Add part
- ps.parts[part.Index] = part
- ps.partsBitArray.SetIndex(uint(part.Index), true)
- ps.count++
- return true, nil
- }
-
- func (ps *PartSet) GetPart(index uint16) *Part {
- ps.mtx.Lock()
- defer ps.mtx.Unlock()
- return ps.parts[index]
- }
-
- func (ps *PartSet) IsComplete() bool {
- return ps.count == ps.total
- }
-
- func (ps *PartSet) GetReader() io.Reader {
- if !ps.IsComplete() {
- panic("Cannot GetReader() on incomplete PartSet")
- }
- buf := []byte{}
- for _, part := range ps.parts {
- buf = append(buf, part.Bytes...)
- }
- return bytes.NewReader(buf)
- }
-
- func (ps *PartSet) Description() string {
- if ps == nil {
- return "nil-PartSet"
- } else {
- return fmt.Sprintf("(%v of %v)", ps.Count(), ps.Total())
- }
- }
|