tutus-consensus/internal/consensus/block.go

152 lines
3.7 KiB
Go

package consensus
import (
"bytes"
"encoding/gob"
"git.marketally.com/tutus-one/tutus-consensus"
"git.marketally.com/tutus-one/tutus-consensus/internal/crypto"
"git.marketally.com/tutus-one/tutus-consensus/internal/merkle"
)
type (
// base is a structure containing all
// hashable and signable fields of the block.
base struct {
ConsensusData uint64
Index uint32
Timestamp uint32
Version uint32
MerkleRoot crypto.Uint256
PrevHash crypto.Uint256
NextConsensus crypto.Uint160
}
neoBlock struct {
base
transactions []dbft.Transaction[crypto.Uint256]
signature []byte
hash *crypto.Uint256
}
// signable is an interface used within consensus package to abstract private key
// functionality. This interface is used instead of direct structure usage to be
// able to mock private key implementation in unit tests.
signable interface {
Sign([]byte) ([]byte, error)
}
)
var _ dbft.Block[crypto.Uint256] = new(neoBlock)
// PrevHash implements Block interface.
func (b *neoBlock) PrevHash() crypto.Uint256 {
return b.base.PrevHash
}
// Index implements Block interface.
func (b *neoBlock) Index() uint32 {
return b.base.Index
}
// MerkleRoot implements Block interface.
func (b *neoBlock) MerkleRoot() crypto.Uint256 {
return b.base.MerkleRoot
}
// Transactions implements Block interface.
func (b *neoBlock) Transactions() []dbft.Transaction[crypto.Uint256] {
return b.transactions
}
// SetTransactions implements Block interface.
func (b *neoBlock) SetTransactions(txx []dbft.Transaction[crypto.Uint256]) {
b.transactions = txx
}
// NewBlock returns new block.
func NewBlock(timestamp uint64, index uint32, prevHash crypto.Uint256, nonce uint64, txHashes []crypto.Uint256) dbft.Block[crypto.Uint256] {
block := new(neoBlock)
block.Timestamp = uint32(timestamp / 1000000000)
block.base.Index = index
// NextConsensus and Version information is not provided by dBFT context,
// these are implementation-specific fields, and thus, should be managed outside the
// dBFT library. For simulation simplicity, let's assume that these fields are filled
// by every CN separately and is not verified.
block.NextConsensus = crypto.Uint160{1, 2, 3}
block.Version = 0
block.base.PrevHash = prevHash
block.ConsensusData = nonce
if len(txHashes) != 0 {
mt := merkle.NewMerkleTree(txHashes...)
block.base.MerkleRoot = mt.Root().Hash
}
return block
}
// Signature implements Block interface.
func (b *neoBlock) Signature() []byte {
return b.signature
}
// GetHashData returns data for hashing and signing.
// It must be an injection of the set of blocks to the set
// of byte slices, i.e:
// 1. It must have only one valid result for one block.
// 2. Two different blocks must have different hash data.
func (b *neoBlock) GetHashData() []byte {
buf := bytes.Buffer{}
w := gob.NewEncoder(&buf)
_ = b.EncodeBinary(w)
return buf.Bytes()
}
// Sign implements Block interface.
func (b *neoBlock) Sign(key dbft.PrivateKey) error {
data := b.GetHashData()
sign, err := key.(signable).Sign(data)
if err != nil {
return err
}
b.signature = sign
return nil
}
// Verify implements Block interface.
func (b *neoBlock) Verify(pub dbft.PublicKey, sign []byte) error {
data := b.GetHashData()
return pub.(*crypto.ECDSAPub).Verify(data, sign)
}
// Hash implements Block interface.
func (b *neoBlock) Hash() (h crypto.Uint256) {
if b.hash != nil {
return *b.hash
} else if b.transactions == nil {
return
}
hash := crypto.Hash256(b.GetHashData())
b.hash = &hash
return hash
}
// EncodeBinary implements Serializable interface.
func (b base) EncodeBinary(w *gob.Encoder) error {
return w.Encode(b)
}
// DecodeBinary implements Serializable interface.
func (b *base) DecodeBinary(r *gob.Decoder) error {
return r.Decode(b)
}