Skip to content

Commit

Permalink
change! FeesPaid -> IsFeePaidEnough
Browse files Browse the repository at this point in the history
this is to be used on the receiving end of a tx/payment
  • Loading branch information
Jad Wahab committed Sep 5, 2021
1 parent a6be96a commit bd71290
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 45 deletions.
15 changes: 9 additions & 6 deletions tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,11 +406,15 @@ type TxFees struct {
DataFeePaid uint64
}

// FeesPaid will calculate the fees that this transaction is paying
// IsFeePaidEnough will calculate the fees that this transaction is paying
// including the individual fee types (std/data/etc.).
func (tx *Tx) FeesPaid(fees *FeeQuote) (*TxFees, error) {
size := tx.SizeWithTypes()
return tx.feesPaid(size, fees)
func (tx *Tx) IsFeePaidEnough(fees *FeeQuote) (bool, error) {
expFeesPaid, err := tx.feesPaid(tx.SizeWithTypes(), fees)
if err != nil {
return false, err
}
actualFeePaid := tx.TotalInputSatoshis() - tx.TotalOutputSatoshis()
return actualFeePaid >= expFeesPaid.TotalFeePaid, nil
}

// EstimateFeesPaid will estimate how big the tx will be when finalised
Expand All @@ -436,8 +440,7 @@ func (tx *Tx) feesPaid(size *TxSize, fees *FeeQuote) (*TxFees, error) {
}

resp := &TxFees{
StdFeePaid: size.TotalStdBytes *
uint64(stdFee.MiningFee.Satoshis) / uint64(stdFee.MiningFee.Bytes),
StdFeePaid: size.TotalStdBytes * uint64(stdFee.MiningFee.Satoshis) / uint64(stdFee.MiningFee.Bytes),
DataFeePaid: size.TotalDataBytes * uint64(dataFee.MiningFee.Satoshis) / uint64(dataFee.MiningFee.Bytes),
}
resp.TotalFeePaid = resp.StdFeePaid + resp.DataFeePaid
Expand Down
116 changes: 77 additions & 39 deletions tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,51 +902,75 @@ func TestTx_Clone(t *testing.T) {
})
}

func Test_FeesPaid(t *testing.T) {
func Test_IsFeePaidEnough(t *testing.T) {
tests := map[string]struct {
tx *bt.Tx
dataLength uint64
expFees *bt.TxFees
expSize *bt.TxSize
isEnough bool
}{
"226B transaction (1 input 1 P2PKHOutput + no change) no data should return 113 sats fee": {
"unsigned transaction (1 input 1 P2PKHOutput + no change) paying less by 1 satoshi": {
tx: func() *bt.Tx {
tx := bt.NewTx()
assert.NoError(t, tx.From("a4c76f8a7c05a91dcf5699b95b54e856298e50c1ceca9a8a5569c8532c500c11",
0, "76a91455b61be43392125d127f1780fb038437cd67ef9c88ac", 1000))

assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 100))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 959))
return tx
}(),
expFees: &bt.TxFees{
TotalFeePaid: 42,
StdFeePaid: 42,
DataFeePaid: 0,
},
expSize: &bt.TxSize{
TotalBytes: 85,
TotalStdBytes: 85,
},
}, "226B transaction (1 input 1 P2PKHOutput + change) no data should return 113 sats fee": {
isEnough: false,
}, "unsigned transaction (1 input 1 P2PKHOutput + change) should pay exact amount": {
tx: func() *bt.Tx {
tx := bt.NewTx()
assert.NoError(t, tx.From("a4c76f8a7c05a91dcf5699b95b54e856298e50c1ceca9a8a5569c8532c500c11",
0, "76a91455b61be43392125d127f1780fb038437cd67ef9c88ac", 1000))
0, "76a91455b61be43392125d127f1780fb038437cd67ef9c88ac", 834709))

assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 100))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559))
assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote()))
return tx
}(),
expFees: &bt.TxFees{
TotalFeePaid: 59,
StdFeePaid: 59,
DataFeePaid: 0,
expSize: &bt.TxSize{
TotalBytes: 119,
TotalStdBytes: 119,
TotalDataBytes: 0,
},
isEnough: true,
}, "unsigned transaction (1 input 2 P2PKHOutputs) should pay exact amount": {
tx: func() *bt.Tx {
tx := bt.NewTx()
assert.NoError(t, tx.From("a4c76f8a7c05a91dcf5699b95b54e856298e50c1ceca9a8a5569c8532c500c11",
0, "76a91455b61be43392125d127f1780fb038437cd67ef9c88ac", 834709))

assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 578091))
return tx
}(),
expSize: &bt.TxSize{
TotalBytes: 119,
TotalStdBytes: 119,
TotalDataBytes: 0,
},
isEnough: true,
}, "unsigned transaction (1 input 2 P2PKHOutputs) should fail paying less by 1 sat": {
tx: func() *bt.Tx {
tx := bt.NewTx()
assert.NoError(t, tx.From("a4c76f8a7c05a91dcf5699b95b54e856298e50c1ceca9a8a5569c8532c500c11",
0, "76a91455b61be43392125d127f1780fb038437cd67ef9c88ac", 834709))

assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256560))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 578091))
return tx
}(),
expSize: &bt.TxSize{
TotalBytes: 119,
TotalStdBytes: 119,
TotalDataBytes: 0,
},
isEnough: false,
}, "226B signed transaction (1 input 1 P2PKHOutput + change) no data should return 113 sats fee": {
tx: func() *bt.Tx {
tx := bt.NewTx()
Expand All @@ -955,23 +979,19 @@ func Test_FeesPaid(t *testing.T) {
log.Fatal(err)
}
assert.NoError(t, tx.From("a4c76f8a7c05a91dcf5699b95b54e856298e50c1ceca9a8a5569c8532c500c11",
0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000))
0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 834709))

assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 100))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559))
assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote()))
tx.SignAuto(context.Background(), &bt.LocalSigner{PrivateKey: w.PrivKey})
return tx
}(),
expFees: &bt.TxFees{
TotalFeePaid: 113,
StdFeePaid: 113,
DataFeePaid: 0,
},
expSize: &bt.TxSize{
TotalBytes: 226,
TotalStdBytes: 226,
},
}, "226B signed transaction (1 input 1 P2PKHOutput + no change) no data should return 113 sats fee": {
isEnough: true,
}, "192B signed transaction (1 input 1 P2PKHOutput + no change) should pay exact amount": {
tx: func() *bt.Tx {
tx := bt.NewTx()
w, err := wif.DecodeWIF("cRhdUmZx4MbsjxVxGH4bM4geNLzQEPxspnhGtDCvMmfCLcED8Q6G")
Expand All @@ -981,20 +1001,16 @@ func Test_FeesPaid(t *testing.T) {
assert.NoError(t, tx.From("a4c76f8a7c05a91dcf5699b95b54e856298e50c1ceca9a8a5569c8532c500c11",
0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000))

assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 100))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 904))
tx.SignAuto(context.Background(), &bt.LocalSigner{PrivateKey: w.PrivKey})
return tx
}(),
expFees: &bt.TxFees{
TotalFeePaid: 96,
StdFeePaid: 96,
DataFeePaid: 0,
},
expSize: &bt.TxSize{
TotalBytes: 192,
TotalStdBytes: 192,
},
}, "214B signed transaction (1 input, 1 change output, 1 opreturn) 10 byte of data should return 100 sats fee 6 data fee": {
isEnough: true,
}, "214B signed transaction (1 input, 1 change output, 1 opreturn) should pay exact amount": {
tx: func() *bt.Tx {
w, err := wif.DecodeWIF("cRhdUmZx4MbsjxVxGH4bM4geNLzQEPxspnhGtDCvMmfCLcED8Q6G")
if err != nil {
Expand All @@ -1004,30 +1020,52 @@ func Test_FeesPaid(t *testing.T) {
assert.NoError(t, tx.From("160f06232540dcb0e9b6db9b36a27f01da1e7e473989df67859742cf098d498f",
0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000))
assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello")))
assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote()))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 894))
is, err := tx.SignAuto(context.Background(), &bt.LocalSigner{PrivateKey: w.PrivKey})
assert.Nil(t, err)
assert.Equal(t, 1, len(is))
return tx
}(),
expFees: &bt.TxFees{
TotalFeePaid: 106,
StdFeePaid: 100,
DataFeePaid: 6,
},
expSize: &bt.TxSize{
TotalBytes: 214,
TotalStdBytes: 201,
TotalDataBytes: 13,
},
isEnough: true,
}, "214B signed transaction (1 input, 1 change output, 1 opreturn) should fail paying less by 1 sat": {
tx: func() *bt.Tx {
w, err := wif.DecodeWIF("cRhdUmZx4MbsjxVxGH4bM4geNLzQEPxspnhGtDCvMmfCLcED8Q6G")
if err != nil {
log.Fatal(err)
}
tx := bt.NewTx()
assert.NoError(t, tx.From("160f06232540dcb0e9b6db9b36a27f01da1e7e473989df67859742cf098d498f",
0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000))
assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello")))
assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 895))
is, err := tx.SignAuto(context.Background(), &bt.LocalSigner{PrivateKey: w.PrivKey})
assert.Nil(t, err)
assert.Equal(t, 1, len(is))
return tx
}(),
expSize: &bt.TxSize{
TotalBytes: 213,
TotalStdBytes: 200,
TotalDataBytes: 13,
},
isEnough: false,
},
// TODO: add tests for different fee type values
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
fee := bt.NewFeeQuote()
resp, err := test.tx.FeesPaid(fee)
isEnough, err := test.tx.IsFeePaidEnough(fee)
assert.NoError(t, err)
assert.Equal(t, test.expFees, resp)
assert.Equal(t, test.isEnough, isEnough)

swt := test.tx.SizeWithTypes()
assert.Equal(t, test.expSize, swt)
})
}
}
Expand Down

0 comments on commit bd71290

Please sign in to comment.