github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/blockchain/txbuilder/txbuilder_test.go (about) 1 package txbuilder 2 3 import ( 4 "context" 5 "crypto/ed25519" 6 "encoding/hex" 7 "encoding/json" 8 "math" 9 "testing" 10 "time" 11 12 "github.com/davecgh/go-spew/spew" 13 "golang.org/x/crypto/sha3" 14 15 "github.com/bytom/bytom/common" 16 "github.com/bytom/bytom/consensus" 17 "github.com/bytom/bytom/crypto" 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, nil) 32 tplIn := &SigningInstruction{} 33 34 err := b.AddInput(in, tplIn) 35 if err != nil { 36 return err 37 } 38 return b.AddOutput(types.NewOriginalTxOutput(*t.AssetId, t.Amount, []byte("change"), nil)) 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, nil), 73 }, 74 Outputs: []*types.TxOutput{ 75 types.NewOriginalTxOutput(assetID2, 6, []byte("dest"), nil), 76 types.NewOriginalTxOutput(assetID1, 5, []byte("change"), nil), 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.NewOriginalTxOutput(assetID, 100, outscript, nil), 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.Uint64Bytes(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, 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, nil)}, 204 Outputs: []*types.TxOutput{types.NewOriginalTxOutput(bc.AssetID{}, 3, nil, 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, nil), 211 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.NewAssetID([32]byte{1}), 5, 0, nil, nil), 212 }, 213 Outputs: []*types.TxOutput{types.NewOriginalTxOutput(bc.AssetID{}, 5, nil, 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, nil)}, 219 Outputs: []*types.TxOutput{ 220 types.NewOriginalTxOutput(bc.AssetID{}, math.MaxInt64, nil, nil), 221 types.NewOriginalTxOutput(bc.AssetID{}, 7, nil, 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, nil), 229 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, math.MaxInt64, 0, nil, 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, nil)}, 236 Outputs: []*types.TxOutput{types.NewOriginalTxOutput(bc.AssetID{}, 5, nil, nil)}, 237 }, 238 want: nil, 239 }, { 240 tx: &types.TxData{ 241 Outputs: []*types.TxOutput{types.NewOriginalTxOutput(bc.AssetID{}, 5, nil, 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, nil)}, 247 Outputs: []*types.TxOutput{types.NewOriginalTxOutput(bc.NewAssetID([32]byte{1}), 5, nil, 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, nil), 300 }, 301 Outputs: []*types.TxOutput{ 302 types.NewOriginalTxOutput(*consensus.BTMAssetID, 10000000000, recvProg, nil), 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 }