DataHoarder
e6812c58b1
All checks were successful
continuous-integration/drone/push Build is passing
312 lines
9.2 KiB
Go
312 lines
9.2 KiB
Go
package block
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/monero"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
|
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
|
"io"
|
|
"math"
|
|
)
|
|
|
|
const MaxTransactionCount = uint64(math.MaxUint64) / types.HashSize
|
|
|
|
type Block struct {
|
|
MajorVersion uint8 `json:"major_version"`
|
|
MinorVersion uint8 `json:"minor_version"`
|
|
// Nonce re-arranged here to improve memory layout space
|
|
Nonce uint32 `json:"nonce"`
|
|
|
|
Timestamp uint64 `json:"timestamp"`
|
|
PreviousId types.Hash `json:"previous_id"`
|
|
//Nonce would be here
|
|
|
|
Coinbase transaction.CoinbaseTransaction `json:"coinbase"`
|
|
|
|
Transactions []types.Hash `json:"transactions,omitempty"`
|
|
// TransactionParentIndices amount of reward existing Outputs. Used by p2pool serialized compact broadcasted blocks in protocol >= 1.1, filled only in compact blocks or by pre-processing.
|
|
TransactionParentIndices []uint64 `json:"transaction_parent_indices,omitempty"`
|
|
}
|
|
|
|
type Header struct {
|
|
MajorVersion uint8 `json:"major_version"`
|
|
MinorVersion uint8 `json:"minor_version"`
|
|
// Nonce re-arranged here to improve memory layout space
|
|
Nonce uint32 `json:"nonce"`
|
|
|
|
Timestamp uint64 `json:"timestamp"`
|
|
PreviousId types.Hash `json:"previous_id"`
|
|
Height uint64 `json:"height"`
|
|
//Nonce would be here
|
|
Reward uint64 `json:"reward"`
|
|
Difficulty types.Difficulty `json:"difficulty"`
|
|
Id types.Hash `json:"id"`
|
|
}
|
|
|
|
func (b *Block) MarshalBinary() (buf []byte, err error) {
|
|
return b.MarshalBinaryFlags(false, false, false)
|
|
}
|
|
|
|
func (b *Block) BufferLength() int {
|
|
return utils.UVarInt64Size(b.MajorVersion) +
|
|
utils.UVarInt64Size(b.MinorVersion) +
|
|
utils.UVarInt64Size(b.Timestamp) +
|
|
types.HashSize +
|
|
4 +
|
|
b.Coinbase.BufferLength() +
|
|
utils.UVarInt64Size(len(b.Transactions)) + types.HashSize*len(b.Transactions)
|
|
}
|
|
|
|
func (b *Block) MarshalBinaryFlags(compact, pruned, containsAuxiliaryTemplateId bool) (buf []byte, err error) {
|
|
return b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), pruned, compact, containsAuxiliaryTemplateId)
|
|
}
|
|
|
|
func (b *Block) AppendBinaryFlags(preAllocatedBuf []byte, compact, pruned, containsAuxiliaryTemplateId bool) (buf []byte, err error) {
|
|
buf = preAllocatedBuf
|
|
|
|
if b.MajorVersion > monero.HardForkSupportedVersion {
|
|
return nil, fmt.Errorf("unsupported version %d", b.MajorVersion)
|
|
}
|
|
|
|
if b.MinorVersion < b.MajorVersion {
|
|
return nil, fmt.Errorf("minor version %d smaller than major %d", b.MinorVersion, b.MajorVersion)
|
|
}
|
|
|
|
buf = binary.AppendUvarint(buf, uint64(b.MajorVersion))
|
|
buf = binary.AppendUvarint(buf, uint64(b.MinorVersion))
|
|
|
|
buf = binary.AppendUvarint(buf, b.Timestamp)
|
|
buf = append(buf, b.PreviousId[:]...)
|
|
buf = binary.LittleEndian.AppendUint32(buf, b.Nonce)
|
|
|
|
if buf, err = b.Coinbase.AppendBinaryFlags(buf, pruned, containsAuxiliaryTemplateId); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf = binary.AppendUvarint(buf, uint64(len(b.Transactions)))
|
|
if compact {
|
|
for i, txId := range b.Transactions {
|
|
if i < len(b.TransactionParentIndices) && b.TransactionParentIndices[i] != 0 {
|
|
buf = binary.AppendUvarint(buf, b.TransactionParentIndices[i])
|
|
} else {
|
|
buf = binary.AppendUvarint(buf, 0)
|
|
buf = append(buf, txId[:]...)
|
|
}
|
|
}
|
|
} else {
|
|
for _, txId := range b.Transactions {
|
|
buf = append(buf, txId[:]...)
|
|
}
|
|
}
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
type PrunedFlagsFunc func() (containsAuxiliaryTemplateId bool)
|
|
|
|
func (b *Block) FromReader(reader utils.ReaderAndByteReader, canBePruned bool, f PrunedFlagsFunc) (err error) {
|
|
return b.FromReaderFlags(reader, false, canBePruned, f)
|
|
}
|
|
|
|
func (b *Block) FromCompactReader(reader utils.ReaderAndByteReader, canBePruned bool, f PrunedFlagsFunc) (err error) {
|
|
return b.FromReaderFlags(reader, true, canBePruned, f)
|
|
}
|
|
|
|
func (b *Block) UnmarshalBinary(data []byte, canBePruned bool, f PrunedFlagsFunc) error {
|
|
reader := bytes.NewReader(data)
|
|
return b.FromReader(reader, canBePruned, f)
|
|
}
|
|
|
|
func (b *Block) FromReaderFlags(reader utils.ReaderAndByteReader, compact, canBePruned bool, f PrunedFlagsFunc) (err error) {
|
|
var (
|
|
txCount uint64
|
|
transactionHash types.Hash
|
|
)
|
|
|
|
if b.MajorVersion, err = reader.ReadByte(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if b.MajorVersion > monero.HardForkSupportedVersion {
|
|
return fmt.Errorf("unsupported version %d", b.MajorVersion)
|
|
}
|
|
|
|
if b.MinorVersion, err = reader.ReadByte(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if b.MinorVersion < b.MajorVersion {
|
|
return fmt.Errorf("minor version %d smaller than major version %d", b.MinorVersion, b.MajorVersion)
|
|
}
|
|
|
|
if b.Timestamp, err = binary.ReadUvarint(reader); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err = io.ReadFull(reader, b.PreviousId[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = binary.Read(reader, binary.LittleEndian, &b.Nonce); err != nil {
|
|
return err
|
|
}
|
|
|
|
var containsAuxiliaryTemplateId bool
|
|
|
|
if canBePruned && f != nil {
|
|
containsAuxiliaryTemplateId = f()
|
|
}
|
|
|
|
// Coinbase Tx Decoding
|
|
{
|
|
if err = b.Coinbase.FromReader(reader, canBePruned, containsAuxiliaryTemplateId); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
//TODO: verify hardfork major versions
|
|
|
|
if txCount, err = binary.ReadUvarint(reader); err != nil {
|
|
return err
|
|
} else if txCount > MaxTransactionCount {
|
|
return fmt.Errorf("transaction count count too large: %d > %d", txCount, MaxTransactionCount)
|
|
} else if txCount > 0 {
|
|
if compact {
|
|
// preallocate with soft cap
|
|
b.Transactions = make([]types.Hash, 0, min(8192, txCount))
|
|
b.TransactionParentIndices = make([]uint64, 0, min(8192, txCount))
|
|
|
|
var parentIndex uint64
|
|
for i := 0; i < int(txCount); i++ {
|
|
if parentIndex, err = binary.ReadUvarint(reader); err != nil {
|
|
return err
|
|
}
|
|
|
|
if parentIndex == 0 {
|
|
//not in lookup
|
|
if _, err = io.ReadFull(reader, transactionHash[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
b.Transactions = append(b.Transactions, transactionHash)
|
|
} else {
|
|
b.Transactions = append(b.Transactions, types.ZeroHash)
|
|
}
|
|
|
|
b.TransactionParentIndices = append(b.TransactionParentIndices, parentIndex)
|
|
}
|
|
} else {
|
|
// preallocate with soft cap
|
|
b.Transactions = make([]types.Hash, 0, min(8192, txCount))
|
|
|
|
for i := 0; i < int(txCount); i++ {
|
|
if _, err = io.ReadFull(reader, transactionHash[:]); err != nil {
|
|
return err
|
|
}
|
|
b.Transactions = append(b.Transactions, transactionHash)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *Block) Header() *Header {
|
|
return &Header{
|
|
MajorVersion: b.MajorVersion,
|
|
MinorVersion: b.MinorVersion,
|
|
Timestamp: b.Timestamp,
|
|
PreviousId: b.PreviousId,
|
|
Height: b.Coinbase.GenHeight,
|
|
Nonce: b.Nonce,
|
|
Reward: b.Coinbase.AuxiliaryData.TotalReward,
|
|
Id: b.Id(),
|
|
Difficulty: types.ZeroDifficulty,
|
|
}
|
|
}
|
|
|
|
func (b *Block) HeaderBlobBufferLength() int {
|
|
return 1 + 1 +
|
|
utils.UVarInt64Size(b.Timestamp) +
|
|
types.HashSize +
|
|
4
|
|
}
|
|
|
|
func (b *Block) HeaderBlob(preAllocatedBuf []byte) []byte {
|
|
buf := preAllocatedBuf
|
|
buf = append(buf, b.MajorVersion)
|
|
buf = append(buf, b.MinorVersion)
|
|
buf = binary.AppendUvarint(buf, b.Timestamp)
|
|
buf = append(buf, b.PreviousId[:]...)
|
|
buf = binary.LittleEndian.AppendUint32(buf, b.Nonce)
|
|
|
|
return buf
|
|
}
|
|
|
|
// SideChainHashingBlob Same as MarshalBinary but with nonce or template id set to 0
|
|
func (b *Block) SideChainHashingBlob(preAllocatedBuf []byte, zeroTemplateId bool) (buf []byte, err error) {
|
|
buf = preAllocatedBuf
|
|
buf = append(buf, b.MajorVersion)
|
|
buf = append(buf, b.MinorVersion)
|
|
buf = binary.AppendUvarint(buf, b.Timestamp)
|
|
buf = append(buf, b.PreviousId[:]...)
|
|
buf = binary.LittleEndian.AppendUint32(buf, 0) //replaced
|
|
|
|
if buf, err = b.Coinbase.SideChainHashingBlob(buf, zeroTemplateId); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf = binary.AppendUvarint(buf, uint64(len(b.Transactions)))
|
|
for _, txId := range b.Transactions {
|
|
buf = append(buf, txId[:]...)
|
|
}
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
func (b *Block) HashingBlobBufferLength() int {
|
|
return b.HeaderBlobBufferLength() +
|
|
types.HashSize + utils.UVarInt64Size(len(b.Transactions)+1)
|
|
}
|
|
|
|
func (b *Block) HashingBlob(preAllocatedBuf []byte) []byte {
|
|
buf := b.HeaderBlob(preAllocatedBuf)
|
|
|
|
merkleTree := make(crypto.BinaryTreeHash, len(b.Transactions)+1)
|
|
//TODO: cache?
|
|
merkleTree[0] = b.Coinbase.CalculateId()
|
|
copy(merkleTree[1:], b.Transactions)
|
|
txTreeHash := merkleTree.RootHash()
|
|
buf = append(buf, txTreeHash[:]...)
|
|
|
|
buf = binary.AppendUvarint(buf, uint64(len(b.Transactions)+1))
|
|
|
|
return buf
|
|
}
|
|
|
|
func (b *Block) Difficulty(f GetDifficultyByHeightFunc) types.Difficulty {
|
|
//cached by sidechain.Share
|
|
return f(b.Coinbase.GenHeight)
|
|
}
|
|
|
|
func (b *Block) PowHashWithError(hasher randomx.Hasher, f GetSeedByHeightFunc) (types.Hash, error) {
|
|
//not cached
|
|
if seed := f(b.Coinbase.GenHeight); seed == types.ZeroHash {
|
|
return types.ZeroHash, errors.New("could not get seed")
|
|
} else {
|
|
return hasher.Hash(seed[:], b.HashingBlob(make([]byte, 0, b.HashingBlobBufferLength())))
|
|
}
|
|
}
|
|
|
|
func (b *Block) Id() types.Hash {
|
|
//cached by sidechain.Share
|
|
var varIntBuf [binary.MaxVarintLen64]byte
|
|
buf := b.HashingBlob(make([]byte, 0, b.HashingBlobBufferLength()))
|
|
return crypto.PooledKeccak256(varIntBuf[:binary.PutUvarint(varIntBuf[:], uint64(len(buf)))], buf)
|
|
}
|