author_test.go raw
1 // Copyright (c) 2016 The btcsuite developers
2 package txauthor
3
4 import (
5 "github.com/p9c/p9/pkg/amt"
6 "testing"
7
8 "github.com/p9c/p9/pkg/txrules"
9 "github.com/p9c/p9/pkg/txsizes"
10 "github.com/p9c/p9/pkg/wire"
11 )
12
13 func p2pkhOutputs(amounts ...amt.Amount) []*wire.TxOut {
14 v := make([]*wire.TxOut, 0, len(amounts))
15 for _, a := range amounts {
16 outScript := make([]byte, txsizes.P2PKHOutputSize)
17 v = append(v, wire.NewTxOut(int64(a), outScript))
18 }
19 return v
20 }
21 func makeInputSource(unspents []*wire.TxOut) InputSource {
22 // Return outputs in order.
23 currentTotal := amt.Amount(0)
24 currentInputs := make([]*wire.TxIn, 0, len(unspents))
25 currentInputValues := make([]amt.Amount, 0, len(unspents))
26 f := func(target amt.Amount) (amt.Amount, []*wire.TxIn, []amt.Amount, [][]byte, error) {
27 for currentTotal < target && len(unspents) != 0 {
28 u := unspents[0]
29 unspents = unspents[1:]
30 nextInput := wire.NewTxIn(&wire.OutPoint{}, nil, nil)
31 currentTotal += amt.Amount(u.Value)
32 currentInputs = append(currentInputs, nextInput)
33 currentInputValues = append(currentInputValues, amt.Amount(u.Value))
34 }
35 return currentTotal, currentInputs, currentInputValues, make([][]byte, len(currentInputs)), nil
36 }
37 return f
38 }
39 func TestNewUnsignedTransaction(t *testing.T) {
40 tests := []struct {
41 UnspentOutputs []*wire.TxOut
42 Outputs []*wire.TxOut
43 RelayFee amt.Amount
44 ChangeAmount amt.Amount
45 InputSourceError bool
46 InputCount int
47 }{
48 0: {
49 UnspentOutputs: p2pkhOutputs(1e8),
50 Outputs: p2pkhOutputs(1e8),
51 RelayFee: 1e3,
52 InputSourceError: true,
53 },
54 1: {
55 UnspentOutputs: p2pkhOutputs(1e8),
56 Outputs: p2pkhOutputs(1e6),
57 RelayFee: 1e3,
58 ChangeAmount: 1e8 - 1e6 - txrules.FeeForSerializeSize(1e3,
59 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(1e6), true),
60 ),
61 InputCount: 1,
62 },
63 2: {
64 UnspentOutputs: p2pkhOutputs(1e8),
65 Outputs: p2pkhOutputs(1e6),
66 RelayFee: 1e4,
67 ChangeAmount: 1e8 - 1e6 - txrules.FeeForSerializeSize(1e4,
68 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(1e6), true),
69 ),
70 InputCount: 1,
71 },
72 3: {
73 UnspentOutputs: p2pkhOutputs(1e8),
74 Outputs: p2pkhOutputs(1e6, 1e6, 1e6),
75 RelayFee: 1e4,
76 ChangeAmount: 1e8 - 3e6 - txrules.FeeForSerializeSize(1e4,
77 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(1e6, 1e6, 1e6), true),
78 ),
79 InputCount: 1,
80 },
81 4: {
82 UnspentOutputs: p2pkhOutputs(1e8),
83 Outputs: p2pkhOutputs(1e6, 1e6, 1e6),
84 RelayFee: 2.55e3,
85 ChangeAmount: 1e8 - 3e6 - txrules.FeeForSerializeSize(2.55e3,
86 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(1e6, 1e6, 1e6), true),
87 ),
88 InputCount: 1,
89 },
90 // Test dust thresholds (546 for a 1e3 relay fee).
91 5: {
92 UnspentOutputs: p2pkhOutputs(1e8),
93 Outputs: p2pkhOutputs(1e8 - 545 - txrules.FeeForSerializeSize(1e3,
94 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(0), true),
95 ),
96 ),
97 RelayFee: 1e3,
98 ChangeAmount: 545,
99 InputCount: 1,
100 },
101 6: {
102 UnspentOutputs: p2pkhOutputs(1e8),
103 Outputs: p2pkhOutputs(1e8 - 546 - txrules.FeeForSerializeSize(1e3,
104 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(0), true),
105 ),
106 ),
107 RelayFee: 1e3,
108 ChangeAmount: 546,
109 InputCount: 1,
110 },
111 // Test dust thresholds (1392.3 for a 2.55e3 relay fee).
112 7: {
113 UnspentOutputs: p2pkhOutputs(1e8),
114 Outputs: p2pkhOutputs(1e8 - 1392 - txrules.FeeForSerializeSize(2.55e3,
115 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(0), true),
116 ),
117 ),
118 RelayFee: 2.55e3,
119 ChangeAmount: 1392,
120 InputCount: 1,
121 },
122 8: {
123 UnspentOutputs: p2pkhOutputs(1e8),
124 Outputs: p2pkhOutputs(1e8 - 1393 - txrules.FeeForSerializeSize(2.55e3,
125 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(0), true),
126 ),
127 ),
128 RelayFee: 2.55e3,
129 ChangeAmount: 1393,
130 InputCount: 1,
131 },
132 // Test two unspent outputs available but only one needed (tested fee only includes one input rather than using
133 // a serialize size for each).
134 9: {
135 UnspentOutputs: p2pkhOutputs(1e8, 1e8),
136 Outputs: p2pkhOutputs(1e8 - 546 - txrules.FeeForSerializeSize(1e3,
137 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(0), true),
138 ),
139 ),
140 RelayFee: 1e3,
141 ChangeAmount: 546,
142 InputCount: 1,
143 },
144 // Test that second output is not included to make the change output not dust and be included in the
145 // transaction.
146 //
147 // It's debatable whether or not this is a good idea, but it's how the function was written, so test it anyways.
148 10: {
149 UnspentOutputs: p2pkhOutputs(1e8, 1e8),
150 Outputs: p2pkhOutputs(1e8 - 545 - txrules.FeeForSerializeSize(1e3,
151 txsizes.EstimateVirtualSize(1, 0, 0, p2pkhOutputs(0), true),
152 ),
153 ),
154 RelayFee: 1e3,
155 ChangeAmount: 545,
156 InputCount: 1,
157 },
158 // Test two unspent outputs available where both are needed.
159 11: {
160 UnspentOutputs: p2pkhOutputs(1e8, 1e8),
161 Outputs: p2pkhOutputs(1e8),
162 RelayFee: 1e3,
163 ChangeAmount: 1e8 - txrules.FeeForSerializeSize(1e3,
164 txsizes.EstimateVirtualSize(2, 0, 0, p2pkhOutputs(1e8), true),
165 ),
166 InputCount: 2,
167 },
168 // Test that zero change outputs are not included (ChangeAmount=0 means don't include any change output).
169 12: {
170 UnspentOutputs: p2pkhOutputs(1e8),
171 Outputs: p2pkhOutputs(1e8),
172 RelayFee: 0,
173 ChangeAmount: 0,
174 InputCount: 1,
175 },
176 }
177 changeSource := func() ([]byte, error) {
178 // Only length matters for these tests.
179 return make([]byte, txsizes.P2PKHPkScriptSize), nil
180 }
181 for i, test := range tests {
182 inputSource := makeInputSource(test.UnspentOutputs)
183 tx, e := NewUnsignedTransaction(test.Outputs, test.RelayFee, inputSource, changeSource)
184 switch e := e.(type) {
185 case nil:
186 case InputSourceError:
187 if !test.InputSourceError {
188 t.Errorf("Test %d: Returned InputSourceError but expected "+
189 "change output with amount %v", i, test.ChangeAmount,
190 )
191 }
192 continue
193 default:
194 t.Errorf("Test %d: Unexpected error: %v", i, e)
195 continue
196 }
197 if tx.ChangeIndex < 0 {
198 if test.ChangeAmount != 0 {
199 t.Errorf("Test %d: No change output added but expected output with amount %v",
200 i, test.ChangeAmount,
201 )
202 continue
203 }
204 } else {
205 changeAmount := amt.Amount(tx.Tx.TxOut[tx.ChangeIndex].Value)
206 if test.ChangeAmount == 0 {
207 t.Errorf("Test %d: Included change output with value %v but expected no change",
208 i, changeAmount,
209 )
210 continue
211 }
212 if changeAmount != test.ChangeAmount {
213 t.Errorf("Test %d: Got change amount %v, Expected %v",
214 i, changeAmount, test.ChangeAmount,
215 )
216 continue
217 }
218 }
219 if len(tx.Tx.TxIn) != test.InputCount {
220 t.Errorf("Test %d: Used %d outputs from input source, Expected %d",
221 i, len(tx.Tx.TxIn), test.InputCount,
222 )
223 }
224 }
225 }
226