git.gammaspectra.live/P2Pool/consensus@v0.0.0-20240403173234-a039820b20c9/monero/block/block.go (about) 1 package block 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "git.gammaspectra.live/P2Pool/consensus/monero" 9 "git.gammaspectra.live/P2Pool/consensus/monero/crypto" 10 "git.gammaspectra.live/P2Pool/consensus/monero/randomx" 11 "git.gammaspectra.live/P2Pool/consensus/monero/transaction" 12 "git.gammaspectra.live/P2Pool/consensus/types" 13 "git.gammaspectra.live/P2Pool/consensus/utils" 14 "io" 15 ) 16 17 type Block struct { 18 MajorVersion uint8 `json:"major_version"` 19 MinorVersion uint8 `json:"minor_version"` 20 // Nonce re-arranged here to improve memory layout space 21 Nonce uint32 `json:"nonce"` 22 23 Timestamp uint64 `json:"timestamp"` 24 PreviousId types.Hash `json:"previous_id"` 25 //Nonce would be here 26 27 Coinbase transaction.CoinbaseTransaction `json:"coinbase"` 28 29 Transactions []types.Hash `json:"transactions,omitempty"` 30 // 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. 31 TransactionParentIndices []uint64 `json:"transaction_parent_indices,omitempty"` 32 } 33 34 type Header struct { 35 MajorVersion uint8 `json:"major_version"` 36 MinorVersion uint8 `json:"minor_version"` 37 // Nonce re-arranged here to improve memory layout space 38 Nonce uint32 `json:"nonce"` 39 40 Timestamp uint64 `json:"timestamp"` 41 PreviousId types.Hash `json:"previous_id"` 42 Height uint64 `json:"height"` 43 //Nonce would be here 44 Reward uint64 `json:"reward"` 45 Difficulty types.Difficulty `json:"difficulty"` 46 Id types.Hash `json:"id"` 47 } 48 49 func (b *Block) MarshalBinary() (buf []byte, err error) { 50 return b.MarshalBinaryFlags(false, false) 51 } 52 53 func (b *Block) BufferLength() int { 54 return 1 + 1 + 55 utils.UVarInt64Size(b.Timestamp) + 56 types.HashSize + 57 4 + 58 b.Coinbase.BufferLength() + 59 utils.UVarInt64Size(len(b.Transactions)) + types.HashSize*len(b.Transactions) 60 } 61 62 func (b *Block) MarshalBinaryFlags(pruned, compact bool) (buf []byte, err error) { 63 return b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), pruned, compact) 64 } 65 66 func (b *Block) AppendBinaryFlags(preAllocatedBuf []byte, pruned, compact bool) (buf []byte, err error) { 67 buf = preAllocatedBuf 68 buf = append(buf, b.MajorVersion) 69 if b.MajorVersion > monero.HardForkSupportedVersion { 70 return nil, fmt.Errorf("unsupported version %d", b.MajorVersion) 71 } 72 buf = append(buf, b.MinorVersion) 73 if b.MinorVersion < b.MajorVersion { 74 return nil, fmt.Errorf("minor version %d smaller than major %d", b.MinorVersion, b.MajorVersion) 75 } 76 buf = binary.AppendUvarint(buf, b.Timestamp) 77 buf = append(buf, b.PreviousId[:]...) 78 buf = binary.LittleEndian.AppendUint32(buf, b.Nonce) 79 80 if buf, err = b.Coinbase.AppendBinaryFlags(buf, pruned); err != nil { 81 return nil, err 82 } 83 84 buf = binary.AppendUvarint(buf, uint64(len(b.Transactions))) 85 if compact { 86 for i, txId := range b.Transactions { 87 if i < len(b.TransactionParentIndices) && b.TransactionParentIndices[i] != 0 { 88 buf = binary.AppendUvarint(buf, b.TransactionParentIndices[i]) 89 } else { 90 buf = binary.AppendUvarint(buf, 0) 91 buf = append(buf, txId[:]...) 92 } 93 } 94 } else { 95 for _, txId := range b.Transactions { 96 buf = append(buf, txId[:]...) 97 } 98 } 99 100 return buf, nil 101 } 102 103 func (b *Block) FromReader(reader utils.ReaderAndByteReader) (err error) { 104 return b.FromReaderFlags(reader, false) 105 } 106 107 func (b *Block) FromCompactReader(reader utils.ReaderAndByteReader) (err error) { 108 return b.FromReaderFlags(reader, true) 109 } 110 111 func (b *Block) UnmarshalBinary(data []byte) error { 112 reader := bytes.NewReader(data) 113 return b.FromReader(reader) 114 } 115 116 func (b *Block) FromReaderFlags(reader utils.ReaderAndByteReader, compact bool) (err error) { 117 var ( 118 txCount uint64 119 transactionHash types.Hash 120 ) 121 122 if b.MajorVersion, err = reader.ReadByte(); err != nil { 123 return err 124 } 125 if b.MinorVersion, err = reader.ReadByte(); err != nil { 126 return err 127 } 128 129 if b.Timestamp, err = binary.ReadUvarint(reader); err != nil { 130 return err 131 } 132 133 if _, err = io.ReadFull(reader, b.PreviousId[:]); err != nil { 134 return err 135 } 136 137 if err = binary.Read(reader, binary.LittleEndian, &b.Nonce); err != nil { 138 return err 139 } 140 141 // Coinbase Tx Decoding 142 { 143 if err = b.Coinbase.FromReader(reader); err != nil { 144 return err 145 } 146 } 147 148 //TODO: verify hardfork major versions 149 150 if txCount, err = binary.ReadUvarint(reader); err != nil { 151 return err 152 } 153 154 if compact { 155 if txCount < 8192 { 156 b.Transactions = make([]types.Hash, 0, txCount) 157 b.TransactionParentIndices = make([]uint64, 0, txCount) 158 } 159 160 var parentIndex uint64 161 for i := 0; i < int(txCount); i++ { 162 if parentIndex, err = binary.ReadUvarint(reader); err != nil { 163 return err 164 } 165 166 if parentIndex == 0 { 167 //not in lookup 168 if _, err = io.ReadFull(reader, transactionHash[:]); err != nil { 169 return err 170 } 171 172 b.Transactions = append(b.Transactions, transactionHash) 173 } else { 174 b.Transactions = append(b.Transactions, types.ZeroHash) 175 } 176 177 b.TransactionParentIndices = append(b.TransactionParentIndices, parentIndex) 178 } 179 } else { 180 if txCount < 8192 { 181 b.Transactions = make([]types.Hash, 0, txCount) 182 } 183 184 for i := 0; i < int(txCount); i++ { 185 if _, err = io.ReadFull(reader, transactionHash[:]); err != nil { 186 return err 187 } 188 b.Transactions = append(b.Transactions, transactionHash) 189 } 190 } 191 192 return nil 193 } 194 195 func (b *Block) Header() *Header { 196 return &Header{ 197 MajorVersion: b.MajorVersion, 198 MinorVersion: b.MinorVersion, 199 Timestamp: b.Timestamp, 200 PreviousId: b.PreviousId, 201 Height: b.Coinbase.GenHeight, 202 Nonce: b.Nonce, 203 Reward: b.Coinbase.TotalReward, 204 Id: b.Id(), 205 Difficulty: types.ZeroDifficulty, 206 } 207 } 208 209 func (b *Block) HeaderBlobBufferLength() int { 210 return 1 + 1 + 211 utils.UVarInt64Size(b.Timestamp) + 212 types.HashSize + 213 4 214 } 215 216 func (b *Block) HeaderBlob(preAllocatedBuf []byte) []byte { 217 buf := preAllocatedBuf 218 buf = append(buf, b.MajorVersion) 219 buf = append(buf, b.MinorVersion) 220 buf = binary.AppendUvarint(buf, b.Timestamp) 221 buf = append(buf, b.PreviousId[:]...) 222 buf = binary.LittleEndian.AppendUint32(buf, b.Nonce) 223 224 return buf 225 } 226 227 // SideChainHashingBlob Same as MarshalBinary but with nonce or template id set to 0 228 func (b *Block) SideChainHashingBlob(preAllocatedBuf []byte, zeroTemplateId bool) (buf []byte, err error) { 229 buf = preAllocatedBuf 230 buf = append(buf, b.MajorVersion) 231 buf = append(buf, b.MinorVersion) 232 buf = binary.AppendUvarint(buf, b.Timestamp) 233 buf = append(buf, b.PreviousId[:]...) 234 buf = binary.LittleEndian.AppendUint32(buf, 0) //replaced 235 236 if buf, err = b.Coinbase.SideChainHashingBlob(buf, zeroTemplateId); err != nil { 237 return nil, err 238 } 239 240 buf = binary.AppendUvarint(buf, uint64(len(b.Transactions))) 241 for _, txId := range b.Transactions { 242 buf = append(buf, txId[:]...) 243 } 244 245 return buf, nil 246 } 247 248 func (b *Block) HashingBlobBufferLength() int { 249 return b.HeaderBlobBufferLength() + 250 types.HashSize + utils.UVarInt64Size(len(b.Transactions)+1) 251 } 252 253 func (b *Block) HashingBlob(preAllocatedBuf []byte) []byte { 254 buf := b.HeaderBlob(preAllocatedBuf) 255 256 merkleTree := make(crypto.BinaryTreeHash, len(b.Transactions)+1) 257 //TODO: cache? 258 merkleTree[0] = b.Coinbase.CalculateId() 259 copy(merkleTree[1:], b.Transactions) 260 txTreeHash := merkleTree.RootHash() 261 buf = append(buf, txTreeHash[:]...) 262 263 buf = binary.AppendUvarint(buf, uint64(len(b.Transactions)+1)) 264 265 return buf 266 } 267 268 func (b *Block) Difficulty(f GetDifficultyByHeightFunc) types.Difficulty { 269 //cached by sidechain.Share 270 return f(b.Coinbase.GenHeight) 271 } 272 273 func (b *Block) PowHashWithError(hasher randomx.Hasher, f GetSeedByHeightFunc) (types.Hash, error) { 274 //not cached 275 if seed := f(b.Coinbase.GenHeight); seed == types.ZeroHash { 276 return types.ZeroHash, errors.New("could not get seed") 277 } else { 278 return hasher.Hash(seed[:], b.HashingBlob(make([]byte, 0, b.HashingBlobBufferLength()))) 279 } 280 } 281 282 func (b *Block) Id() types.Hash { 283 //cached by sidechain.Share 284 var varIntBuf [binary.MaxVarintLen64]byte 285 buf := b.HashingBlob(make([]byte, 0, b.HashingBlobBufferLength())) 286 return crypto.PooledKeccak256(varIntBuf[:binary.PutUvarint(varIntBuf[:], uint64(len(buf)))], buf) 287 }