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