OSDN Git Service

update master (#487)
[bytom/bytom-spv.git] / blockchain / txbuilder / txbuilder_test.go
1 package txbuilder
2
3 import (
4         "context"
5         "encoding/hex"
6         "math"
7         "testing"
8         "time"
9
10         "github.com/davecgh/go-spew/spew"
11
12         "golang.org/x/crypto/sha3"
13
14         "github.com/bytom/crypto/ed25519"
15         "github.com/bytom/crypto/ed25519/chainkd"
16         "github.com/bytom/encoding/json"
17         "github.com/bytom/errors"
18         "github.com/bytom/protocol/bc"
19         "github.com/bytom/protocol/bc/types"
20         "github.com/bytom/protocol/vm"
21         "github.com/bytom/protocol/vm/vmutil"
22         "github.com/bytom/testutil"
23 )
24
25 type testAction bc.AssetAmount
26
27 func (t testAction) Build(ctx context.Context, b *TemplateBuilder) error {
28         in := types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), *t.AssetId, t.Amount, 0, nil)
29         tplIn := &SigningInstruction{}
30
31         err := b.AddInput(in, tplIn)
32         if err != nil {
33                 return err
34         }
35         return b.AddOutput(types.NewTxOutput(*t.AssetId, t.Amount, []byte("change")))
36 }
37
38 func newControlProgramAction(assetAmt bc.AssetAmount, script []byte) *controlProgramAction {
39         return &controlProgramAction{
40                 AssetAmount: assetAmt,
41                 Program:     script,
42         }
43 }
44
45 func TestBuild(t *testing.T) {
46         ctx := context.Background()
47
48         assetID1 := bc.NewAssetID([32]byte{1})
49         assetID2 := bc.NewAssetID([32]byte{2})
50
51         actions := []Action{
52                 newControlProgramAction(bc.AssetAmount{AssetId: &assetID2, Amount: 6}, []byte("dest")),
53                 testAction(bc.AssetAmount{AssetId: &assetID1, Amount: 5}),
54         }
55         expiryTime := time.Now().Add(time.Minute)
56         got, err := Build(ctx, nil, actions, expiryTime, 0)
57         if err != nil {
58                 testutil.FatalErr(t, err)
59         }
60
61         want := &Template{
62                 Transaction: types.NewTx(types.TxData{
63                         Version:        1,
64                         SerializedSize: 332,
65                         Inputs: []*types.TxInput{
66                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), assetID1, 5, 0, nil),
67                         },
68                         Outputs: []*types.TxOutput{
69                                 types.NewTxOutput(assetID2, 6, []byte("dest")),
70                                 types.NewTxOutput(assetID1, 5, []byte("change")),
71                         },
72                 }),
73                 SigningInstructions: []*SigningInstruction{{
74                         WitnessComponents: []witnessComponent{},
75                 }},
76         }
77
78         if !testutil.DeepEqual(got.Transaction.TxData, want.Transaction.TxData) {
79                 t.Errorf("got tx:\n%s\nwant tx:\n%s", spew.Sdump(got.Transaction.TxData), spew.Sdump(want.Transaction.TxData))
80         }
81
82         if !testutil.DeepEqual(got.SigningInstructions, want.SigningInstructions) {
83                 t.Errorf("got signing instructions:\n\t%#v\nwant signing instructions:\n\t%#v", got.SigningInstructions, want.SigningInstructions)
84         }
85 }
86
87 func TestSignatureWitnessMaterialize(t *testing.T) {
88         privkey1, pubkey1, err := chainkd.NewXKeys(nil)
89         if err != nil {
90                 t.Fatal(err)
91         }
92         privkey2, pubkey2, err := chainkd.NewXKeys(nil)
93         if err != nil {
94                 t.Fatal(err)
95         }
96         privkey3, pubkey3, err := chainkd.NewXKeys(nil)
97         if err != nil {
98                 t.Fatal(err)
99         }
100         issuanceProg, _ := vmutil.P2SPMultiSigProgram([]ed25519.PublicKey{pubkey1.PublicKey(), pubkey2.PublicKey(), pubkey3.PublicKey()}, 2)
101         assetID := bc.ComputeAssetID(issuanceProg, 1, &bc.EmptyStringHash)
102         outscript := mustDecodeHex("76a914c5d128911c28776f56baaac550963f7b88501dc388c0")
103         unsigned := types.NewTx(types.TxData{
104                 Version: 1,
105                 Inputs: []*types.TxInput{
106                         types.NewIssuanceInput([]byte{1}, 100, issuanceProg, nil, nil),
107                 },
108                 Outputs: []*types.TxOutput{
109                         types.NewTxOutput(assetID, 100, outscript),
110                 },
111         })
112
113         tpl := &Template{
114                 Transaction: unsigned,
115         }
116         h := tpl.Hash(0)
117         builder := vmutil.NewBuilder()
118         builder.AddData(h.Bytes())
119         builder.AddOp(vm.OP_TXSIGHASH).AddOp(vm.OP_EQUAL)
120         prog, _ := builder.Build()
121         msg := sha3.Sum256(prog)
122         sig1 := privkey1.Sign(msg[:])
123         sig2 := privkey2.Sign(msg[:])
124         sig3 := privkey3.Sign(msg[:])
125         want := [][]byte{
126                 vm.Int64Bytes(0),
127                 sig1,
128                 sig2,
129                 prog,
130         }
131
132         // Test with more signatures than required, in correct order
133         tpl.SigningInstructions = []*SigningInstruction{{
134                 WitnessComponents: []witnessComponent{
135                         &SignatureWitness{
136                                 Quorum: 2,
137                                 Keys: []keyID{
138                                         {
139                                                 XPub:           pubkey1,
140                                                 DerivationPath: []json.HexBytes{{0, 0, 0, 0}},
141                                         },
142                                         {
143                                                 XPub:           pubkey2,
144                                                 DerivationPath: []json.HexBytes{{0, 0, 0, 0}},
145                                         },
146                                         {
147                                                 XPub:           pubkey3,
148                                                 DerivationPath: []json.HexBytes{{0, 0, 0, 0}},
149                                         },
150                                 },
151                                 Program: prog,
152                                 Sigs:    []json.HexBytes{sig1, sig2, sig3},
153                         },
154                 },
155         }}
156         err = materializeWitnesses(tpl)
157         if err != nil {
158                 testutil.FatalErr(t, err)
159         }
160         got := tpl.Transaction.Inputs[0].Arguments()
161         if !testutil.DeepEqual(got, want) {
162                 t.Errorf("got input witness %v, want input witness %v", got, want)
163         }
164
165         // Test with exact amount of signatures required, in correct order
166         component := tpl.SigningInstructions[0].WitnessComponents[0].(*SignatureWitness)
167         component.Sigs = []json.HexBytes{sig1, sig2}
168         err = materializeWitnesses(tpl)
169         if err != nil {
170                 testutil.FatalErr(t, err)
171         }
172         got = tpl.Transaction.Inputs[0].Arguments()
173         if !testutil.DeepEqual(got, want) {
174                 t.Errorf("got input witness %v, want input witness %v", got, want)
175         }
176 }
177
178 func mustDecodeHex(str string) []byte {
179         data, err := hex.DecodeString(str)
180         if err != nil {
181                 panic(err)
182         }
183         return data
184 }
185
186 func TestCheckBlankCheck(t *testing.T) {
187         cases := []struct {
188                 tx   *types.TxData
189                 want error
190         }{{
191                 tx: &types.TxData{
192                         Inputs: []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
193                 },
194                 want: ErrBlankCheck,
195         }, {
196                 tx: &types.TxData{
197                         Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
198                         Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 3, nil)},
199                 },
200                 want: ErrBlankCheck,
201         }, {
202                 tx: &types.TxData{
203                         Inputs: []*types.TxInput{
204                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil),
205                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.NewAssetID([32]byte{1}), 5, 0, nil),
206                         },
207                         Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 5, nil)},
208                 },
209                 want: ErrBlankCheck,
210         }, {
211                 tx: &types.TxData{
212                         Inputs: []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
213                         Outputs: []*types.TxOutput{
214                                 types.NewTxOutput(bc.AssetID{}, math.MaxInt64, nil),
215                                 types.NewTxOutput(bc.AssetID{}, 7, nil),
216                         },
217                 },
218                 want: ErrBadAmount,
219         }, {
220                 tx: &types.TxData{
221                         Inputs: []*types.TxInput{
222                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil),
223                                 types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, math.MaxInt64, 0, nil),
224                         },
225                 },
226                 want: ErrBadAmount,
227         }, {
228                 tx: &types.TxData{
229                         Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
230                         Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 5, nil)},
231                 },
232                 want: nil,
233         }, {
234                 tx: &types.TxData{
235                         Outputs: []*types.TxOutput{types.NewTxOutput(bc.AssetID{}, 5, nil)},
236                 },
237                 want: nil,
238         }, {
239                 tx: &types.TxData{
240                         Inputs:  []*types.TxInput{types.NewSpendInput(nil, bc.NewHash([32]byte{0xff}), bc.AssetID{}, 5, 0, nil)},
241                         Outputs: []*types.TxOutput{types.NewTxOutput(bc.NewAssetID([32]byte{1}), 5, nil)},
242                 },
243                 want: nil,
244         }}
245
246         for _, c := range cases {
247                 got := checkBlankCheck(c.tx)
248                 if errors.Root(got) != c.want {
249                         t.Errorf("checkUnsafe(%+v) err = %v want %v", c.tx, errors.Root(got), c.want)
250                 }
251         }
252 }