github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/bc/types/transaction_test.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "encoding/json" 7 "io/ioutil" 8 "strings" 9 "testing" 10 11 "github.com/davecgh/go-spew/spew" 12 13 "github.com/bytom/bytom/consensus" 14 "github.com/bytom/bytom/errors" 15 "github.com/bytom/bytom/protocol/bc" 16 "github.com/bytom/bytom/testutil" 17 ) 18 19 func TestTransaction(t *testing.T) { 20 cases := []struct { 21 tx *Tx 22 hex string 23 hash bc.Hash 24 }{ 25 { 26 tx: NewTx(TxData{ 27 Version: 1, 28 SerializedSize: uint64(5), 29 Inputs: nil, 30 Outputs: nil, 31 }), 32 hex: strings.Join([]string{ 33 "07", // serflags 34 "01", // transaction version 35 "00", // tx time range 36 "00", // inputs count 37 "00", // outputs count 38 }, ""), 39 hash: testutil.MustDecodeHash("8e88b9cb4615128c7209dff695f68b8de5b38648bf3d44d2d0e6a674848539c9"), 40 }, 41 { 42 tx: NewTx(TxData{ 43 Version: 1, 44 SerializedSize: uint64(261), 45 TimeRange: 654, 46 Inputs: []*TxInput{ 47 NewIssuanceInput([]byte("nonce"), 254354, []byte("issuanceProgram"), [][]byte{[]byte("arguments1"), []byte("arguments2")}, []byte("assetDefinition")), 48 NewSpendInput([][]byte{[]byte("arguments3"), []byte("arguments4")}, testutil.MustDecodeHash("fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409"), *consensus.BTMAssetID, 254354, 3, []byte("spendProgram")), 49 }, 50 Outputs: []*TxOutput{ 51 NewTxOutput(testutil.MustDecodeAsset("a69849e11add96ac7053aad22ba2349a4abf5feb0475a0afcadff4e128be76cf"), 254354, []byte("true")), 52 }, 53 }), 54 hex: strings.Join([]string{ 55 "07", // serflags 56 "01", // transaction version 57 "8e05", // tx time range 58 "02", // inputs count 59 "01", // input 0: asset version 60 "2a", // input 0: serialization length 61 "00", // input 0: issuance type flag 62 "05", // input 0: nonce length 63 "6e6f6e6365", // input 0: nonce 64 "a69849e11add96ac7053aad22ba2349a4abf5feb0475a0afcadff4e128be76cf", // input 0: assetID 65 "92c30f", // input 0: amount 66 "38", // input 0: input witness length 67 "0f", // input 0: asset definition length 68 "6173736574446566696e6974696f6e", // input 0: asset definition 69 "01", // input 0: vm version 70 "0f", // input 0: issuanceProgram length 71 "69737375616e636550726f6772616d", // input 0: issuance program 72 "02", // input 0: argument array length 73 "0a", // input 0: first argument length 74 "617267756d656e747331", // input 0: first argument data 75 "0a", // input 0: second argument length 76 "617267756d656e747332", // input 0: second argument data 77 "01", // input 1: asset version 78 "54", // input 1: input commitment length 79 "01", // input 1: spend type flag 80 "52", // input 1: spend commitment length 81 "fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409", // input 1: source id 82 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // input 1: assetID 83 "92c30f", // input 1: amount 84 "03", // input 1: source position 85 "01", // input 1: vm version 86 "0c", // input 1: spend program length 87 "7370656e6450726f6772616d", // input 1: spend program 88 "17", // input 1: witness length 89 "02", // input 1: argument array length 90 "0a", // input 1: first argument length 91 "617267756d656e747333", // input 1: first argument data 92 "0a", // input 1: second argument length 93 "617267756d656e747334", // input 1: second argument data 94 "01", // outputs count 95 "01", // output 0: asset version 96 "29", // output 0: serialization length 97 "a69849e11add96ac7053aad22ba2349a4abf5feb0475a0afcadff4e128be76cf", // output 0: assetID 98 "92c30f", // output 0: amount 99 "01", // output 0: version 100 "04", // output 0: control program length 101 "74727565", // output 0: control program 102 "00", // output 0: witness length 103 }, ""), 104 hash: testutil.MustDecodeHash("a0ece5ca48dca27708394852599cb4d04af22c36538c03cb72663f3091406c17"), 105 }, 106 { 107 tx: NewTx(TxData{ 108 Version: 1, 109 SerializedSize: uint64(108), 110 Inputs: []*TxInput{ 111 NewCoinbaseInput([]byte("arbitrary")), 112 }, 113 Outputs: []*TxOutput{ 114 NewTxOutput(*consensus.BTMAssetID, 254354, []byte("true")), 115 NewTxOutput(*consensus.BTMAssetID, 254354, []byte("false")), 116 }, 117 }), 118 hex: strings.Join([]string{ 119 "07", // serflags 120 "01", // transaction version 121 "00", // tx time range 122 "01", // inputs count 123 "01", // input 0: asset version 124 "0b", // input 0: input commitment length 125 "02", // input 0: coinbase type flag 126 "09", // input 0: arbitrary length 127 "617262697472617279", // input 0: arbitrary data 128 "00", // input 0: witness length 129 "02", // outputs count 130 "01", // output 0: asset version 131 "29", // output 0: serialization length 132 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // output 0: assetID 133 "92c30f", // output 0: amount 134 "01", // output 0: version 135 "04", // output 0: control program length 136 "74727565", // output 0: control program 137 "00", // output 0: witness length 138 "01", // output 1: asset version 139 "2a", // output 1: serialization length 140 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // output 1: assetID 141 "92c30f", // output 1: amount 142 "01", // output 1: version 143 "05", // output 1: control program length 144 "66616c7365", // output 1: control program 145 "00", // output 1: witness length 146 }, ""), 147 hash: testutil.MustDecodeHash("c2e2f388706fc06cca6aba5e85e0e85029f772872e1b6e6c32a70da22d0309dc"), 148 }, 149 } 150 for i, test := range cases { 151 got := testutil.Serialize(t, test.tx) 152 want, err := hex.DecodeString(test.hex) 153 if err != nil { 154 t.Fatal(err) 155 } 156 157 if !bytes.Equal(got, want) { 158 t.Errorf("test %d: bytes = %x want %x", i, got, want) 159 } 160 if test.tx.ID != test.hash { 161 t.Errorf("test %d: hash = %x want %x", i, test.tx.ID.Bytes(), test.hash.Bytes()) 162 } 163 164 txJSON, err := json.Marshal(test.tx) 165 if err != nil { 166 t.Errorf("test %d: error marshaling tx to json: %s", i, err) 167 } 168 txFromJSON := Tx{} 169 if err := json.Unmarshal(txJSON, &txFromJSON); err != nil { 170 t.Errorf("test %d: error unmarshaling tx from json: %s", i, err) 171 } 172 if !testutil.DeepEqual(test.tx.TxData, txFromJSON.TxData) { 173 t.Errorf("test %d: types.TxData -> json -> types.TxData: got:\n%s\nwant:\n%s", i, spew.Sdump(txFromJSON.TxData), spew.Sdump(test.tx.TxData)) 174 } 175 176 tx1 := new(TxData) 177 if err := tx1.UnmarshalText([]byte(test.hex)); err != nil { 178 t.Errorf("test %d: unexpected err %v", i, err) 179 } 180 if !testutil.DeepEqual(*tx1, test.tx.TxData) { 181 t.Errorf("test %d: tx1 is:\n%swant:\n%s", i, spew.Sdump(*tx1), spew.Sdump(test.tx.TxData)) 182 } 183 } 184 } 185 186 func TestTransactionTrailingGarbage(t *testing.T) { 187 // validTxHex is a valid tx, we don't care what's inside as long as it's valid 188 validTxHex := `07010001012b00030a0908916133a0d64d1d973b631e226ef95338ad4a536b95635f32f0d04708a6f2a26380a094a58d1d09000101010103010203010129000000000000000000000000000000000000000000000000000000000000000080a094a58d1d01010100` 189 validTx := Tx{} 190 if err := validTx.UnmarshalText([]byte(validTxHex)); err != nil { 191 t.Fatal(err) 192 } 193 194 invalidTxHex := validTxHex + strings.Repeat("00", 10) 195 invalidTx := Tx{} 196 if err := invalidTx.UnmarshalText([]byte(invalidTxHex)); err == nil { 197 t.Fatal("expected error with trailing garbage but got nil") 198 } 199 } 200 201 func TestInvalidIssuance(t *testing.T) { 202 hex := strings.Join([]string{ 203 "07", // serflags 204 "01", // transaction version 205 "00", // tx maxtime 206 "01", // inputs count 207 "01", // input 0, asset version 208 "2b", // input 0, input commitment length prefix 209 "00", // input 0, input commitment, "issuance" type 210 "03", // input 0, input commitment, nonce length prefix 211 "0a0908", // input 0, input commitment, nonce 212 "0000000000000000000000000000000000000000000000000000000000000000", // input 0, input commitment, WRONG asset id 213 "80a094a58d1d", // input 0, input commitment, amount 214 "29", // input 0, issuance input witness length prefix 215 "03deff1d4319d67baa10a6d26c1fea9c3e8d30e33474efee1a610a9bb49d758d", // input 0, issuance input witness, initial block 216 "00", // input 0, issuance input witness, asset definition 217 "01", // input 0, issuance input witness, vm version 218 "01", // input 0, issuance input witness, issuance program length prefix 219 "01", // input 0, issuance input witness, issuance program 220 "01", // input 0, issuance input witness, arguments count 221 "03", // input 0, issuance input witness, argument 0 length prefix 222 "010203", // input 0, issuance input witness, argument 0 223 "01", // outputs count 224 "01", // output 0, asset version 225 "29", // output 0, output commitment length 226 "0000000000000000000000000000000000000000000000000000000000000000", // output 0, output commitment, asset id 227 "80a094a58d1d", // output 0, output commitment, amount 228 "01", // output 0, output commitment, vm version 229 "0101", // output 0, output commitment, control program 230 "066f7574707574", // output 0, reference data 231 "00", // output 0, output witness 232 "0869737375616e6365", 233 }, "") 234 235 tx := new(TxData) 236 if err := tx.UnmarshalText([]byte(hex)); errors.Root(err) != errBadAssetID { 237 t.Errorf("want errBadAssetID, got %v", err) 238 } 239 } 240 241 func TestFuzzUnknownAssetVersion(t *testing.T) { 242 rawTx := `07010001012b00030a0908916133a0d64d1d973b631e226ef95338ad4a536b95635f32f0d04708a6f2a26380a094a58d1d09000101010103010203010129000000000000000000000000000000000000000000000000000000000000000080a094a58d1d01010100` 243 want := Tx{} 244 if err := want.UnmarshalText([]byte(rawTx)); err != nil { 245 t.Fatal(err) 246 } 247 248 b, err := want.MarshalText() 249 if err != nil { 250 t.Fatal(err) 251 } 252 253 got := Tx{} 254 if err = got.UnmarshalText(b); err != nil { 255 t.Fatal(err) 256 } 257 if got.ID.String() != want.ID.String() { 258 t.Errorf("tx id changed to %s", got.ID.String()) 259 } 260 } 261 262 func BenchmarkTxWriteToTrue(b *testing.B) { 263 tx := &Tx{} 264 for i := 0; i < b.N; i++ { 265 tx.writeTo(ioutil.Discard, 0) 266 } 267 } 268 269 func BenchmarkTxWriteToFalse(b *testing.B) { 270 tx := &Tx{} 271 for i := 0; i < b.N; i++ { 272 tx.writeTo(ioutil.Discard, serRequired) 273 } 274 } 275 276 func BenchmarkTxWriteToTrue200(b *testing.B) { 277 tx := &Tx{} 278 for i := 0; i < 200; i++ { 279 tx.Inputs = append(tx.Inputs, NewSpendInput(nil, bc.Hash{}, bc.AssetID{}, 0, 0, nil)) 280 tx.Outputs = append(tx.Outputs, NewTxOutput(bc.AssetID{}, 0, nil)) 281 } 282 for i := 0; i < b.N; i++ { 283 tx.writeTo(ioutil.Discard, 0) 284 } 285 } 286 287 func BenchmarkTxWriteToFalse200(b *testing.B) { 288 tx := &Tx{} 289 for i := 0; i < 200; i++ { 290 tx.Inputs = append(tx.Inputs, NewSpendInput(nil, bc.Hash{}, bc.AssetID{}, 0, 0, nil)) 291 tx.Outputs = append(tx.Outputs, NewTxOutput(bc.AssetID{}, 0, nil)) 292 } 293 for i := 0; i < b.N; i++ { 294 tx.writeTo(ioutil.Discard, serRequired) 295 } 296 } 297 298 func BenchmarkTxInputWriteToTrue(b *testing.B) { 299 input := NewSpendInput(nil, bc.Hash{}, bc.AssetID{}, 0, 0, nil) 300 ew := errors.NewWriter(ioutil.Discard) 301 for i := 0; i < b.N; i++ { 302 input.writeTo(ew) 303 } 304 } 305 306 func BenchmarkTxInputWriteToFalse(b *testing.B) { 307 input := NewSpendInput(nil, bc.Hash{}, bc.AssetID{}, 0, 0, nil) 308 ew := errors.NewWriter(ioutil.Discard) 309 for i := 0; i < b.N; i++ { 310 input.writeTo(ew) 311 } 312 } 313 314 func BenchmarkTxOutputWriteToTrue(b *testing.B) { 315 output := NewTxOutput(bc.AssetID{}, 0, nil) 316 ew := errors.NewWriter(ioutil.Discard) 317 for i := 0; i < b.N; i++ { 318 output.writeTo(ew) 319 } 320 } 321 322 func BenchmarkTxOutputWriteToFalse(b *testing.B) { 323 output := NewTxOutput(bc.AssetID{}, 0, nil) 324 ew := errors.NewWriter(ioutil.Discard) 325 for i := 0; i < b.N; i++ { 326 output.writeTo(ew) 327 } 328 } 329 330 func BenchmarkAssetAmountWriteTo(b *testing.B) { 331 aa := bc.AssetAmount{} 332 for i := 0; i < b.N; i++ { 333 aa.WriteTo(ioutil.Discard) 334 } 335 }