consensus/p2pool/stratum/template.go
DataHoarder 6efd52cae7
All checks were successful
continuous-integration/drone/push Build is passing
Experimental: Support new merge mine Consensus Id
Depends on p2pool how migration/hardfork of existing chains will be done. A challenge still exists due to handshake challenges not discriminating
2024-04-10 03:38:44 +02:00

266 lines
8.5 KiB
Go

package stratum
import (
"encoding/binary"
"errors"
"git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto"
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
"git.gammaspectra.live/P2Pool/consensus/v3/types"
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
"git.gammaspectra.live/P2Pool/sha3"
"io"
)
type Template struct {
Buffer []byte
// NonceOffset offset of an uint32
NonceOffset int
CoinbaseOffset int
// ExtraNonceOffset offset of an uint32
ExtraNonceOffset int
// TemplateIdOffset offset of a types.Hash
TemplateIdOffset int
// TransactionsOffset Start of transactions section
TransactionsOffset int
// TemplateExtraBufferOffset offset of 4*uint32
TemplateExtraBufferOffset int
MainHeight uint64
MainParent types.Hash
SideHeight uint64
SideParent types.Hash
SideDifficulty types.Difficulty
MerkleTreeMainBranch []types.Hash
}
func (tpl *Template) Write(writer io.Writer, nonce, extraNonce, sideRandomNumber, sideExtraNonce uint32, templateId types.Hash) error {
var uint32Buf [4]byte
if _, err := writer.Write(tpl.Buffer[:tpl.NonceOffset]); err != nil {
return err
}
binary.LittleEndian.PutUint32(uint32Buf[:], nonce)
if _, err := writer.Write(uint32Buf[:]); err != nil {
return err
}
if _, err := writer.Write(tpl.Buffer[tpl.NonceOffset+4 : tpl.ExtraNonceOffset]); err != nil {
return err
}
binary.LittleEndian.PutUint32(uint32Buf[:], extraNonce)
if _, err := writer.Write(uint32Buf[:]); err != nil {
return err
}
if _, err := writer.Write(tpl.Buffer[tpl.ExtraNonceOffset+4 : tpl.TemplateIdOffset]); err != nil {
return err
}
if _, err := writer.Write(templateId[:]); err != nil {
return err
}
if _, err := writer.Write(tpl.Buffer[tpl.TemplateIdOffset+types.HashSize : tpl.TemplateExtraBufferOffset+4*2]); err != nil {
return err
}
binary.LittleEndian.PutUint32(uint32Buf[:], sideRandomNumber)
if _, err := writer.Write(uint32Buf[:]); err != nil {
return err
}
binary.LittleEndian.PutUint32(uint32Buf[:], sideExtraNonce)
if _, err := writer.Write(uint32Buf[:]); err != nil {
return err
}
return nil
}
func (tpl *Template) Blob(preAllocatedBuffer []byte, nonce, extraNonce, sideRandomNumber, sideExtraNonce uint32, templateId types.Hash) []byte {
buf := append(preAllocatedBuffer, tpl.Buffer...)
// Overwrite nonce
binary.LittleEndian.PutUint32(buf[tpl.NonceOffset:], nonce)
// Overwrite extra nonce
binary.LittleEndian.PutUint32(buf[tpl.ExtraNonceOffset:], extraNonce)
// Overwrite template id
copy(buf[tpl.TemplateIdOffset:], templateId[:])
// Overwrite sidechain random number
binary.LittleEndian.PutUint32(buf[tpl.TemplateExtraBufferOffset+4*2:], sideRandomNumber)
// Overwrite sidechain extra nonce number
binary.LittleEndian.PutUint32(buf[tpl.TemplateExtraBufferOffset+4*3:], sideExtraNonce)
return buf
}
func (tpl *Template) TemplateId(hasher *sha3.HasherState, preAllocatedBuffer []byte, consensus *sidechain.Consensus, sideRandomNumber, sideExtraNonce uint32, result *types.Hash) {
buf := tpl.Blob(preAllocatedBuffer, 0, 0, sideRandomNumber, sideExtraNonce, types.ZeroHash)
_, _ = hasher.Write(buf)
if tpl.ShareVersion(consensus) > sidechain.ShareVersion_V2 {
_, _ = hasher.Write(consensus.MergeMiningId[:])
} else {
_, _ = hasher.Write(consensus.Id[:])
}
crypto.HashFastSum(hasher, (*result)[:])
hasher.Reset()
}
func (tpl *Template) Timestamp() uint64 {
t, _ := binary.Uvarint(tpl.Buffer[2:])
return t
}
func (tpl *Template) ShareVersion(consensus *sidechain.Consensus) sidechain.ShareVersion {
return sidechain.P2PoolShareVersion(consensus, tpl.Timestamp())
}
func (tpl *Template) CoinbaseBufferLength() int {
return tpl.TransactionsOffset - tpl.CoinbaseOffset
}
func (tpl *Template) CoinbaseBlob(preAllocatedBuffer []byte, extraNonce uint32, templateId types.Hash) []byte {
buf := append(preAllocatedBuffer, tpl.Buffer[tpl.CoinbaseOffset:tpl.TransactionsOffset]...)
// Overwrite extra nonce
binary.LittleEndian.PutUint32(buf[tpl.ExtraNonceOffset-tpl.CoinbaseOffset:], extraNonce)
// Overwrite template id
copy(buf[tpl.TemplateIdOffset-tpl.CoinbaseOffset:], templateId[:])
return buf
}
func (tpl *Template) CoinbaseBlobId(hasher *sha3.HasherState, preAllocatedBuffer []byte, extraNonce uint32, templateId types.Hash, result *types.Hash) {
buf := tpl.CoinbaseBlob(preAllocatedBuffer, extraNonce, templateId)
_, _ = hasher.Write(buf[:len(buf)-1])
crypto.HashFastSum(hasher, (*result)[:])
hasher.Reset()
CoinbaseIdHash(hasher, *result, result)
}
func (tpl *Template) CoinbaseId(hasher *sha3.HasherState, extraNonce uint32, templateId types.Hash, result *types.Hash) {
var extraNonceBuf [4]byte
_, _ = hasher.Write(tpl.Buffer[tpl.CoinbaseOffset:tpl.ExtraNonceOffset])
// extra nonce
binary.LittleEndian.PutUint32(extraNonceBuf[:], extraNonce)
_, _ = hasher.Write(extraNonceBuf[:])
_, _ = hasher.Write(tpl.Buffer[tpl.ExtraNonceOffset+4 : tpl.TemplateIdOffset])
// template id
_, _ = hasher.Write(templateId[:])
_, _ = hasher.Write(tpl.Buffer[tpl.TemplateIdOffset+types.HashSize : tpl.TransactionsOffset-1])
crypto.HashFastSum(hasher, (*result)[:])
hasher.Reset()
CoinbaseIdHash(hasher, *result, result)
}
var zeroExtraBaseRCTHash = crypto.PooledKeccak256([]byte{0})
func CoinbaseIdHash(hasher *sha3.HasherState, coinbaseBlobMinusBaseRTC types.Hash, result *types.Hash) {
_, _ = hasher.Write(coinbaseBlobMinusBaseRTC[:])
// Base RCT, single 0 byte in miner tx
_, _ = hasher.Write(zeroExtraBaseRCTHash[:])
// Prunable RCT, empty in miner tx
_, _ = hasher.Write(types.ZeroHash[:])
crypto.HashFastSum(hasher, (*result)[:])
hasher.Reset()
}
func (tpl *Template) HashingBlobBufferLength() int {
_, n := binary.Uvarint(tpl.Buffer[tpl.TransactionsOffset:])
return tpl.NonceOffset + 4 + types.HashSize + n
}
func (tpl *Template) HashingBlob(hasher *sha3.HasherState, preAllocatedBuffer []byte, nonce, extraNonce uint32, templateId types.Hash) []byte {
var rootHash types.Hash
tpl.CoinbaseId(hasher, extraNonce, templateId, &rootHash)
buf := append(preAllocatedBuffer, tpl.Buffer[:tpl.NonceOffset]...)
buf = binary.LittleEndian.AppendUint32(buf, nonce)
numTransactions, n := binary.Uvarint(tpl.Buffer[tpl.TransactionsOffset:])
if numTransactions < 1 {
} else if numTransactions < 2 {
_, _ = hasher.Write(rootHash[:])
_, _ = hasher.Write(tpl.Buffer[tpl.TransactionsOffset+n : tpl.TransactionsOffset+n+types.HashSize])
crypto.HashFastSum(hasher, rootHash[:])
hasher.Reset()
} else {
for i := range tpl.MerkleTreeMainBranch {
_, _ = hasher.Write(rootHash[:])
_, _ = hasher.Write(tpl.MerkleTreeMainBranch[i][:])
crypto.HashFastSum(hasher, rootHash[:])
hasher.Reset()
}
}
buf = append(buf, rootHash[:]...)
buf = binary.AppendUvarint(buf, numTransactions+1)
return buf
}
func TemplateFromPoolBlock(b *sidechain.PoolBlock) (tpl *Template, err error) {
if b.ShareVersion() != sidechain.ShareVersion_V2 {
return nil, errors.New("unsupported share version")
}
totalLen := b.BufferLength()
buf := make([]byte, 0, b.BufferLength())
if buf, err = b.AppendBinaryFlags(buf, false, false); err != nil {
return nil, err
}
tpl = &Template{
Buffer: buf,
}
mainBufferLength := b.Main.BufferLength()
coinbaseLength := b.Main.Coinbase.BufferLength()
tpl.NonceOffset = mainBufferLength - (4 + coinbaseLength + utils.UVarInt64Size(len(b.Main.Transactions)) + types.HashSize*len(b.Main.Transactions))
tpl.CoinbaseOffset = mainBufferLength - (coinbaseLength + utils.UVarInt64Size(len(b.Main.Transactions)) + types.HashSize*len(b.Main.Transactions))
tpl.TransactionsOffset = mainBufferLength - (utils.UVarInt64Size(len(b.Main.Transactions)) + types.HashSize*len(b.Main.Transactions))
tpl.ExtraNonceOffset = tpl.NonceOffset + 4 + (coinbaseLength - (b.Main.Coinbase.Extra[1].BufferLength() + b.Main.Coinbase.Extra[2].BufferLength() + 1)) + 1 + utils.UVarInt64Size(b.Main.Coinbase.Extra[1].VarInt)
tpl.TemplateIdOffset = tpl.NonceOffset + 4 + (coinbaseLength - (b.Main.Coinbase.Extra[2].BufferLength() + 1)) + 1 + utils.UVarInt64Size(b.Main.Coinbase.Extra[2].VarInt)
tpl.TemplateExtraBufferOffset = totalLen - 4*4
// Set places to zeroes where necessary
tpl.Buffer = tpl.Blob(make([]byte, 0, len(tpl.Buffer)), 0, 0, 0, 0, types.ZeroHash)
if len(b.Main.Transactions) > 1 {
merkleTree := make(crypto.BinaryTreeHash, len(b.Main.Transactions)+1)
copy(merkleTree[1:], b.Main.Transactions)
tpl.MerkleTreeMainBranch = merkleTree.MainBranch()
}
tpl.MainHeight = b.Main.Coinbase.GenHeight
tpl.MainParent = b.Main.PreviousId
tpl.SideHeight = b.Side.Height
tpl.SideParent = b.Side.Parent
tpl.SideDifficulty = b.Side.Difficulty
return tpl, nil
}