github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/blockchain/txbuilder/txbuilder_test.go (about)

     1  package txbuilder
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"math"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/davecgh/go-spew/spew"
    12  	"golang.org/x/crypto/sha3"
    13  
    14  	"github.com/bytom/bytom/common"
    15  	"github.com/bytom/bytom/consensus"
    16  	"github.com/bytom/bytom/crypto"
    17  	"github.com/bytom/bytom/crypto/ed25519"
    18  	"github.com/bytom/bytom/crypto/ed25519/chainkd"
    19  	chainjson "github.com/bytom/bytom/encoding/json"
    20  	"github.com/bytom/bytom/errors"
    21  	"github.com/bytom/bytom/protocol/bc"
    22  	"github.com/bytom/bytom/protocol/bc/types"
    23  	"github.com/bytom/bytom/protocol/vm"
    24  	"github.com/bytom/bytom/protocol/vm/vmutil"
    25  	"github.com/bytom/bytom/testutil"
    26  )
    27  
    28  type testAction bc.AssetAmount
    29  
    30  func (t testAction) Build(ctx context.Context, b *TemplateBuilder) error {
    31  	in := types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), *t.AssetId, t.Amount, 0, nil)
    32  	tplIn := &SigningInstruction{}
    33  
    34  	err := b.AddInput(in, tplIn)
    35  	if err != nil {
    36  		return err
    37  	}
    38  	return b.AddOutput(types.NewTxOutput(*t.AssetId, t.Amount, []byte("change")))
    39  }
    40  
    41  func (t testAction) ActionType() string {
    42  	return "test-action"
    43  }
    44  
    45  func newControlProgramAction(assetAmt bc.AssetAmount, script []byte) *controlProgramAction {
    46  	return &controlProgramAction{
    47  		AssetAmount: assetAmt,
    48  		Program:     script,
    49  	}
    50  }
    51  
    52  func TestBuild(t *testing.T) {
    53  	ctx := context.Background()
    54  
    55  	assetID1 := bc.NewAssetID([32]byte{1})
    56  	assetID2 := bc.NewAssetID([32]byte{2})
    57  
    58  	actions := []Action{
    59  		newControlProgramAction(bc.AssetAmount{AssetId: &assetID2, Amount: 6}, []byte("dest")),
    60  		testAction(bc.AssetAmount{AssetId: &assetID1, Amount: 5}),
    61  	}
    62  	expiryTime := time.Now().Add(time.Minute)
    63  	got, err := Build(ctx, nil, actions, expiryTime, 0)
    64  	if err != nil {
    65  		testutil.FatalErr(t, err)
    66  	}
    67  
    68  	want := &Template{
    69  		Transaction: types.NewTx(types.TxData{
    70  			Version: 1,
    71  			Inputs: []*types.TxInput{
    72  				types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), assetID1, 5, 0, nil),
    73  			},
    74  			Outputs: []*types.TxOutput{
    75  				types.NewTxOutput(assetID2, 6, []byte("dest")),
    76  				types.NewTxOutput(assetID1, 5, []byte("change")),
    77  			},
    78  		}),
    79  		SigningInstructions: []*SigningInstruction{{
    80  			WitnessComponents: []witnessComponent{},
    81  		}},
    82  	}
    83  
    84  	if !testutil.DeepEqual(got.Transaction.TxData, want.Transaction.TxData) {
    85  		t.Errorf("got tx:\n%s\nwant tx:\n%s", spew.Sdump(got.Transaction.TxData), spew.Sdump(want.Transaction.TxData))
    86  	}
    87  
    88  	if !testutil.DeepEqual(got.SigningInstructions, want.SigningInstructions) {
    89  		t.Errorf("got signing instructions:\n\t%#v\nwant signing instructions:\n\t%#v", got.SigningInstructions, want.SigningInstructions)
    90  	}
    91  }
    92  
    93  func TestSignatureWitnessMaterialize(t *testing.T) {
    94  	privkey1, pubkey1, err := chainkd.NewXKeys(nil)
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	privkey2, pubkey2, err := chainkd.NewXKeys(nil)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  	privkey3, pubkey3, err := chainkd.NewXKeys(nil)
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	issuanceProg, _ := vmutil.P2SPMultiSigProgram([]ed25519.PublicKey{pubkey1.PublicKey(), pubkey2.PublicKey(), pubkey3.PublicKey()}, 2)
   107  	assetID := bc.ComputeAssetID(issuanceProg, 1, &bc.EmptyStringHash)
   108  	outscript := mustDecodeHex("76a914c5d128911c28776f56baaac550963f7b88501dc388c0")
   109  	unsigned := types.NewTx(types.TxData{
   110  		Version: 1,
   111  		Inputs: []*types.TxInput{
   112  			types.NewIssuanceInput([]byte{1}, 100, issuanceProg, nil, nil),
   113  		},
   114  		Outputs: []*types.TxOutput{
   115  			types.NewTxOutput(assetID, 100, outscript),
   116  		},
   117  	})
   118  
   119  	tpl := &Template{
   120  		Transaction: unsigned,
   121  	}
   122  	h := tpl.Hash(0)
   123  	builder := vmutil.NewBuilder()
   124  	builder.AddData(h.Bytes())
   125  	builder.AddOp(vm.OP_TXSIGHASH).AddOp(vm.OP_EQUAL)
   126  	prog, _ := builder.Build()
   127  	msg := sha3.Sum256(prog)
   128  	sig1 := privkey1.Sign(msg[:])
   129  	sig2 := privkey2.Sign(msg[:])
   130  	sig3 := privkey3.Sign(msg[:])
   131  	want := [][]byte{
   132  		vm.Int64Bytes(0),
   133  		sig1,
   134  		sig2,
   135  		prog,
   136  	}
   137  
   138  	// Test with more signatures than required, in correct order
   139  	tpl.SigningInstructions = []*SigningInstruction{{
   140  		WitnessComponents: []witnessComponent{
   141  			&SignatureWitness{
   142  				Quorum: 2,
   143  				Keys: []keyID{
   144  					{
   145  						XPub:           pubkey1,
   146  						DerivationPath: []chainjson.HexBytes{{0, 0, 0, 0}},
   147  					},
   148  					{
   149  						XPub:           pubkey2,
   150  						DerivationPath: []chainjson.HexBytes{{0, 0, 0, 0}},
   151  					},
   152  					{
   153  						XPub:           pubkey3,
   154  						DerivationPath: []chainjson.HexBytes{{0, 0, 0, 0}},
   155  					},
   156  				},
   157  				Program: prog,
   158  				Sigs:    []chainjson.HexBytes{sig1, sig2, sig3},
   159  			},
   160  		},
   161  	}}
   162  	err = materializeWitnesses(tpl)
   163  	if err != nil {
   164  		testutil.FatalErr(t, err)
   165  	}
   166  	got := tpl.Transaction.Inputs[0].Arguments()
   167  	if !testutil.DeepEqual(got, want) {
   168  		t.Errorf("got input witness %v, want input witness %v", got, want)
   169  	}
   170  
   171  	// Test with exact amount of signatures required, in correct order
   172  	component := tpl.SigningInstructions[0].WitnessComponents[0].(*SignatureWitness)
   173  	component.Sigs = []chainjson.HexBytes{sig1, sig2}
   174  	err = materializeWitnesses(tpl)
   175  	if err != nil {
   176  		testutil.FatalErr(t, err)
   177  	}
   178  	got = tpl.Transaction.Inputs[0].Arguments()
   179  	if !testutil.DeepEqual(got, want) {
   180  		t.Errorf("got input witness %v, want input witness %v", got, want)
   181  	}
   182  }
   183  
   184  func mustDecodeHex(str string) []byte {
   185  	data, err := hex.DecodeString(str)
   186  	if err != nil {
   187  		panic(err)
   188  	}
   189  	return data
   190  }
   191  
   192  func TestCheckBlankCheck(t *testing.T) {
   193  	cases := []struct {
   194  		tx   *types.TxData
   195  		want error
   196  	}{{
   197  		tx: &types.TxData{
   198  			Inputs: []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
   199  		},
   200  		want: ErrBlankCheck,
   201  	}, {
   202  		tx: &types.TxData{
   203  			Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
   204  			Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 3, nil)},
   205  		},
   206  		want: ErrBlankCheck,
   207  	}, {
   208  		tx: &types.TxData{
   209  			Inputs: []*types.TxInput{
   210  				types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil),
   211  				types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.NewAssetID([32]byte{1}), 5, 0, nil),
   212  			},
   213  			Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 5, nil)},
   214  		},
   215  		want: ErrBlankCheck,
   216  	}, {
   217  		tx: &types.TxData{
   218  			Inputs: []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
   219  			Outputs: []*types.TxOutput{
   220  				types.NewTxOutput(bc.AssetID{}, math.MaxInt64, nil),
   221  				types.NewTxOutput(bc.AssetID{}, 7, nil),
   222  			},
   223  		},
   224  		want: ErrBadAmount,
   225  	}, {
   226  		tx: &types.TxData{
   227  			Inputs: []*types.TxInput{
   228  				types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil),
   229  				types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, math.MaxInt64, 0, nil),
   230  			},
   231  		},
   232  		want: ErrBadAmount,
   233  	}, {
   234  		tx: &types.TxData{
   235  			Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
   236  			Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 5, nil)},
   237  		},
   238  		want: nil,
   239  	}, {
   240  		tx: &types.TxData{
   241  			Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 5, nil)},
   242  		},
   243  		want: nil,
   244  	}, {
   245  		tx: &types.TxData{
   246  			Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
   247  			Outputs: []*types.TxOutput{types.NewTxOutput(bc.NewAssetID([32]byte{1}), 5, nil)},
   248  		},
   249  		want: nil,
   250  	}}
   251  
   252  	for _, c := range cases {
   253  		got := checkBlankCheck(c.tx)
   254  		if errors.Root(got) != c.want {
   255  			t.Errorf("checkUnsafe(%+v) err = %v want %v", c.tx, errors.Root(got), c.want)
   256  		}
   257  	}
   258  }
   259  
   260  func TestCreateTxByUtxo(t *testing.T) {
   261  	xprv, xpub, err := chainkd.NewXKeys(nil)
   262  	if err != nil {
   263  		t.Fatal(err)
   264  	}
   265  
   266  	pub := xpub.PublicKey()
   267  	pubHash := crypto.Ripemd160(pub)
   268  	program, err := vmutil.P2WPKHProgram([]byte(pubHash))
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  
   273  	address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  
   278  	muxID := testutil.MustDecodeHash("1e673900965623ec3305cead5a78dfb68a34599f8bc078460f3f202256c3dfa6")
   279  	utxo := struct {
   280  		SourceID       bc.Hash
   281  		AssetID        bc.AssetID
   282  		Amount         uint64
   283  		SourcePos      uint64
   284  		ControlProgram []byte
   285  		Address        string
   286  	}{
   287  		SourceID:       muxID,
   288  		AssetID:        *consensus.BTMAssetID,
   289  		Amount:         20000000000,
   290  		SourcePos:      1,
   291  		ControlProgram: program,
   292  		Address:        address.EncodeAddress(),
   293  	}
   294  
   295  	recvProg := mustDecodeHex("00145056532ecd3621c9ce8adde5505c058610b287cf")
   296  	tx := types.NewTx(types.TxData{
   297  		Version: 1,
   298  		Inputs: []*types.TxInput{
   299  			types.NewSpendInput(nil, utxo.SourceID, utxo.AssetID, utxo.Amount, utxo.SourcePos, utxo.ControlProgram),
   300  		},
   301  		Outputs: []*types.TxOutput{
   302  			types.NewTxOutput(*consensus.BTMAssetID, 10000000000, recvProg),
   303  		},
   304  	})
   305  
   306  	tpl := &Template{
   307  		Transaction:     tx,
   308  		AllowAdditional: false,
   309  	}
   310  
   311  	h := tpl.Hash(0).Byte32()
   312  	sig := xprv.Sign(h[:])
   313  	data := []byte(pub)
   314  
   315  	// Test with more signatures than required, in correct order
   316  	tpl.SigningInstructions = []*SigningInstruction{{
   317  		WitnessComponents: []witnessComponent{
   318  			&RawTxSigWitness{
   319  				Quorum: 1,
   320  				Sigs:   []chainjson.HexBytes{sig},
   321  			},
   322  			DataWitness(data),
   323  		},
   324  	}}
   325  
   326  	if err = materializeWitnesses(tpl); err != nil {
   327  		t.Fatal(err)
   328  	}
   329  
   330  	if !testutil.DeepEqual(tx, tpl.Transaction) {
   331  		t.Errorf("tx:%v result is equal to want:%v", tx, tpl.Transaction)
   332  	}
   333  }
   334  
   335  func TestAddContractArgs(t *testing.T) {
   336  	hexXpub, err := hex.DecodeString("ba76bb52574b3f40315f2c01f1818a9072ced56e9d4b68acbef56a4d0077d08e5e34837963e4cdc54eb251aa34aad01e6ae48b140f6a2743fbb0a0abd9cf8aac")
   337  	if err != nil {
   338  		t.Fatal(err)
   339  	}
   340  
   341  	var xpub chainkd.XPub
   342  	copy(xpub[:], hexXpub)
   343  
   344  	rawTxSig := RawTxSigArgument{RootXPub: xpub, Path: []chainjson.HexBytes{{1, 1, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}}}
   345  	rawTxSigMsg, err := json.Marshal(rawTxSig)
   346  	if err != nil {
   347  		t.Fatal(err)
   348  	}
   349  
   350  	value, err := hex.DecodeString("7468697320697320612074657374")
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  	data := DataArgument{value}
   355  	dataMsg, err := json.Marshal(data)
   356  	if err != nil {
   357  		t.Fatal(err)
   358  	}
   359  
   360  	strMsg, err := json.Marshal(StrArgument{"this is a test string"})
   361  	if err != nil {
   362  		t.Fatal(err)
   363  	}
   364  
   365  	integerMsg, err := json.Marshal(IntegerArgument{100})
   366  	if err != nil {
   367  		t.Fatal(err)
   368  	}
   369  
   370  	boolMsg, err := json.Marshal(BoolArgument{true})
   371  	if err != nil {
   372  		t.Fatal(err)
   373  	}
   374  
   375  	cases := []struct {
   376  		arguments  []ContractArgument
   377  		wantResult error
   378  	}{
   379  		{
   380  			arguments: []ContractArgument{
   381  				{
   382  					Type:    "raw_tx_signature",
   383  					RawData: rawTxSigMsg,
   384  				},
   385  			},
   386  			wantResult: nil,
   387  		},
   388  		{
   389  			arguments: []ContractArgument{
   390  				{
   391  					Type:    "data",
   392  					RawData: dataMsg,
   393  				},
   394  			},
   395  			wantResult: nil,
   396  		},
   397  		{
   398  			arguments: []ContractArgument{
   399  				{
   400  					Type:    "string",
   401  					RawData: strMsg,
   402  				},
   403  			},
   404  			wantResult: nil,
   405  		},
   406  		{
   407  			arguments: []ContractArgument{
   408  				{
   409  					Type:    "integer",
   410  					RawData: integerMsg,
   411  				},
   412  			},
   413  			wantResult: nil,
   414  		},
   415  		{
   416  			arguments: []ContractArgument{
   417  				{
   418  					Type:    "boolean",
   419  					RawData: boolMsg,
   420  				},
   421  			},
   422  			wantResult: nil,
   423  		},
   424  		{
   425  			arguments: []ContractArgument{
   426  				{
   427  					Type:    "raw_tx_signature",
   428  					RawData: rawTxSigMsg,
   429  				},
   430  				{
   431  					Type:    "data",
   432  					RawData: dataMsg,
   433  				},
   434  			},
   435  			wantResult: nil,
   436  		},
   437  		{
   438  			arguments: []ContractArgument{
   439  				{
   440  					Type:    "data",
   441  					RawData: dataMsg,
   442  				},
   443  				{
   444  					Type:    "raw_tx_signature",
   445  					RawData: rawTxSigMsg,
   446  				},
   447  			},
   448  			wantResult: nil,
   449  		},
   450  		{
   451  			arguments: []ContractArgument{
   452  				{
   453  					Type:    "raw_tx_signature",
   454  					RawData: rawTxSigMsg,
   455  				},
   456  				{
   457  					Type:    "data",
   458  					RawData: dataMsg,
   459  				},
   460  				{
   461  					Type:    "string",
   462  					RawData: strMsg,
   463  				},
   464  				{
   465  					Type:    "integer",
   466  					RawData: integerMsg,
   467  				},
   468  				{
   469  					Type:    "boolean",
   470  					RawData: boolMsg,
   471  				},
   472  			},
   473  			wantResult: nil,
   474  		},
   475  		{
   476  			arguments: []ContractArgument{
   477  				{
   478  					Type:    "data",
   479  					RawData: dataMsg,
   480  				},
   481  				{
   482  					Type:    "err_data",
   483  					RawData: rawTxSigMsg,
   484  				},
   485  			},
   486  			wantResult: ErrBadContractArgType,
   487  		},
   488  	}
   489  
   490  	sigInst := &SigningInstruction{}
   491  	for _, c := range cases {
   492  		err := AddContractArgs(sigInst, c.arguments)
   493  		if err != c.wantResult {
   494  			t.Fatalf("got result=%v, want result=%v", err, c.wantResult)
   495  		}
   496  	}
   497  }