2022-10-26 16:46:41 +00:00
package block
import (
"bytes"
"encoding/binary"
2023-03-05 14:06:49 +00:00
"errors"
2022-11-07 22:59:52 +00:00
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
2022-10-26 16:46:41 +00:00
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
2022-11-07 22:59:52 +00:00
"hash"
2022-10-26 16:46:41 +00:00
"io"
)
type Block struct {
MajorVersion uint8
MinorVersion uint8
Timestamp uint64
PreviousId types . Hash
2022-10-27 08:09:39 +00:00
Nonce uint32
2022-10-26 16:46:41 +00:00
Coinbase * transaction . CoinbaseTransaction
Transactions [ ] types . Hash
2022-12-04 00:46:22 +00:00
// 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.
2022-12-03 22:43:18 +00:00
TransactionParentIndices [ ] uint64
2022-10-26 16:46:41 +00:00
}
2022-11-05 05:29:12 +00:00
type Header struct {
2022-11-03 11:32:07 +00:00
MajorVersion uint8
MinorVersion uint8
Timestamp uint64
PreviousId types . Hash
Height uint64
Nonce uint32
Reward uint64
Difficulty types . Difficulty
Id types . Hash
}
2022-10-26 16:46:41 +00:00
type readerAndByteReader interface {
io . Reader
io . ByteReader
}
func ( b * Block ) MarshalBinary ( ) ( buf [ ] byte , err error ) {
2022-12-04 00:46:22 +00:00
return b . MarshalBinaryFlags ( false , false )
}
func ( b * Block ) MarshalBinaryFlags ( pruned , compact bool ) ( buf [ ] byte , err error ) {
2022-10-26 16:46:41 +00:00
var txBuf [ ] byte
2022-12-04 00:46:22 +00:00
if txBuf , err = b . Coinbase . MarshalBinaryFlags ( pruned ) ; err != nil {
2022-10-26 16:46:41 +00:00
return nil , err
}
2022-10-27 08:09:39 +00:00
buf = make ( [ ] byte , 0 , 1 + 1 + binary . MaxVarintLen64 + types . HashSize + 4 + len ( txBuf ) + binary . MaxVarintLen64 + types . HashSize * len ( b . Transactions ) )
2022-10-26 16:46:41 +00:00
buf = append ( buf , b . MajorVersion )
buf = append ( buf , b . MinorVersion )
buf = binary . AppendUvarint ( buf , b . Timestamp )
buf = append ( buf , b . PreviousId [ : ] ... )
2022-10-27 08:09:39 +00:00
buf = binary . LittleEndian . AppendUint32 ( buf , b . Nonce )
2022-10-26 16:46:41 +00:00
buf = append ( buf , txBuf [ : ] ... )
buf = binary . AppendUvarint ( buf , uint64 ( len ( b . Transactions ) ) )
2022-12-04 00:46:22 +00:00
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 [ : ] ... )
}
2022-10-26 16:46:41 +00:00
}
return buf , nil
}
func ( b * Block ) FromReader ( reader readerAndByteReader ) ( err error ) {
2022-12-04 00:46:22 +00:00
return b . FromReaderFlags ( reader , false )
}
func ( b * Block ) FromCompactReader ( reader readerAndByteReader ) ( err error ) {
return b . FromReaderFlags ( reader , true )
}
func ( b * Block ) UnmarshalBinary ( data [ ] byte ) error {
reader := bytes . NewReader ( data )
return b . FromReader ( reader )
}
func ( b * Block ) FromReaderFlags ( reader readerAndByteReader , compact bool ) ( err error ) {
2022-10-26 16:46:41 +00:00
var (
txCount uint64
transactionHash types . Hash
)
2022-10-27 08:09:39 +00:00
if b . MajorVersion , err = reader . ReadByte ( ) ; err != nil {
2022-10-26 16:46:41 +00:00
return err
}
2022-10-27 08:09:39 +00:00
if b . MinorVersion , err = reader . ReadByte ( ) ; err != nil {
2022-10-26 16:46:41 +00:00
return err
}
if b . Timestamp , err = binary . ReadUvarint ( reader ) ; err != nil {
return err
}
if _ , err = io . ReadFull ( reader , b . PreviousId [ : ] ) ; err != nil {
return err
}
2022-10-27 08:09:39 +00:00
if err = binary . Read ( reader , binary . LittleEndian , & b . Nonce ) ; err != nil {
2022-10-26 16:46:41 +00:00
return err
}
// Coinbase Tx Decoding
{
b . Coinbase = & transaction . CoinbaseTransaction { }
if err = b . Coinbase . FromReader ( reader ) ; err != nil {
return err
}
}
if txCount , err = binary . ReadUvarint ( reader ) ; err != nil {
return err
}
2022-12-04 00:46:22 +00:00
if compact {
if txCount < 8192 {
b . Transactions = make ( [ ] types . Hash , 0 , txCount )
b . TransactionParentIndices = make ( [ ] uint64 , 0 , txCount )
2022-10-26 16:46:41 +00:00
}
2022-12-03 22:43:18 +00:00
2022-12-04 00:46:22 +00:00
var parentIndex uint64
for i := 0 ; i < int ( txCount ) ; i ++ {
if parentIndex , err = binary . ReadUvarint ( reader ) ; err != nil {
return err
}
2022-12-03 22:43:18 +00:00
2022-12-04 00:46:22 +00:00
if parentIndex == 0 {
//not in lookup
if _ , err = io . ReadFull ( reader , transactionHash [ : ] ) ; err != nil {
return err
}
2022-12-03 22:43:18 +00:00
2022-12-04 00:46:22 +00:00
b . Transactions = append ( b . Transactions , transactionHash )
} else {
b . Transactions = append ( b . Transactions , types . ZeroHash )
}
2022-12-03 22:43:18 +00:00
2022-12-04 00:46:22 +00:00
b . TransactionParentIndices = append ( b . TransactionParentIndices , parentIndex )
2022-12-03 22:43:18 +00:00
}
2022-12-04 00:46:22 +00:00
} else {
if txCount < 8192 {
b . Transactions = make ( [ ] types . Hash , 0 , txCount )
2022-12-03 22:43:18 +00:00
}
2022-12-04 00:46:22 +00:00
for i := 0 ; i < int ( txCount ) ; i ++ {
2022-12-03 22:43:18 +00:00
if _ , err = io . ReadFull ( reader , transactionHash [ : ] ) ; err != nil {
return err
}
b . Transactions = append ( b . Transactions , transactionHash )
}
}
return nil
}
2022-11-05 05:29:12 +00:00
func ( b * Block ) Header ( ) * Header {
return & Header {
2022-11-03 11:32:07 +00:00
MajorVersion : b . MajorVersion ,
MinorVersion : b . MinorVersion ,
Timestamp : b . Timestamp ,
PreviousId : b . PreviousId ,
Height : b . Coinbase . GenHeight ,
Nonce : b . Nonce ,
Reward : b . Coinbase . TotalReward ,
Id : b . Id ( ) ,
Difficulty : types . ZeroDifficulty ,
}
}
func ( b * Block ) HeaderBlob ( ) [ ] byte {
2022-10-26 16:46:41 +00:00
//TODO: cache
2022-10-27 08:09:39 +00:00
buf := make ( [ ] byte , 0 , 1 + 1 + binary . MaxVarintLen64 + types . HashSize + 4 + types . HashSize + binary . MaxVarintLen64 ) //predict its use on HashingBlob
2022-10-26 16:46:41 +00:00
buf = append ( buf , b . MajorVersion )
buf = append ( buf , b . MinorVersion )
buf = binary . AppendUvarint ( buf , b . Timestamp )
buf = append ( buf , b . PreviousId [ : ] ... )
2022-10-27 08:09:39 +00:00
buf = binary . LittleEndian . AppendUint32 ( buf , b . Nonce )
2022-10-26 16:46:41 +00:00
return buf
}
2023-03-07 17:57:06 +00:00
// SideChainHashingBlob Same as MarshalBinary but with nonce set to 0
2022-10-26 16:46:41 +00:00
func ( b * Block ) SideChainHashingBlob ( ) ( buf [ ] byte , err error ) {
2023-03-07 17:57:06 +00:00
2022-10-26 16:46:41 +00:00
var txBuf [ ] byte
if txBuf , err = b . Coinbase . SideChainHashingBlob ( ) ; err != nil {
return nil , err
}
2022-10-27 08:09:39 +00:00
buf = make ( [ ] byte , 0 , 1 + 1 + binary . MaxVarintLen64 + types . HashSize + 4 + len ( txBuf ) + binary . MaxVarintLen64 + types . HashSize * len ( b . Transactions ) )
2022-10-26 16:46:41 +00:00
buf = append ( buf , b . MajorVersion )
buf = append ( buf , b . MinorVersion )
buf = binary . AppendUvarint ( buf , b . Timestamp )
buf = append ( buf , b . PreviousId [ : ] ... )
2022-10-27 08:09:39 +00:00
buf = binary . LittleEndian . AppendUint32 ( buf , 0 ) //replaced
2022-10-26 16:46:41 +00:00
buf = append ( buf , txBuf [ : ] ... )
buf = binary . AppendUvarint ( buf , uint64 ( len ( b . Transactions ) ) )
for _ , txId := range b . Transactions {
buf = append ( buf , txId [ : ] ... )
}
return buf , nil
}
func ( b * Block ) HashingBlob ( ) [ ] byte {
//TODO: cache
2022-11-03 11:32:07 +00:00
buf := b . HeaderBlob ( )
2022-10-26 16:46:41 +00:00
txTreeHash := b . TxTreeHash ( )
buf = append ( buf , txTreeHash [ : ] ... )
buf = binary . AppendUvarint ( buf , uint64 ( len ( b . Transactions ) + 1 ) )
return buf
}
func ( b * Block ) TxTreeHash ( ) ( rootHash types . Hash ) {
//TODO: cache
//transaction hashes
h := make ( [ ] byte , 0 , types . HashSize * len ( b . Transactions ) + types . HashSize )
coinbaseTxId := b . Coinbase . Id ( )
h = append ( h , coinbaseTxId [ : ] ... )
for _ , txId := range b . Transactions {
h = append ( h , txId [ : ] ... )
}
count := len ( b . Transactions ) + 1
if count == 1 {
rootHash = types . HashFromBytes ( h )
} else if count == 2 {
2022-11-08 13:25:29 +00:00
rootHash = crypto . PooledKeccak256 ( h )
2022-10-26 16:46:41 +00:00
} else {
2022-11-07 22:59:52 +00:00
hashInstance := crypto . GetKeccak256Hasher ( )
defer crypto . PutKeccak256Hasher ( hashInstance )
2022-10-26 16:46:41 +00:00
var cnt int
{
//TODO: expand this loop properly
//find closest low power of two
for cnt = 1 ; cnt <= count ; cnt <<= 1 {
}
cnt >>= 1
}
ints := make ( [ ] byte , cnt * types . HashSize )
copy ( ints , h [ : ( cnt * 2 - count ) * types . HashSize ] )
{
i := cnt * 2 - count
j := cnt * 2 - count
for j < cnt {
2022-11-07 22:59:52 +00:00
keccakl ( hashInstance , ints [ j * types . HashSize : ] , h [ i * types . HashSize : ] , types . HashSize * 2 )
2022-10-26 16:46:41 +00:00
i += 2
j ++
}
}
for cnt > 2 {
cnt >>= 1
{
i := 0
j := 0
for j < cnt {
2022-11-07 22:59:52 +00:00
keccakl ( hashInstance , ints [ j * types . HashSize : ] , ints [ i * types . HashSize : ] , types . HashSize * 2 )
2022-10-26 16:46:41 +00:00
i += 2
j ++
}
}
}
2022-11-07 22:59:52 +00:00
keccakl ( hashInstance , rootHash [ : ] , ints , types . HashSize * 2 )
2022-10-26 16:46:41 +00:00
}
return
}
2023-03-05 14:06:49 +00:00
func ( b * Block ) Difficulty ( f GetDifficultyByHeightFunc ) types . Difficulty {
2022-10-28 09:47:14 +00:00
//cached by sidechain.Share
2023-03-05 14:06:49 +00:00
return f ( b . Coinbase . GenHeight )
2022-10-28 09:47:14 +00:00
}
2023-03-05 14:06:49 +00:00
func ( b * Block ) PowHash ( f GetSeedByHeightFunc ) types . Hash {
2022-10-28 09:47:14 +00:00
//cached by sidechain.Share
2023-03-05 14:06:49 +00:00
h , _ := b . PowHashWithError ( f )
2022-10-26 16:46:41 +00:00
return h
}
2023-03-05 14:06:49 +00:00
func ( b * Block ) PowHashWithError ( f GetSeedByHeightFunc ) ( types . Hash , error ) {
2022-11-03 11:32:07 +00:00
//not cached
2023-03-05 14:06:49 +00:00
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 ( ) )
}
2022-11-03 11:32:07 +00:00
}
2022-10-26 16:46:41 +00:00
func ( b * Block ) Id ( ) types . Hash {
2022-10-28 09:47:14 +00:00
//cached by sidechain.Share
2022-10-26 16:46:41 +00:00
buf := b . HashingBlob ( )
2022-11-07 22:59:52 +00:00
return crypto . PooledKeccak256 ( binary . AppendUvarint ( nil , uint64 ( len ( buf ) ) ) , buf )
2022-10-26 16:46:41 +00:00
}
var hasher = randomx . NewRandomX ( )
2022-11-07 22:59:52 +00:00
func keccakl ( hasher hash . Hash , dst [ ] byte , data [ ] byte , len int ) {
hasher . Reset ( )
hasher . Write ( data [ : len ] )
crypto . HashFastSum ( hasher , dst )
2022-10-26 16:46:41 +00:00
}