-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: FromUTXOs #45
Changes from 14 commits
94bfa8d
644ae93
a67e50e
be5c4ba
68fdc0b
92a5633
0685253
d053f1a
e3c93b7
e0fc927
7094e5b
82e1bf0
8eff6b5
f50985d
bfe23db
0d6ea41
e2f01a1
cf29059
2db9190
5eb4ae6
85398dd
1a7d125
73fd259
a163144
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,25 @@ package bt | |
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/binary" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/libsv/go-bk/crypto" | ||
|
||
"github.com/libsv/go-bt/v2/bscript" | ||
) | ||
|
||
// ErrNoInput signals the InputGetterFunc has reached the end of its input. | ||
var ErrNoInput = errors.New("no remaining inputs") | ||
|
||
// InputGetterFunc is used for FromInputs. It expects *bt.Input to be returned containing | ||
// relevant input information, and an err informing any retrieval errors. | ||
// | ||
// It is expected that bt.ErrNoInput will be returned once the input source is depleted. | ||
type InputGetterFunc func(ctx context.Context) (*Input, error) | ||
|
||
// NewInputFromBytes returns a transaction input from the bytes provided. | ||
func NewInputFromBytes(bytes []byte) (*Input, int, error) { | ||
if len(bytes) < 36 { | ||
|
@@ -34,6 +45,28 @@ func NewInputFromBytes(bytes []byte) (*Input, int, error) { | |
}, totalLength, nil | ||
} | ||
|
||
// NewInputFrom builds and returns a new input from the specified UTXO fields, using the default | ||
// finalised sequence number (0xFFFFFFFF). If you want a different nSeq, change it manually | ||
// afterwards. | ||
func NewInputFrom(prevTxID string, vout uint32, prevTxLockingScript string, satoshis uint64) (*Input, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't these functions go into the inputs.go file instead of here? i think what we were going for was to try an minimise the number of ways users could do things. so in v1 the user had to create the input himself and then add that input to the tx, whereas with v2 the point is to use the .From() method to create the input as part of the tx straight away since having an input alone isn't much use is it? maybe we can keep this func but just put it in inputs.go and make in private? what do you guys think? @theflyingcodr @Tigh-Gherr ? that was the reasoning behind making tx.addInput private as well especially since it confused a couple devs into using it the wrong way in the past... |
||
pts, err := bscript.NewFromHexString(prevTxLockingScript) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
i := &Input{ | ||
PreviousTxOutIndex: vout, | ||
PreviousTxSatoshis: satoshis, | ||
PreviousTxScript: pts, | ||
SequenceNumber: DefaultSequenceNumber, // use default finalised sequence number | ||
} | ||
if err := i.PreviousTxIDAddStr(prevTxID); err != nil { | ||
return nil, err | ||
} | ||
|
||
return i, nil | ||
} | ||
|
||
// TotalInputSatoshis returns the total Satoshis inputted to the transaction. | ||
func (tx *Tx) TotalInputSatoshis() (total uint64) { | ||
for _, in := range tx.Inputs { | ||
|
@@ -69,21 +102,54 @@ func (tx *Tx) AddP2PKHInputsFromTx(pvsTx *Tx, matchPK []byte) error { | |
// finalised sequence number (0xFFFFFFFF). If you want a different nSeq, change it manually | ||
// afterwards. | ||
func (tx *Tx) From(prevTxID string, vout uint32, prevTxLockingScript string, satoshis uint64) error { | ||
pts, err := bscript.NewFromHexString(prevTxLockingScript) | ||
i, err := NewInputFrom(prevTxID, vout, prevTxLockingScript, satoshis) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
i := &Input{ | ||
PreviousTxOutIndex: vout, | ||
PreviousTxSatoshis: satoshis, | ||
PreviousTxScript: pts, | ||
SequenceNumber: DefaultSequenceNumber, // use default finalised sequence number | ||
tx.addInput(i) | ||
return nil | ||
} | ||
|
||
// FromInputs continuously calls the provided bt.InputGetterFunc, adding each returned input | ||
// as an input via tx.From(...), until it is estimated that inputs cover the outputs + fees. | ||
// | ||
// After completion, the receiver is ready for `Change(...)` to be called, and then be signed. | ||
// Note, this function works under the assumption that receiver *bt.Tx alread has all the outputs | ||
// which need covered. | ||
// | ||
// Example usage, for when working with a list: | ||
// tx.FromInputs(ctx, bt.NewFeeQuote(), func() bt.InputGetterFunc { | ||
// i := 0 | ||
// return func(ctx context.Context) (*bt.Input, error) { | ||
// if i >= len(utxos) { | ||
// return nil, bt.ErrNoInput | ||
// } | ||
// defer func() { i++ }() | ||
// return bt.NewInputFrom(utxos[i].TxID, utxo[i].Vout, utxos[i].Script, utxos[i].Satoshis), true | ||
// } | ||
// }()) | ||
func (tx *Tx) FromInputs(ctx context.Context, fq *FeeQuote, next InputGetterFunc) (err error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ooh this is good shit, i like it 👍 |
||
var feesPaid bool | ||
for !feesPaid { | ||
input, err := next(ctx) | ||
if err != nil { | ||
if errors.Is(err, ErrNoInput) { | ||
break | ||
} | ||
|
||
return err | ||
} | ||
tx.addInput(input) | ||
|
||
feesPaid, err = tx.EstimateIsFeePaidEnough(fq) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
if err := i.PreviousTxIDAddStr(prevTxID); err != nil { | ||
return err | ||
if !feesPaid { | ||
return errors.New("insufficient inputs provided") | ||
} | ||
tx.addInput(i) | ||
|
||
return nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i see this went thought a few iterations haha but i still think it may be able to go for 1 more iteration to make it little clearer - let's discuss on monday 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i really like where this is going actually, but i have a much better idea of how i would like this to work now and it follows the reasoning that we were discussing about potentially making inputs and outputs private that are used in a tx - since (unless someone can convince me otherwise) we will never have an input or output that isn't part of a tx and it just creates confusion, as we've seen previously, when separating them.
1st: we should probably return an array here instead of just one (input) since many times you will have an array of utxos to choose from and then we can add functionality to choose different utxos and stuff. plus of the utxogetter involved i/o then we'd want to minimise that and create a bulk call or something like that which we've done many times before.
2nd: it doesn't really feel accurate to say input getter since we're not getting the inputs, we're getting the utxos and creating the input... i think it would be much better to change this to UTXOGetterFunc and have it return an array of utxos and then we internally create the input from the utxo(s) and add them to the tx.
i'm still unsure whether we should still make the inputs/outputs private (ideally we could just leave them public to retain flexibility for users that know what they're doing but to remove all functions that imply separation between inputs/outputs and txs - for example the NewInput/Ouput funcs)...
thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.