votingpool: API to store withdrawal txs in the txstore

This commit is contained in:
Guilherme Salgado 2015-02-18 12:22:03 +00:00
parent 09c391cc38
commit d050a32cb2
4 changed files with 132 additions and 0 deletions

View file

@ -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.

View file

@ -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)"},
}

View file

@ -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
}

View file

@ -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