Skip to content

Commit

Permalink
mempool: add revocation orphan test.
Browse files Browse the repository at this point in the history
  • Loading branch information
dnldd committed Jul 9, 2018
1 parent 6ab2609 commit 871e445
Showing 1 changed file with 130 additions and 2 deletions.
132 changes: 130 additions & 2 deletions mempool/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
const (
// singleInputTicketSize is the typical size of a normal P2PKH ticket
// in bytes when the ticket has one input, rounded up.
singleInputTicketSize = int64(300)
singleInputTicketSize int64 = 300
)

// fakeChain is used by the pool harness to provide generated test utxos and
Expand Down Expand Up @@ -503,6 +503,51 @@ func (p *poolHarness) CreateVote(ticket *dcrutil.Tx) (*dcrutil.Tx, error) {
return dcrutil.NewTx(vote), nil
}

// CreateRevocation creates a revocation using the provided ticket.
func (p *poolHarness) CreateRevocation(ticket *dcrutil.Tx) (*dcrutil.Tx, error) {
ticketPurchase := ticket.MsgTx()
ticketHash := ticketPurchase.TxHash()

// Parse the ticket purchase transaction and generate the revocation value.
ticketPayKinds, ticketHash160s, ticketValues, _, _, _ :=
stake.TxSStxStakeOutputInfo(ticketPurchase)
revocationValues := stake.CalculateRewards(ticketValues,
ticketPurchase.TxOut[0].Value, 0)

// Add the ticket input.
revocation := wire.NewMsgTx()
ticketOutPoint := wire.NewOutPoint(&ticketHash, 0, wire.TxTreeStake)
ticketInput := wire.NewTxIn(ticketOutPoint,
ticketPurchase.TxOut[ticketOutPoint.Index].Value, nil)
revocation.AddTxIn(ticketInput)

// All remaining outputs pay to the output destinations and amounts tagged
// by the ticket purchase.
for i, hash160 := range ticketHash160s {
scriptFn := txscript.PayToSSRtxPKHDirect
if ticketPayKinds[i] { // P2SH
scriptFn = txscript.PayToSSRtxSHDirect
}
// Error is checking for a nil hash160, just ignore it.
script, _ := scriptFn(hash160)
revocation.AddTxOut(wire.NewTxOut(revocationValues[i], script))
}

// Sign the input.
inputToSign := 0
redeemTicketScript := ticket.MsgTx().TxOut[0].PkScript
signedScript, err := txscript.SignTxOutput(p.chainParams, revocation, inputToSign,
redeemTicketScript, txscript.SigHashAll, p,
p, revocation.TxIn[inputToSign].SignatureScript, dcrec.STEcdsaSecp256k1)
if err != nil {
return nil, err
}

revocation.TxIn[0].SignatureScript = signedScript

return dcrutil.NewTx(revocation), nil
}

// newPoolHarness returns a new instance of a pool harness initialized with a
// fake chain and a TxPool bound to it that is configured with a policy suitable
// for testing. Also, the fake chain is populated with the returned spendable
Expand Down Expand Up @@ -696,6 +741,8 @@ func TestSimpleOrphanChain(t *testing.T) {
}
}

// TestTicketPurchaseOrphan ensures that ticket purchases are orphaned when
// referenced outputs spent are from missing transactions.
func TestTicketPurchaseOrphan(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -751,6 +798,8 @@ func TestTicketPurchaseOrphan(t *testing.T) {
testPoolMembership(tc, ticket, false, true)
}

// TestVoteOrphan ensures that votes are orphaned when referenced outputs
// spent are from missing transactions.
func TestVoteOrphan(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -778,7 +827,7 @@ func TestVoteOrphan(t *testing.T) {

vote, err := harness.CreateVote(ticket)
if err != nil {
t.Fatalf("unable to create vote transaction: %v", err)
t.Fatalf("unable to create vote: %v", err)
}

// Ensure the vote is rejected because it is an orphan.
Expand Down Expand Up @@ -828,6 +877,85 @@ func TestVoteOrphan(t *testing.T) {
testPoolMembership(tc, vote, false, true)
}

// TestRevocationOrphan ensures that revocations are orphaned when
// referenced outputs spent are from missing transactions.
func TestRevocationOrphan(t *testing.T) {
t.Parallel()

harness, spendableOuts, err := newPoolHarness(&chaincfg.MainNetParams)
if err != nil {
t.Fatalf("unable to create test pool: %v", err)
}
tc := &testContext{t, harness}

// Create a regular transaction from the first spendable output
// provided by the harness.
tx, err := harness.createTx(spendableOuts[0])
if err != nil {
t.Fatalf("unable to create transaction: %v", err)
}

// Create a ticket purchase transaction spending the outputs of the
// prior regular transaction.
ticket, err := harness.CreateTicketPurchase(tx, 40000)
if err != nil {
t.Fatalf("unable to create ticket purchase transaction: %v", err)
}

harness.chain.SetHeight(harness.chainParams.StakeEnabledHeight + 1)

revocation, err := harness.CreateRevocation(ticket)
if err != nil {
t.Fatalf("unable to create revocation: %v", err)
}

// Ensure the vote is rejected because it is an orphan.
_, err = harness.txPool.ProcessTransaction(revocation,
false, false, true)
if err == nil {
t.Fatalf("ProcessTransaction: accepted transaction references " +
"outputs of unknown or fully-spent transaction")
}

testPoolMembership(tc, revocation, false, false)

// Ensure the ticket is accepted as an orphan.
_, err = harness.txPool.ProcessTransaction(ticket,
true, false, true)
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"transaction %v", err)
}

testPoolMembership(tc, ticket, true, false)

// Ensure the regular tx whose ouput is spent by the ticket is accepted.
_, err = harness.txPool.ProcessTransaction(tx, false, false, true)
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept valid "+
"transaction %v", err)
}

testPoolMembership(tc, tx, false, true)

// Generate fake mined utxos for the regular transaction and the
// ticket created.
harness.AddFakeUTXO(tx, int64(tx.MsgTx().TxIn[0].BlockHeight))
harness.AddFakeUTXO(ticket, int64(ticket.MsgTx().TxIn[0].BlockHeight))

// Ensure the previously rejected revocation is accepted now since all referenced
// utxos can now be found.
_, err = harness.txPool.ProcessTransaction(revocation, false, false, true)
if err != nil {
t.Fatalf("ProcessTransaction: failed to accept orphan "+
"transaction %v", err)
}

testPoolMembership(tc, tx, false, true)
testPoolMembership(tc, ticket, false, true)
testPoolMembership(tc, revocation, false, true)
}

// TestOrphanReject ensures that orphans are properly rejected when the allow
// orphans flag is not set on ProcessTransaction.
func TestOrphanReject(t *testing.T) {
Expand Down

0 comments on commit 871e445

Please sign in to comment.