Skip to content

Commit

Permalink
adding new ChangeToOutput function, used to add change and add the va…
Browse files Browse the repository at this point in the history
…lue to an existing output
Mark committed May 4, 2021

Unverified

This user has not yet uploaded their public signing key.
1 parent e5e01aa commit 578f68a
Showing 2 changed files with 109 additions and 13 deletions.
48 changes: 36 additions & 12 deletions tx.go
Original file line number Diff line number Diff line change
@@ -233,43 +233,68 @@ func (tx *Tx) ChangeToAddress(addr string, f []*Fee) error {
// Change calculates the amount of fees needed to cover the transaction
// and adds the left over change in a new output using the script provided.
func (tx *Tx) Change(s *bscript.Script, f []*Fee) error {
available, hasChange, err := tx.change(s, f, true)
if err != nil {
return err
}
if hasChange {
// add rest of available sats to the change output
tx.Outputs[len(tx.GetOutputs())-1].Satoshis = available
}
return nil
}

// ChangeToOutput will calculate fees and add them to an output at the index specified (0 based).
// If an invalid index is supplied and error is returned.
func (tx *Tx) ChangeToOutput(index uint, f []*Fee) error {
if len(tx.Outputs)-1 > int(index) {
return errors.New("index is greater than number of inputs in transaction")
}
available, hasChange, err := tx.change(tx.Outputs[index].LockingScript, f, false)
if err != nil {
return err
}
if hasChange {
tx.Outputs[index].Satoshis += available
}
return nil
}

func (tx *Tx) change(s *bscript.Script, f []*Fee, newOutput bool) (uint64, bool, error) {
inputAmount := tx.GetTotalInputSatoshis()
outputAmount := tx.GetTotalOutputSatoshis()

if inputAmount < outputAmount {
return errors.New("satoshis inputted to the tx are less than the outputted satoshis")
return 0, false, errors.New("satoshis inputted to the tx are less than the outputted satoshis")
}

available := inputAmount - outputAmount

standardFees, err := GetStandardFee(f)
if err != nil {
return err
return 0, false, err
}

if !tx.canAddChange(available, standardFees) {
return nil
return 0, false, err
}
if newOutput {
tx.AddOutput(&Output{Satoshis: 0, LockingScript: s})
}

tx.AddOutput(&Output{Satoshis: 0, LockingScript: s})

var preSignedFeeRequired uint64
if preSignedFeeRequired, err = tx.getPreSignedFeeRequired(f); err != nil {
return err
return 0, false, err
}

var expectedUnlockingScriptFees uint64
if expectedUnlockingScriptFees, err = tx.getExpectedUnlockingScriptFees(f); err != nil {
return err
return 0, false, err
}

available -= preSignedFeeRequired + expectedUnlockingScriptFees

// add rest of available sats to the change output
tx.Outputs[len(tx.GetOutputs())-1].Satoshis = available

return nil
return available, true, nil
}

func (tx *Tx) canAddChange(available uint64, standardFees *Fee) bool {
@@ -305,7 +330,6 @@ func (tx *Tx) getPreSignedFeeRequired(f []*Fee) (uint64, error) {
}

fr += dataBytes * dataFee.MiningFee.Satoshis / dataFee.MiningFee.Bytes

return uint64(fr), nil
}

74 changes: 73 additions & 1 deletion tx_test.go
Original file line number Diff line number Diff line change
@@ -6,9 +6,10 @@ import (
"testing"

"github.com/bitcoinsv/bsvutil"
"github.com/stretchr/testify/assert"

"github.com/libsv/go-bt"
"github.com/libsv/go-bt/bscript"
"github.com/stretchr/testify/assert"
)

func TestNewTx(t *testing.T) {
@@ -779,3 +780,74 @@ func TestTx_SignAuto(t *testing.T) {
assert.Equal(t, rawTxBefore, tx.ToString())
})
}

func TestTx_ChangeToOutput(t *testing.T) {
tests := map[string]struct {
tx *bt.Tx
index uint
fees []*bt.Fee
expOutputTotal uint64
expChangeOutput uint64
err error
}{
"no change to add should return no change output": {
tx: func() *bt.Tx {
tx := bt.NewTx()
assert.NoError(t, tx.From(
"07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b",
0,
"76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac",
1000))
assert.NoError(t, tx.PayTo("mxAoAyZFXX6LZBWhoam3vjm6xt9NxPQ15f", 1000))
return tx
}(),
index: 0,
fees: bt.DefaultFees(),
expOutputTotal: 1000,
expChangeOutput: 1000,
err: nil,
}, "change to add should add change to output": {
tx: func() *bt.Tx {
tx := bt.NewTx()
assert.NoError(t, tx.From(
"07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b",
0,
"76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac",
1000))
assert.NoError(t, tx.PayTo("mxAoAyZFXX6LZBWhoam3vjm6xt9NxPQ15f", 500))
return tx
}(),
index: 0,
fees: bt.DefaultFees(),
expOutputTotal: 904,
expChangeOutput: 904,
err: nil,
}, "change to add should add change to specified output": {
tx: func() *bt.Tx {
tx := bt.NewTx()
assert.NoError(t, tx.From(
"07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b",
0,
"76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac",
2500))
assert.NoError(t, tx.PayTo("mxAoAyZFXX6LZBWhoam3vjm6xt9NxPQ15f", 500))
assert.NoError(t, tx.PayTo("mxAoAyZFXX6LZBWhoam3vjm6xt9NxPQ15f", 500))
assert.NoError(t, tx.PayTo("mxAoAyZFXX6LZBWhoam3vjm6xt9NxPQ15f", 500))
assert.NoError(t, tx.PayTo("mxAoAyZFXX6LZBWhoam3vjm6xt9NxPQ15f", 500))
return tx
}(),
index: 3,
fees: bt.DefaultFees(),
expOutputTotal: 2353,
expChangeOutput: 853,
err: nil,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
assert.Equal(t, test.err, test.tx.ChangeToOutput(test.index, test.fees))
assert.Equal(t, test.expOutputTotal, test.tx.GetTotalOutputSatoshis())
assert.Equal(t, test.expChangeOutput, test.tx.Outputs[test.index].Satoshis)
})
}
}

0 comments on commit 578f68a

Please sign in to comment.