git.gammaspectra.live/P2Pool/consensus/v3@v3.8.0/p2pool/stratum/template.go (about) 1 package stratum 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" 7 "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain" 8 "git.gammaspectra.live/P2Pool/consensus/v3/types" 9 "git.gammaspectra.live/P2Pool/consensus/v3/utils" 10 "git.gammaspectra.live/P2Pool/sha3" 11 "io" 12 ) 13 14 type Template struct { 15 Buffer []byte 16 17 // NonceOffset offset of an uint32 18 NonceOffset int 19 20 CoinbaseOffset int 21 22 // ExtraNonceOffset offset of an uint32 23 ExtraNonceOffset int 24 25 // TemplateIdOffset offset of a types.Hash 26 TemplateIdOffset int 27 28 // TransactionsOffset Start of transactions section 29 TransactionsOffset int 30 31 // TemplateExtraBufferOffset offset of 4*uint32 32 TemplateExtraBufferOffset int 33 34 MainHeight uint64 35 MainParent types.Hash 36 37 SideHeight uint64 38 SideParent types.Hash 39 SideDifficulty types.Difficulty 40 41 MerkleTreeMainBranch []types.Hash 42 } 43 44 func (tpl *Template) Write(writer io.Writer, nonce, extraNonce, sideRandomNumber, sideExtraNonce uint32, templateId types.Hash) error { 45 var uint32Buf [4]byte 46 47 if _, err := writer.Write(tpl.Buffer[:tpl.NonceOffset]); err != nil { 48 return err 49 } 50 51 binary.LittleEndian.PutUint32(uint32Buf[:], nonce) 52 if _, err := writer.Write(uint32Buf[:]); err != nil { 53 return err 54 } 55 56 if _, err := writer.Write(tpl.Buffer[tpl.NonceOffset+4 : tpl.ExtraNonceOffset]); err != nil { 57 return err 58 } 59 60 binary.LittleEndian.PutUint32(uint32Buf[:], extraNonce) 61 if _, err := writer.Write(uint32Buf[:]); err != nil { 62 return err 63 } 64 65 if _, err := writer.Write(tpl.Buffer[tpl.ExtraNonceOffset+4 : tpl.TemplateIdOffset]); err != nil { 66 return err 67 } 68 if _, err := writer.Write(templateId[:]); err != nil { 69 return err 70 } 71 if _, err := writer.Write(tpl.Buffer[tpl.TemplateIdOffset+types.HashSize : tpl.TemplateExtraBufferOffset+4*2]); err != nil { 72 return err 73 } 74 75 binary.LittleEndian.PutUint32(uint32Buf[:], sideRandomNumber) 76 if _, err := writer.Write(uint32Buf[:]); err != nil { 77 return err 78 } 79 80 binary.LittleEndian.PutUint32(uint32Buf[:], sideExtraNonce) 81 if _, err := writer.Write(uint32Buf[:]); err != nil { 82 return err 83 } 84 85 return nil 86 } 87 88 func (tpl *Template) Blob(preAllocatedBuffer []byte, nonce, extraNonce, sideRandomNumber, sideExtraNonce uint32, templateId types.Hash) []byte { 89 buf := append(preAllocatedBuffer, tpl.Buffer...) 90 91 // Overwrite nonce 92 binary.LittleEndian.PutUint32(buf[tpl.NonceOffset:], nonce) 93 // Overwrite extra nonce 94 binary.LittleEndian.PutUint32(buf[tpl.ExtraNonceOffset:], extraNonce) 95 // Overwrite template id 96 copy(buf[tpl.TemplateIdOffset:], templateId[:]) 97 // Overwrite sidechain random number 98 binary.LittleEndian.PutUint32(buf[tpl.TemplateExtraBufferOffset+4*2:], sideRandomNumber) 99 // Overwrite sidechain extra nonce number 100 binary.LittleEndian.PutUint32(buf[tpl.TemplateExtraBufferOffset+4*3:], sideExtraNonce) 101 102 return buf 103 } 104 105 func (tpl *Template) TemplateId(hasher *sha3.HasherState, preAllocatedBuffer []byte, consensus *sidechain.Consensus, sideRandomNumber, sideExtraNonce uint32, result *types.Hash) { 106 buf := tpl.Blob(preAllocatedBuffer, 0, 0, sideRandomNumber, sideExtraNonce, types.ZeroHash) 107 108 _, _ = hasher.Write(buf) 109 _, _ = hasher.Write(consensus.Id[:]) 110 111 crypto.HashFastSum(hasher, (*result)[:]) 112 hasher.Reset() 113 } 114 115 func (tpl *Template) Timestamp() uint64 { 116 t, _ := binary.Uvarint(tpl.Buffer[2:]) 117 return t 118 } 119 120 func (tpl *Template) ShareVersion(consensus *sidechain.Consensus) sidechain.ShareVersion { 121 return sidechain.P2PoolShareVersion(consensus, tpl.Timestamp()) 122 } 123 124 func (tpl *Template) CoinbaseBufferLength() int { 125 return tpl.TransactionsOffset - tpl.CoinbaseOffset 126 } 127 128 func (tpl *Template) CoinbaseBlob(preAllocatedBuffer []byte, extraNonce uint32, templateId types.Hash) []byte { 129 buf := append(preAllocatedBuffer, tpl.Buffer[tpl.CoinbaseOffset:tpl.TransactionsOffset]...) 130 131 // Overwrite extra nonce 132 binary.LittleEndian.PutUint32(buf[tpl.ExtraNonceOffset-tpl.CoinbaseOffset:], extraNonce) 133 // Overwrite template id 134 copy(buf[tpl.TemplateIdOffset-tpl.CoinbaseOffset:], templateId[:]) 135 136 return buf 137 } 138 139 func (tpl *Template) CoinbaseBlobId(hasher *sha3.HasherState, preAllocatedBuffer []byte, extraNonce uint32, templateId types.Hash, result *types.Hash) { 140 141 buf := tpl.CoinbaseBlob(preAllocatedBuffer, extraNonce, templateId) 142 _, _ = hasher.Write(buf[:len(buf)-1]) 143 crypto.HashFastSum(hasher, (*result)[:]) 144 hasher.Reset() 145 146 CoinbaseIdHash(hasher, *result, result) 147 } 148 149 func (tpl *Template) CoinbaseId(hasher *sha3.HasherState, extraNonce uint32, templateId types.Hash, result *types.Hash) { 150 151 var extraNonceBuf [4]byte 152 153 _, _ = hasher.Write(tpl.Buffer[tpl.CoinbaseOffset:tpl.ExtraNonceOffset]) 154 // extra nonce 155 binary.LittleEndian.PutUint32(extraNonceBuf[:], extraNonce) 156 _, _ = hasher.Write(extraNonceBuf[:]) 157 158 _, _ = hasher.Write(tpl.Buffer[tpl.ExtraNonceOffset+4 : tpl.TemplateIdOffset]) 159 // template id 160 _, _ = hasher.Write(templateId[:]) 161 162 _, _ = hasher.Write(tpl.Buffer[tpl.TemplateIdOffset+types.HashSize : tpl.TransactionsOffset-1]) 163 164 crypto.HashFastSum(hasher, (*result)[:]) 165 hasher.Reset() 166 167 CoinbaseIdHash(hasher, *result, result) 168 } 169 170 var zeroExtraBaseRCTHash = crypto.PooledKeccak256([]byte{0}) 171 172 func CoinbaseIdHash(hasher *sha3.HasherState, coinbaseBlobMinusBaseRTC types.Hash, result *types.Hash) { 173 _, _ = hasher.Write(coinbaseBlobMinusBaseRTC[:]) 174 // Base RCT, single 0 byte in miner tx 175 _, _ = hasher.Write(zeroExtraBaseRCTHash[:]) 176 // Prunable RCT, empty in miner tx 177 _, _ = hasher.Write(types.ZeroHash[:]) 178 crypto.HashFastSum(hasher, (*result)[:]) 179 hasher.Reset() 180 } 181 182 func (tpl *Template) HashingBlobBufferLength() int { 183 _, n := binary.Uvarint(tpl.Buffer[tpl.TransactionsOffset:]) 184 185 return tpl.NonceOffset + 4 + types.HashSize + n 186 } 187 188 func (tpl *Template) HashingBlob(hasher *sha3.HasherState, preAllocatedBuffer []byte, nonce, extraNonce uint32, templateId types.Hash) []byte { 189 190 var rootHash types.Hash 191 tpl.CoinbaseId(hasher, extraNonce, templateId, &rootHash) 192 193 buf := append(preAllocatedBuffer, tpl.Buffer[:tpl.NonceOffset]...) 194 buf = binary.LittleEndian.AppendUint32(buf, nonce) 195 196 numTransactions, n := binary.Uvarint(tpl.Buffer[tpl.TransactionsOffset:]) 197 198 if numTransactions < 1 { 199 } else if numTransactions < 2 { 200 _, _ = hasher.Write(rootHash[:]) 201 _, _ = hasher.Write(tpl.Buffer[tpl.TransactionsOffset+n : tpl.TransactionsOffset+n+types.HashSize]) 202 crypto.HashFastSum(hasher, rootHash[:]) 203 hasher.Reset() 204 } else { 205 for i := range tpl.MerkleTreeMainBranch { 206 _, _ = hasher.Write(rootHash[:]) 207 _, _ = hasher.Write(tpl.MerkleTreeMainBranch[i][:]) 208 crypto.HashFastSum(hasher, rootHash[:]) 209 hasher.Reset() 210 } 211 } 212 213 buf = append(buf, rootHash[:]...) 214 buf = binary.AppendUvarint(buf, numTransactions+1) 215 return buf 216 } 217 218 func TemplateFromPoolBlock(b *sidechain.PoolBlock) (tpl *Template, err error) { 219 if b.ShareVersion() < sidechain.ShareVersion_V1 || b.ShareVersion() > sidechain.ShareVersion_V2 { 220 return nil, errors.New("unsupported share version") 221 } 222 totalLen := b.BufferLength() 223 buf := make([]byte, 0, b.BufferLength()) 224 if buf, err = b.AppendBinaryFlags(buf, false, false); err != nil { 225 return nil, err 226 } 227 228 tpl = &Template{ 229 Buffer: buf, 230 } 231 232 mainBufferLength := b.Main.BufferLength() 233 coinbaseLength := b.Main.Coinbase.BufferLength() 234 tpl.NonceOffset = mainBufferLength - (4 + coinbaseLength + utils.UVarInt64Size(len(b.Main.Transactions)) + types.HashSize*len(b.Main.Transactions)) 235 236 tpl.CoinbaseOffset = mainBufferLength - (coinbaseLength + utils.UVarInt64Size(len(b.Main.Transactions)) + types.HashSize*len(b.Main.Transactions)) 237 238 tpl.TransactionsOffset = mainBufferLength - (utils.UVarInt64Size(len(b.Main.Transactions)) + types.HashSize*len(b.Main.Transactions)) 239 240 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) 241 242 tpl.TemplateIdOffset = tpl.NonceOffset + 4 + (coinbaseLength - (b.Main.Coinbase.Extra[2].BufferLength() + 1)) + 1 + utils.UVarInt64Size(b.Main.Coinbase.Extra[2].VarInt) 243 tpl.TemplateExtraBufferOffset = totalLen - 4*4 244 245 // Set places to zeroes where necessary 246 tpl.Buffer = tpl.Blob(make([]byte, 0, len(tpl.Buffer)), 0, 0, 0, 0, types.ZeroHash) 247 248 if len(b.Main.Transactions) > 1 { 249 merkleTree := make(crypto.BinaryTreeHash, len(b.Main.Transactions)+1) 250 copy(merkleTree[1:], b.Main.Transactions) 251 tpl.MerkleTreeMainBranch = merkleTree.MainBranch() 252 } 253 254 tpl.MainHeight = b.Main.Coinbase.GenHeight 255 tpl.MainParent = b.Main.PreviousId 256 257 tpl.SideHeight = b.Side.Height 258 tpl.SideParent = b.Side.Parent 259 tpl.SideDifficulty = b.Side.Difficulty 260 261 return tpl, nil 262 }