mirror of
https://github.com/LBRYFoundation/lbcwallet.git
synced 2025-08-27 07:23:25 +00:00
votingpool: API to store withdrawal txs in the txstore
This commit is contained in:
parent
09c391cc38
commit
d050a32cb2
4 changed files with 132 additions and 0 deletions
|
@ -147,6 +147,10 @@ const (
|
||||||
// invalid ID.
|
// invalid ID.
|
||||||
ErrSeriesIDInvalid
|
ErrSeriesIDInvalid
|
||||||
|
|
||||||
|
// ErrWithdrawalTxStorage indicates an error when storing withdrawal
|
||||||
|
// transactions.
|
||||||
|
ErrWithdrawalTxStorage
|
||||||
|
|
||||||
// lastErr is used for testing, making it possible to iterate over
|
// lastErr is used for testing, making it possible to iterate over
|
||||||
// the error codes in order to check that they all have proper
|
// the error codes in order to check that they all have proper
|
||||||
// translations in errorCodeStrings.
|
// translations in errorCodeStrings.
|
||||||
|
@ -187,6 +191,7 @@ var errorCodeStrings = map[ErrorCode]string{
|
||||||
ErrTxSigning: "ErrTxSigning",
|
ErrTxSigning: "ErrTxSigning",
|
||||||
ErrInvalidScriptHash: "ErrInvalidScriptHash",
|
ErrInvalidScriptHash: "ErrInvalidScriptHash",
|
||||||
ErrWithdrawFromUnusedAddr: "ErrWithdrawFromUnusedAddr",
|
ErrWithdrawFromUnusedAddr: "ErrWithdrawFromUnusedAddr",
|
||||||
|
ErrWithdrawalTxStorage: "ErrWithdrawalTxStorage",
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the ErrorCode as a human-readable name.
|
// String returns the ErrorCode as a human-readable name.
|
||||||
|
|
|
@ -64,6 +64,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
||||||
{vp.ErrTxSigning, "ErrTxSigning"},
|
{vp.ErrTxSigning, "ErrTxSigning"},
|
||||||
{vp.ErrInvalidScriptHash, "ErrInvalidScriptHash"},
|
{vp.ErrInvalidScriptHash, "ErrInvalidScriptHash"},
|
||||||
{vp.ErrWithdrawFromUnusedAddr, "ErrWithdrawFromUnusedAddr"},
|
{vp.ErrWithdrawFromUnusedAddr, "ErrWithdrawFromUnusedAddr"},
|
||||||
|
{vp.ErrWithdrawalTxStorage, "ErrWithdrawalTxStorage"},
|
||||||
{0xffff, "Unknown ErrorCode (65535)"},
|
{0xffff, "Unknown ErrorCode (65535)"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,23 @@ func (s outputStatus) String() string {
|
||||||
return strings[s]
|
return strings[s]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *changeAwareTx) addSelfToStore(store *wtxmgr.Store) error {
|
||||||
|
rec, err := wtxmgr.NewTxRecordFromMsgTx(tx.MsgTx, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return newError(ErrWithdrawalTxStorage, "error constructing TxRecord for storing", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := store.InsertTx(rec, nil); err != nil {
|
||||||
|
return newError(ErrWithdrawalTxStorage, "error adding tx to store", err)
|
||||||
|
}
|
||||||
|
if tx.changeIdx != -1 {
|
||||||
|
if err = store.AddCredit(rec, nil, uint32(tx.changeIdx), true); err != nil {
|
||||||
|
return newError(ErrWithdrawalTxStorage, "error adding tx credits to store", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Outputs returns a map of outbailment IDs to WithdrawalOutputs for all outputs
|
// Outputs returns a map of outbailment IDs to WithdrawalOutputs for all outputs
|
||||||
// requested in this withdrawal.
|
// requested in this withdrawal.
|
||||||
func (s *WithdrawalStatus) Outputs() map[OutBailmentID]*WithdrawalOutput {
|
func (s *WithdrawalStatus) Outputs() map[OutBailmentID]*WithdrawalOutput {
|
||||||
|
@ -924,3 +941,12 @@ func nextChangeAddress(a ChangeAddress) (ChangeAddress, error) {
|
||||||
addr, err := a.pool.ChangeAddress(seriesID, index)
|
addr, err := a.pool.ChangeAddress(seriesID, index)
|
||||||
return *addr, err
|
return *addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func storeTransactions(store *wtxmgr.Store, transactions []*changeAwareTx) error {
|
||||||
|
for _, tx := range transactions {
|
||||||
|
if err := tx.addSelfToStore(store); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestOutputSplittingNotEnoughInputs checks that an output will get split if we
|
// TestOutputSplittingNotEnoughInputs checks that an output will get split if we
|
||||||
|
@ -972,6 +973,105 @@ func TestTxFeeEstimationForLargeTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStoreTransactionsWithoutChangeOutput(t *testing.T) {
|
||||||
|
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
||||||
|
defer tearDown()
|
||||||
|
|
||||||
|
wtx := createWithdrawalTxWithStoreCredits(t, store, pool, []int64{4e6}, []int64{3e6})
|
||||||
|
tx := &changeAwareTx{MsgTx: wtx.toMsgTx(), changeIdx: int32(-1)}
|
||||||
|
if err := storeTransactions(store, []*changeAwareTx{tx}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
credits, err := store.UnspentOutputs()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(credits) != 0 {
|
||||||
|
t.Fatalf("Unexpected number of credits in txstore; got %d, want 0", len(credits))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreTransactionsWithChangeOutput(t *testing.T) {
|
||||||
|
tearDown, pool, store := TstCreatePoolAndTxStore(t)
|
||||||
|
defer tearDown()
|
||||||
|
|
||||||
|
wtx := createWithdrawalTxWithStoreCredits(t, store, pool, []int64{5e6}, []int64{1e6, 1e6})
|
||||||
|
wtx.changeOutput = wire.NewTxOut(int64(3e6), []byte{})
|
||||||
|
msgtx := wtx.toMsgTx()
|
||||||
|
tx := &changeAwareTx{MsgTx: msgtx, changeIdx: int32(len(msgtx.TxOut) - 1)}
|
||||||
|
|
||||||
|
if err := storeTransactions(store, []*changeAwareTx{tx}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sha := msgtx.TxSha()
|
||||||
|
txDetails, err := store.TxDetails(&sha)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if txDetails == nil {
|
||||||
|
t.Fatal("The new tx doesn't seem to have been stored")
|
||||||
|
}
|
||||||
|
|
||||||
|
storedTx := txDetails.TxRecord.MsgTx
|
||||||
|
outputTotal := int64(0)
|
||||||
|
for i, txOut := range storedTx.TxOut {
|
||||||
|
if int32(i) != tx.changeIdx {
|
||||||
|
outputTotal += txOut.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if outputTotal != int64(2e6) {
|
||||||
|
t.Fatalf("Unexpected output amount; got %v, want %v", outputTotal, int64(2e6))
|
||||||
|
}
|
||||||
|
|
||||||
|
inputTotal := btcutil.Amount(0)
|
||||||
|
for _, debit := range txDetails.Debits {
|
||||||
|
inputTotal += debit.Amount
|
||||||
|
}
|
||||||
|
if inputTotal != btcutil.Amount(5e6) {
|
||||||
|
t.Fatalf("Unexpected input amount; got %v, want %v", inputTotal, btcutil.Amount(5e6))
|
||||||
|
}
|
||||||
|
|
||||||
|
credits, err := store.UnspentOutputs()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(credits) != 1 {
|
||||||
|
t.Fatalf("Unexpected number of credits in txstore; got %d, want 1", len(credits))
|
||||||
|
}
|
||||||
|
changeOutpoint := wire.OutPoint{Hash: sha, Index: uint32(tx.changeIdx)}
|
||||||
|
if credits[0].OutPoint != changeOutpoint {
|
||||||
|
t.Fatalf("Credit's outpoint (%v) doesn't match the one from change output (%v)",
|
||||||
|
credits[0].OutPoint, changeOutpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createWithdrawalTxWithStoreCredits creates a new Credit in the given store
|
||||||
|
// for each entry in inputAmounts, and uses them to construct a withdrawalTx
|
||||||
|
// with one output for every entry in outputAmounts.
|
||||||
|
func createWithdrawalTxWithStoreCredits(t *testing.T, store *wtxmgr.Store, pool *Pool,
|
||||||
|
inputAmounts []int64, outputAmounts []int64) *withdrawalTx {
|
||||||
|
masters := []*hdkeychain.ExtendedKey{
|
||||||
|
TstCreateMasterKey(t, bytes.Repeat(uint32ToBytes(getUniqueID()), 4)),
|
||||||
|
TstCreateMasterKey(t, bytes.Repeat(uint32ToBytes(getUniqueID()), 4)),
|
||||||
|
TstCreateMasterKey(t, bytes.Repeat(uint32ToBytes(getUniqueID()), 4)),
|
||||||
|
}
|
||||||
|
def := TstCreateSeriesDef(t, pool, 2, masters)
|
||||||
|
TstCreateSeries(t, pool, []TstSeriesDef{def})
|
||||||
|
net := pool.Manager().ChainParams()
|
||||||
|
tx := newWithdrawalTx()
|
||||||
|
for _, c := range TstCreateSeriesCreditsOnStore(t, pool, def.SeriesID, inputAmounts, store) {
|
||||||
|
tx.addInput(c)
|
||||||
|
}
|
||||||
|
for i, amount := range outputAmounts {
|
||||||
|
request := TstNewOutputRequest(
|
||||||
|
t, uint32(i), "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", btcutil.Amount(amount), net)
|
||||||
|
tx.addOutput(request)
|
||||||
|
}
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
// checkNonEmptySigsForPrivKeys checks that every signature list in txSigs has
|
// checkNonEmptySigsForPrivKeys checks that every signature list in txSigs has
|
||||||
// one non-empty signature for every non-nil private key in the given list. This
|
// one non-empty signature for every non-nil private key in the given list. This
|
||||||
// is to make sure every signature list matches the specification at
|
// is to make sure every signature list matches the specification at
|
||||||
|
|
Loading…
Add table
Reference in a new issue