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  }