tutus-consensus/internal/consensus/amev_block.go

129 lines
3.3 KiB
Go

package consensus
import (
"bytes"
"encoding/binary"
"encoding/gob"
"math"
"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 amevBlock struct {
base
transactions []dbft.Transaction[crypto.Uint256]
signature []byte
hash *crypto.Uint256
}
var _ dbft.Block[crypto.Uint256] = new(amevBlock)
// NewAMEVBlock returns new block based on PreBlock and additional Commit-level data
// collected from M consensus nodes.
func NewAMEVBlock(pre dbft.PreBlock[crypto.Uint256], cnData [][]byte, m int) dbft.Block[crypto.Uint256] {
preB := pre.(*preBlock)
res := new(amevBlock)
res.base = preB.base
// Based on the provided cnData we'll add one more transaction to the resulting block.
// Some artificial rules of new tx creation are invented here, but in Neo X there will
// be well-defined custom rules for Envelope transactions.
var sum uint32
for i := range m {
sum += binary.BigEndian.Uint32(cnData[i])
}
tx := Tx64(math.MaxInt64 - int64(sum))
res.transactions = append(preB.initialTransactions, &tx)
// Rebuild Merkle root for the new set of transactions.
txHashes := make([]crypto.Uint256, len(res.transactions))
for i := range txHashes {
txHashes[i] = res.transactions[i].Hash()
}
mt := merkle.NewMerkleTree(txHashes...)
res.base.MerkleRoot = mt.Root().Hash
return res
}
// PrevHash implements Block interface.
func (b *amevBlock) PrevHash() crypto.Uint256 {
return b.base.PrevHash
}
// Index implements Block interface.
func (b *amevBlock) Index() uint32 {
return b.base.Index
}
// MerkleRoot implements Block interface.
func (b *amevBlock) MerkleRoot() crypto.Uint256 {
return b.base.MerkleRoot
}
// Transactions implements Block interface.
func (b *amevBlock) Transactions() []dbft.Transaction[crypto.Uint256] {
return b.transactions
}
// SetTransactions implements Block interface. This method is special since it's
// left for dBFT 2.0 compatibility and transactions from this method must not be
// reused to fill final Block's transactions.
func (b *amevBlock) SetTransactions(_ []dbft.Transaction[crypto.Uint256]) {
}
// Signature implements Block interface.
func (b *amevBlock) 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 *amevBlock) GetHashData() []byte {
buf := bytes.Buffer{}
w := gob.NewEncoder(&buf)
_ = b.EncodeBinary(w)
return buf.Bytes()
}
// Sign implements Block interface.
func (b *amevBlock) Sign(key dbft.PrivateKey) error {
data := b.GetHashData()
sign, err := key.(*crypto.ECDSAPriv).Sign(data)
if err != nil {
return err
}
b.signature = sign
return nil
}
// Verify implements Block interface.
func (b *amevBlock) Verify(pub dbft.PublicKey, sign []byte) error {
data := b.GetHashData()
return pub.(*crypto.ECDSAPub).Verify(data, sign)
}
// Hash implements Block interface.
func (b *amevBlock) 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
}