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.
|
||||
ErrSeriesIDInvalid
|
||||
|
||||
// ErrWithdrawalTxStorage indicates an error when storing withdrawal
|
||||
// transactions.
|
||||
ErrWithdrawalTxStorage
|
||||
|
||||
// lastErr is used for testing, making it possible to iterate over
|
||||
// the error codes in order to check that they all have proper
|
||||
// translations in errorCodeStrings.
|
||||
|
@ -187,6 +191,7 @@ var errorCodeStrings = map[ErrorCode]string{
|
|||
ErrTxSigning: "ErrTxSigning",
|
||||
ErrInvalidScriptHash: "ErrInvalidScriptHash",
|
||||
ErrWithdrawFromUnusedAddr: "ErrWithdrawFromUnusedAddr",
|
||||
ErrWithdrawalTxStorage: "ErrWithdrawalTxStorage",
|
||||
}
|
||||
|
||||
// String returns the ErrorCode as a human-readable name.
|
||||
|
|
|
@ -64,6 +64,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
|||
{vp.ErrTxSigning, "ErrTxSigning"},
|
||||
{vp.ErrInvalidScriptHash, "ErrInvalidScriptHash"},
|
||||
{vp.ErrWithdrawFromUnusedAddr, "ErrWithdrawFromUnusedAddr"},
|
||||
{vp.ErrWithdrawalTxStorage, "ErrWithdrawalTxStorage"},
|
||||
{0xffff, "Unknown ErrorCode (65535)"},
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,23 @@ func (s outputStatus) String() string {
|
|||
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
|
||||
// requested in this withdrawal.
|
||||
func (s *WithdrawalStatus) Outputs() map[OutBailmentID]*WithdrawalOutput {
|
||||
|
@ -924,3 +941,12 @@ func nextChangeAddress(a ChangeAddress) (ChangeAddress, error) {
|
|||
addr, err := a.pool.ChangeAddress(seriesID, index)
|
||||
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/hdkeychain"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
)
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
|
Loading…
Reference in a new issue