github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/script/tx_test.go (about) 1 package script 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "github.com/piotrnar/gocoin/lib/btc" 9 "io/ioutil" 10 "testing" 11 ) 12 13 type oneinp struct { 14 txid *btc.Uint256 15 vout int 16 pkscr string 17 value uint64 18 } 19 20 type testvector struct { 21 inps []oneinp 22 tx string 23 ver_flags uint32 24 skip string 25 } 26 27 var last_descr string 28 29 func (tv *testvector) String() (s string) { 30 s += fmt.Sprintf("Tx with %d inputs:\n", len(tv.inps)) 31 for i := range tv.inps { 32 s += fmt.Sprintf(" %3d) %s-%03d\n", i, tv.inps[i].txid, tv.inps[i].vout) 33 s += fmt.Sprintf(" %s\n", tv.inps[i].pkscr) 34 } 35 s += fmt.Sprintf(" tx_len:%d flags:0x%x\n", len(tv.tx), tv.ver_flags) 36 return 37 } 38 39 func parserec(vv []interface{}) (ret *testvector) { 40 ret = new(testvector) 41 for i, u := range vv[0].([]interface{}) { 42 switch uu := u.(type) { 43 case []interface{}: 44 txid := btc.NewUint256FromString(uu[0].(string)) 45 newrec := oneinp{txid: txid, vout: int(uu[1].(float64)), pkscr: uu[2].(string)} 46 if len(uu) > 3 { 47 newrec.value = uint64(uu[3].(float64)) 48 } 49 ret.inps = append(ret.inps, newrec) 50 default: 51 fmt.Printf(" - %d is of a type %T\n", i, uu) 52 } 53 } 54 ret.tx = vv[1].(string) 55 params := vv[2].(string) 56 var e error 57 ret.ver_flags, e = decode_flags(params) // deifned in script_test.go 58 if e != nil { 59 println("skip", params) 60 ret.skip = e.Error() 61 } 62 return 63 } 64 65 // Some tests from the satoshi's json files are not applicable 66 // for our architectre so lets just fake them. 67 func skip_broken_tests(tx *btc.Tx) bool { 68 // No inputs 69 if len(tx.TxIn) == 0 { 70 return true 71 } 72 73 // Negative output 74 for i := range tx.TxOut { 75 if tx.TxOut[i].Value > btc.MAX_MONEY { 76 return true 77 } 78 } 79 80 // Duplicate inputs 81 if len(tx.TxIn) > 1 { 82 for i := 0; i < len(tx.TxIn)-1; i++ { 83 for j := i + 1; j < len(tx.TxIn); j++ { 84 if tx.TxIn[i].Input == tx.TxIn[j].Input { 85 return true 86 } 87 } 88 } 89 } 90 91 // Coinbase of w wrong size 92 if tx.IsCoinBase() { 93 if len(tx.TxIn[0].ScriptSig) < 2 { 94 return true 95 } 96 if len(tx.TxIn[0].ScriptSig) > 100 { 97 return true 98 } 99 } 100 101 return false 102 } 103 104 func execute_test_tx(t *testing.T, tv *testvector) bool { 105 if len(tv.inps) == 0 { 106 t.Error("Vector has no inputs") 107 return false 108 } 109 rd, er := hex.DecodeString(tv.tx) 110 if er != nil { 111 t.Error(er.Error()) 112 return false 113 } 114 tx, _ := btc.NewTx(rd) 115 if tx == nil { 116 t.Error("Canot decode tx") 117 return false 118 } 119 tx.Size = uint32(len(rd)) 120 tx.SetHash(rd) 121 122 if skip_broken_tests(tx) { 123 return false 124 } 125 126 if !tx.IsCoinBase() { 127 for i := range tx.TxIn { 128 if tx.TxIn[i].Input.IsNull() { 129 return false 130 } 131 } 132 } 133 134 oks := 0 135 for i := range tx.TxIn { 136 var j int 137 for j = range tv.inps { 138 if bytes.Equal(tx.TxIn[i].Input.Hash[:], tv.inps[j].txid.Hash[:]) && 139 tx.TxIn[i].Input.Vout == uint32(tv.inps[j].vout) { 140 break 141 } 142 } 143 if j >= len(tv.inps) { 144 t.Error("Matching input not found") 145 continue 146 } 147 148 pk, er := btc.DecodeScript(tv.inps[j].pkscr) 149 if er != nil { 150 t.Error(er.Error()) 151 continue 152 } 153 154 if VerifyTxScript(pk, &SigChecker{Amount: tv.inps[j].value, Idx: i, Tx: tx}, tv.ver_flags) { 155 oks++ 156 } 157 } 158 return oks == len(tx.TxIn) 159 } 160 161 func TestValidTransactions(t *testing.T) { 162 var str interface{} 163 dat, er := ioutil.ReadFile("../test/tx_valid.json") 164 if er != nil { 165 println(er.Error()) 166 return 167 } 168 169 er = json.Unmarshal(dat, &str) 170 if er != nil { 171 println(er.Error()) 172 return 173 } 174 m := str.([]interface{}) 175 cnt := 0 176 for _, v := range m { 177 switch vv := v.(type) { 178 case []interface{}: 179 if len(vv) == 3 { 180 cnt++ 181 tv := parserec(vv) 182 if tv.skip != "" { 183 //println(tv.skip) 184 } else if !execute_test_tx(t, tv) { 185 t.Error(cnt, "Failed transaction:", last_descr) 186 } 187 } else if len(vv) == 1 { 188 last_descr = vv[0].(string) 189 } 190 } 191 } 192 } 193 194 func TestInvalidTransactions(t *testing.T) { 195 var str interface{} 196 dat, er := ioutil.ReadFile("../test/tx_invalid.json") 197 if er != nil { 198 println(er.Error()) 199 return 200 } 201 202 er = json.Unmarshal(dat, &str) 203 if er != nil { 204 println(er.Error()) 205 return 206 } 207 m := str.([]interface{}) 208 cnt := 0 209 for _, v := range m { 210 switch vv := v.(type) { 211 case []interface{}: 212 if len(vv) == 3 { 213 cnt++ 214 if cnt == 64000 { 215 DBG_SCR = true 216 } 217 tv := parserec(vv) 218 if tv.skip != "" { 219 //println(tv.skip) 220 } else if execute_test_tx(t, tv) { 221 t.Error(cnt, "NOT failed transaction:", last_descr) 222 return 223 } 224 last_descr = "" 225 if cnt == 64000 { 226 return 227 } 228 } else if len(vv) == 1 { 229 if last_descr == "" { 230 last_descr = vv[0].(string) 231 } else { 232 last_descr += "\n" + vv[0].(string) 233 } 234 } 235 } 236 } 237 } 238 239 func TestSighash(t *testing.T) { 240 var arr [][]interface{} 241 242 dat, er := ioutil.ReadFile("../test/sighash.json") 243 if er != nil { 244 println(er.Error()) 245 return 246 } 247 248 r := bytes.NewBuffer(dat) 249 d := json.NewDecoder(r) 250 d.UseNumber() 251 252 er = d.Decode(&arr) 253 if er != nil { 254 println(er.Error()) 255 return 256 } 257 for i := range arr { 258 if len(arr[i]) == 5 { 259 tmp, _ := hex.DecodeString(arr[i][0].(string)) 260 tx, _ := btc.NewTx(tmp) 261 if tx == nil { 262 t.Error("Cannot decode tx from text number", i) 263 continue 264 } 265 tmp, _ = hex.DecodeString(arr[i][1].(string)) // script 266 iidx, _ := arr[i][2].(json.Number).Int64() 267 htype, _ := arr[i][3].(json.Number).Int64() 268 got := tx.SignatureHash(tmp, int(iidx), int32(htype)) 269 exp := btc.NewUint256FromString(arr[i][4].(string)) 270 if !bytes.Equal(exp.Hash[:], got) { 271 t.Error("SignatureHash mismatch at index", i) 272 } 273 } 274 } 275 }