github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/btc/block.go (about) 1 package btc 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "sync" 8 9 "github.com/piotrnar/gocoin/lib/others/sys" 10 ) 11 12 type Block struct { 13 Raw []byte 14 Hash *Uint256 15 Txs []*Tx 16 TxCount, TxOffset int // Number of transactions and byte offset to the first one 17 Trusted sys.SyncBool // if the block is trusted, we do not check signatures and some other things... 18 LastKnownHeight uint32 19 20 BlockExtraInfo // If we cache block on disk (between downloading and comitting), this data has to be preserved 21 22 MedianPastTime uint32 // Set in PreCheckBlock() .. last used in PostCheckBlock() 23 24 // These flags are set in BuildTxList() used later (e.g. by script.VerifyTxScript): 25 NoWitnessSize int 26 BlockWeight uint 27 PaidTxsVSize uint 28 TotalInputs int 29 30 OrbTxCnt uint 31 OrbTxSize uint 32 OrbTxWeight uint 33 34 NoWitnessData []byte // This is set by BuildNoWitnessData() 35 } 36 37 type BlockExtraInfo struct { 38 VerifyFlags uint32 39 Height uint32 40 } 41 42 func NewBlockX(data []byte, hash *Uint256) (bl *Block, er error) { 43 bl = new(Block) 44 bl.Hash = hash 45 er = bl.UpdateContent(data) 46 return 47 } 48 49 // tha data may contain only the header (80 bytes) 50 func NewBlock(data []byte) (bl *Block, er error) { 51 if data == nil { 52 er = errors.New("nil pointer") 53 return 54 } 55 return NewBlockX(data, NewSha2Hash(data[:80])) 56 } 57 58 // tha data may contain only the header (80 bytes) 59 func (bl *Block) UpdateContent(data []byte) error { 60 if len(data) < 80 { 61 return errors.New("Block too short") 62 } 63 bl.Raw = data 64 if len(data) > 80 { 65 bl.TxCount, bl.TxOffset = VLen(data[80:]) 66 if bl.TxOffset == 0 { 67 return errors.New("block's txn_count field corrupt - RPC_Result:bad-blk-length") 68 } 69 bl.TxOffset += 80 70 } 71 return nil 72 } 73 74 func (bl *Block) Version() uint32 { 75 return binary.LittleEndian.Uint32(bl.Raw[0:4]) 76 } 77 78 func (bl *Block) ParentHash() []byte { 79 return bl.Raw[4:36] 80 } 81 82 func (bl *Block) MerkleRoot() []byte { 83 return bl.Raw[36:68] 84 } 85 86 func (bl *Block) BlockTime() uint32 { 87 return binary.LittleEndian.Uint32(bl.Raw[68:72]) 88 } 89 90 func (bl *Block) Bits() uint32 { 91 return binary.LittleEndian.Uint32(bl.Raw[72:76]) 92 } 93 94 // BuildTxListExt parses a block's transactions and adds them to the structure. 95 // 96 // dohash - set it to false if you do not ened TxIDs (it is much faster then) 97 // 98 // returns error if block data inconsistent 99 func (bl *Block) BuildTxListExt(dohash bool) (e error) { 100 if bl.TxCount == 0 { 101 bl.TxCount, bl.TxOffset = VLen(bl.Raw[80:]) 102 if bl.TxCount == 0 || bl.TxOffset == 0 { 103 e = errors.New("block's txn_count field corrupt - RPC_Result:bad-blk-length") 104 return 105 } 106 bl.TxOffset += 80 107 } 108 bl.Txs = make([]*Tx, bl.TxCount) 109 110 // It would be more elegant to use bytes.Reader here, but this solution is ~20% faster. 111 offs := bl.TxOffset 112 113 var wg sync.WaitGroup 114 var data2hash, witness2hash []byte 115 116 bl.NoWitnessSize = 80 + VLenSize(uint64(bl.TxCount)) 117 bl.BlockWeight = 4 * uint(bl.NoWitnessSize) 118 119 for i := 0; i < bl.TxCount; i++ { 120 var n int 121 bl.Txs[i], n = NewTx(bl.Raw[offs:]) 122 if bl.Txs[i] == nil || n == 0 { 123 e = errors.New("NewTx failed") 124 bl.Txs = bl.Txs[:i] // make sure we don't leave any nil pointers in bl.Txs 125 break 126 } 127 bl.Txs[i].Raw = bl.Raw[offs : offs+n] 128 bl.Txs[i].Size = uint32(n) 129 if i == 0 { 130 for _, ou := range bl.Txs[0].TxOut { 131 ou.WasCoinbase = true 132 } 133 } else { 134 // Coinbase tx does not have an input 135 bl.TotalInputs += len(bl.Txs[i].TxIn) 136 } 137 if bl.Txs[i].SegWit != nil { 138 data2hash = bl.Txs[i].Serialize() 139 bl.Txs[i].NoWitSize = uint32(len(data2hash)) 140 if i > 0 { 141 witness2hash = bl.Txs[i].Raw 142 } 143 } else { 144 data2hash = bl.Txs[i].Raw 145 bl.Txs[i].NoWitSize = bl.Txs[i].Size 146 witness2hash = nil 147 } 148 weight := uint(3*bl.Txs[i].NoWitSize + bl.Txs[i].Size) 149 bl.BlockWeight += weight 150 if i > 0 { 151 bl.PaidTxsVSize += uint(bl.Txs[i].VSize()) 152 } 153 bl.NoWitnessSize += len(data2hash) 154 if i != 0 { 155 if yes, _ := bl.Txs[i].ContainsOrdFile(true); yes { 156 bl.OrbTxCnt++ 157 bl.OrbTxSize += uint(n) 158 bl.OrbTxWeight += weight 159 } 160 } 161 162 if dohash { 163 wg.Add(1) 164 go func(tx *Tx, b, w []byte) { 165 tx.Hash.Calc(b) // Calculate tx hash in a background 166 if w != nil { 167 tx.wTxID.Calc(w) 168 } 169 wg.Done() 170 }(bl.Txs[i], data2hash, witness2hash) 171 } 172 offs += n 173 } 174 175 wg.Wait() 176 177 return 178 } 179 180 // BuildTxList parses a block's transactions and adds them to the structure, always calculating TX IDs. 181 func (bl *Block) BuildTxList() (e error) { 182 return bl.BuildTxListExt(true) 183 } 184 185 // The block data in non-segwit format 186 func (bl *Block) BuildNoWitnessData() (e error) { 187 if bl.TxCount == 0 { 188 e = bl.BuildTxList() 189 if e != nil { 190 return 191 } 192 } 193 old_format_block := new(bytes.Buffer) 194 old_format_block.Write(bl.Raw[:80]) 195 WriteVlen(old_format_block, uint64(bl.TxCount)) 196 for _, tx := range bl.Txs { 197 tx.WriteSerialized(old_format_block) 198 } 199 bl.NoWitnessData = old_format_block.Bytes() 200 if bl.NoWitnessSize == 0 { 201 bl.NoWitnessSize = len(bl.NoWitnessData) 202 } else if bl.NoWitnessSize != len(bl.NoWitnessData) { 203 panic("NoWitnessSize corrupt") 204 } 205 return 206 } 207 208 func GetBlockReward(height uint32) uint64 { 209 return 50e8 >> (height / 210000) 210 } 211 212 func (bl *Block) MerkleRootMatch() bool { 213 if bl.TxCount == 0 || len(bl.Txs) != bl.TxCount { 214 return false 215 } 216 merkle, mutated := bl.GetMerkle() 217 return !mutated && bytes.Equal(merkle, bl.MerkleRoot()) 218 } 219 220 func (bl *Block) GetMerkle() (res []byte, mutated bool) { 221 mtr := make([][32]byte, len(bl.Txs), 3*len(bl.Txs)) // make the buffer 3 times longer as we use append() inside CalcMerkle 222 for i, tx := range bl.Txs { 223 if tx == nil { 224 println("GetMerkle(): tx missing", i) 225 mutated = true 226 return 227 } 228 mtr[i] = tx.Hash.Hash 229 } 230 res, mutated = CalcMerkle(mtr) 231 return 232 }