github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/chain/block_check.go (about) 1 package chain 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "time" 9 10 "github.com/piotrnar/gocoin/lib/btc" 11 "github.com/piotrnar/gocoin/lib/script" 12 ) 13 14 // Make sure to call this function with ch.BlockIndexAccess locked 15 func (ch *Chain) PreCheckBlock(bl *btc.Block) (dos bool, maybelater bool, er error) { 16 // Size limits 17 if len(bl.Raw) < 80 { 18 er = errors.New("CheckBlock() : size limits failed - RPC_Result:bad-blk-length") 19 dos = true 20 return 21 } 22 23 ver := bl.Version() 24 if ver == 0 { 25 er = errors.New("CheckBlock() : Block version 0 not allowed - RPC_Result:bad-version") 26 dos = true 27 return 28 } 29 30 // Check proof-of-work 31 if !btc.CheckProofOfWork(bl.Hash, bl.Bits()) { 32 er = errors.New("CheckBlock() : proof of work failed - RPC_Result:high-hash") 33 dos = true 34 return 35 } 36 37 // Check timestamp (must not be higher than now +2 hours) 38 if int64(bl.BlockTime()) > time.Now().Unix()+2*60*60 { 39 er = errors.New("CheckBlock() : block timestamp too far in the future - RPC_Result:time-too-new") 40 dos = true 41 return 42 } 43 44 if prv, pres := ch.BlockIndex[bl.Hash.BIdx()]; pres { 45 if prv.Parent == nil { 46 // This is genesis block 47 er = errors.New("Genesis") 48 return 49 } else { 50 er = errors.New("CheckBlock: " + bl.Hash.String() + " already in - RPC_Result:duplicate") 51 return 52 } 53 } 54 55 prevblk, ok := ch.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()] 56 if !ok { 57 er = errors.New("CheckBlock: " + bl.Hash.String() + " parent not found - RPC_Result:bad-prevblk") 58 maybelater = true 59 return 60 } 61 62 bl.Height = prevblk.Height + 1 63 64 // Reject the block if it reaches into the chain deeper than our unwind buffer 65 lst_now := ch.LastBlock() 66 if prevblk != lst_now && int(lst_now.Height)-int(bl.Height) >= MovingCheckopintDepth { 67 er = errors.New(fmt.Sprint("CheckBlock: btc.Block ", bl.Hash.String(), 68 " hooks too deep into the chain: ", bl.Height, "/", lst_now.Height, " ", 69 btc.NewUint256(bl.ParentHash()).String(), " - RPC_Result:bad-prevblk")) 70 return 71 } 72 73 // Check proof of work 74 gnwr := ch.GetNextWorkRequired(prevblk, bl.BlockTime()) 75 if bl.Bits() != gnwr { 76 er = errors.New("CheckBlock: incorrect proof of work - RPC_Result:bad-diffbits") 77 dos = true 78 return 79 } 80 81 // Check timestamp against prev 82 bl.MedianPastTime = prevblk.GetMedianTimePast() 83 if bl.BlockTime() <= bl.MedianPastTime { 84 er = errors.New("CheckBlock: block's timestamp is too early - RPC_Result:time-too-old") 85 dos = true 86 return 87 } 88 89 if ver < 2 && bl.Height >= ch.Consensus.BIP34Height || 90 ver < 3 && bl.Height >= ch.Consensus.BIP66Height || 91 ver < 4 && bl.Height >= ch.Consensus.BIP65Height { 92 // bad block version 93 erstr := fmt.Sprintf("0x%08x", ver) 94 er = errors.New("CheckBlock() : Rejected Version=" + erstr + " block - RPC_Result:bad-version(" + erstr + ")") 95 dos = true 96 return 97 } 98 99 return 100 } 101 102 func (ch *Chain) GetBlockFlags(block_height uint32, block_time uint32) (flags uint32) { 103 if block_time == 0 || block_time >= BIP16SwitchTime { 104 flags = script.VER_P2SH 105 } 106 107 if block_height >= ch.Consensus.BIP66Height { 108 flags |= script.VER_DERSIG 109 } 110 111 if block_height >= ch.Consensus.BIP65Height { 112 flags |= script.VER_CLTV 113 } 114 115 if ch.Consensus.Enforce_CSV != 0 && block_height >= ch.Consensus.Enforce_CSV { 116 flags |= script.VER_CSV 117 } 118 119 if ch.Consensus.Enforce_SEGWIT != 0 && block_height >= ch.Consensus.Enforce_SEGWIT { 120 flags |= script.VER_WITNESS | script.VER_NULLDUMMY 121 } 122 123 if ch.Consensus.Enforce_Taproot != 0 && block_height >= ch.Consensus.Enforce_Taproot { 124 flags |= script.VER_TAPROOT 125 } 126 127 return 128 } 129 130 func (ch *Chain) ApplyBlockFlags(bl *btc.Block) { 131 bl.VerifyFlags = ch.GetBlockFlags(bl.Height, bl.BlockTime()) 132 } 133 134 func (ch *Chain) PostCheckBlock(bl *btc.Block) (er error) { 135 // Size limits 136 if len(bl.Raw) < 81 { 137 er = errors.New("CheckBlock() : size limits failed low - RPC_Result:bad-blk-length") 138 return 139 } 140 141 if bl.Txs == nil { 142 er = bl.BuildTxList() 143 if er != nil { 144 return 145 } 146 if bl.BlockWeight > btc.MAX_BLOCK_WEIGHT { 147 er = errors.New("CheckBlock() : weight limits failed - RPC_Result:bad-blk-weight") 148 return 149 } 150 //fmt.Println("New block", bl.Height, " Weight:", bl.BlockWeight, " Raw:", len(bl.Raw)) 151 } 152 153 if !bl.Trusted.Get() { 154 // We need to be satoshi compatible 155 if len(bl.Txs) == 0 || bl.Txs[0] == nil || !bl.Txs[0].IsCoinBase() { 156 er = errors.New("CheckBlock() : first tx is not coinbase: " + bl.Hash.String() + " - RPC_Result:bad-cb-missing") 157 return 158 } 159 160 // Enforce rule that the coinbase starts with serialized block height 161 if bl.Height >= ch.Consensus.BIP34Height { 162 var exp [6]byte 163 var exp_len int 164 binary.LittleEndian.PutUint32(exp[1:5], bl.Height) 165 for exp_len = 5; exp_len > 1; exp_len-- { 166 if exp[exp_len] != 0 || exp[exp_len-1] >= 0x80 { 167 break 168 } 169 } 170 exp[0] = byte(exp_len) 171 exp_len++ 172 173 if !bytes.HasPrefix(bl.Txs[0].TxIn[0].ScriptSig, exp[:exp_len]) { 174 er = errors.New("CheckBlock() : Unexpected block number in coinbase: " + bl.Hash.String() + " - RPC_Result:bad-cb-height") 175 return 176 } 177 } 178 179 // And again... 180 for i := 1; i < len(bl.Txs); i++ { 181 if bl.Txs[i].IsCoinBase() { 182 er = errors.New("CheckBlock() : more than one coinbase: " + bl.Hash.String() + " - RPC_Result:bad-cb-multiple") 183 return 184 } 185 } 186 } 187 188 // Check Merkle Root, even for trusted blocks - that's important, as they may come from untrusted peers 189 merkle, mutated := bl.GetMerkle() 190 if mutated { 191 er = errors.New("CheckBlock(): duplicate transaction - RPC_Result:bad-txns-duplicate") 192 return 193 } 194 195 if !bytes.Equal(merkle, bl.MerkleRoot()) { 196 er = errors.New("CheckBlock() : Merkle Root mismatch - RPC_Result:bad-txnmrklroot") 197 return 198 } 199 200 ch.ApplyBlockFlags(bl) 201 202 if !bl.Trusted.Get() { 203 var blockTime uint32 204 var had_witness bool 205 206 if (bl.VerifyFlags & script.VER_CSV) != 0 { 207 blockTime = bl.MedianPastTime 208 } else { 209 blockTime = bl.BlockTime() 210 } 211 212 // Verify merkle root of witness data 213 if (bl.VerifyFlags & script.VER_WITNESS) != 0 { 214 var i int 215 for i = len(bl.Txs[0].TxOut) - 1; i >= 0; i-- { 216 o := bl.Txs[0].TxOut[i] 217 if len(o.Pk_script) >= 38 && bytes.Equal(o.Pk_script[:6], []byte{0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed}) { 218 if len(bl.Txs[0].SegWit) != 1 || len(bl.Txs[0].SegWit[0]) != 1 || len(bl.Txs[0].SegWit[0][0]) != 32 { 219 er = errors.New("CheckBlock() : invalid witness nonce size - RPC_Result:bad-witness-nonce-size") 220 println(er.Error()) 221 println(bl.Hash.String(), len(bl.Txs[0].SegWit)) 222 return 223 } 224 225 // The malleation check is ignored; as the transaction tree itself 226 // already does not permit it, it is impossible to trigger in the 227 // witness tree. 228 merkle, _ := btc.GetWitnessMerkle(bl.Txs) 229 with_nonce := btc.Sha2Sum(append(merkle, bl.Txs[0].SegWit[0][0]...)) 230 231 if !bytes.Equal(with_nonce[:], o.Pk_script[6:38]) { 232 er = errors.New("CheckBlock(): Witness Merkle mismatch - RPC_Result:bad-witness-merkle-match") 233 return 234 } 235 236 had_witness = true 237 break 238 } 239 } 240 } 241 242 if !had_witness { 243 for _, t := range bl.Txs { 244 if t.SegWit != nil { 245 er = errors.New("CheckBlock(): unexpected witness data found - RPC_Result:unexpected-witness") 246 return 247 } 248 } 249 } 250 251 // Check transactions - this is the most time consuming task 252 er = CheckTransactions(bl.Txs, bl.Height, blockTime) 253 } 254 return 255 } 256 257 func (ch *Chain) CheckBlock(bl *btc.Block) (dos bool, maybelater bool, er error) { 258 dos, maybelater, er = ch.PreCheckBlock(bl) 259 if er == nil { 260 er = ch.PostCheckBlock(bl) 261 if er != nil { // all post-check errors are DoS kind 262 dos = true 263 } 264 } 265 return 266 }