forked from ElementsProject/elements
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MERGE-FIX: Update compatibility functional tests
I should have split this into one commit copying the entire folder and one where I fix the ELEMENTS-based things. However, grepping the diff for "elements" case-insensitive makes you catch all differences. The next commit will contain the more subtle differences.
- Loading branch information
1 parent
3a01200
commit 420e38a
Showing
72 changed files
with
2,763 additions
and
1,012 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright (c) 2015-2018 The Bitcoin Core developers | ||
# Distributed under the MIT software license, see the accompanying | ||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
""" | ||
Templates for constructing various sorts of invalid transactions. | ||
These templates (or an iterator over all of them) can be reused in different | ||
contexts to test using a number of invalid transaction types. | ||
Hopefully this makes it easier to get coverage of a full variety of tx | ||
validation checks through different interfaces (AcceptBlock, AcceptToMemPool, | ||
etc.) without repeating ourselves. | ||
Invalid tx cases not covered here can be found by running: | ||
$ diff \ | ||
<(grep -IREho "bad-txns[a-zA-Z-]+" src | sort -u) \ | ||
<(grep -IEho "bad-txns[a-zA-Z-]+" test/functional/data/invalid_txs.py | sort -u) | ||
""" | ||
import abc | ||
|
||
from test_framework.messages import CTransaction, CTxIn, CTxOut, COutPoint | ||
from test_framework import script as sc | ||
from test_framework.blocktools import create_tx_with_script, MAX_BLOCK_SIGOPS | ||
|
||
basic_p2sh = sc.CScript([sc.OP_HASH160, sc.hash160(sc.CScript([sc.OP_0])), sc.OP_EQUAL]) | ||
|
||
|
||
class BadTxTemplate: | ||
"""Allows simple construction of a certain kind of invalid tx. Base class to be subclassed.""" | ||
__metaclass__ = abc.ABCMeta | ||
|
||
# The expected error code given by bitcoind upon submission of the tx. | ||
reject_reason = "" | ||
|
||
# Only specified if it differs from mempool acceptance error. | ||
block_reject_reason = "" | ||
|
||
# Do we expect to be disconnected after submitting this tx? | ||
expect_disconnect = False | ||
|
||
# Is this tx considered valid when included in a block, but not for acceptance into | ||
# the mempool (i.e. does it violate policy but not consensus)? | ||
valid_in_block = False | ||
|
||
def __init__(self, *, spend_tx=None, spend_block=None): | ||
self.spend_tx = spend_block.vtx[0] if spend_block else spend_tx | ||
self.spend_avail = sum(o.nValue for o in self.spend_tx.vout) | ||
self.valid_txin = CTxIn(COutPoint(self.spend_tx.sha256, 0), b"", 0xffffffff) | ||
|
||
@abc.abstractmethod | ||
def get_tx(self, *args, **kwargs): | ||
"""Return a CTransaction that is invalid per the subclass.""" | ||
pass | ||
|
||
|
||
class OutputMissing(BadTxTemplate): | ||
reject_reason = "bad-txns-vout-empty" | ||
expect_disconnect = False | ||
|
||
def get_tx(self): | ||
tx = CTransaction() | ||
tx.vin.append(self.valid_txin) | ||
tx.calc_sha256() | ||
return tx | ||
|
||
|
||
class InputMissing(BadTxTemplate): | ||
reject_reason = "bad-txns-vin-empty" | ||
expect_disconnect = False | ||
|
||
def get_tx(self): | ||
tx = CTransaction() | ||
tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE] * 100))) | ||
tx.calc_sha256() | ||
return tx | ||
|
||
|
||
class SizeTooSmall(BadTxTemplate): | ||
reject_reason = "tx-size-small" | ||
expect_disconnect = False | ||
valid_in_block = True | ||
|
||
def get_tx(self): | ||
tx = CTransaction() | ||
tx.vin.append(self.valid_txin) | ||
tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE]))) | ||
tx.calc_sha256() | ||
return tx | ||
|
||
|
||
class BadInputOutpointIndex(BadTxTemplate): | ||
# Won't be rejected - nonexistent outpoint index is treated as an orphan since the coins | ||
# database can't distinguish between spent outpoints and outpoints which never existed. | ||
reject_reason = None | ||
expect_disconnect = False | ||
|
||
def get_tx(self): | ||
num_indices = len(self.spend_tx.vin) | ||
bad_idx = num_indices + 100 | ||
|
||
tx = CTransaction() | ||
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256, bad_idx), b"", 0xffffffff)) | ||
tx.vout.append(CTxOut(0, basic_p2sh)) | ||
tx.calc_sha256() | ||
return tx | ||
|
||
|
||
class DuplicateInput(BadTxTemplate): | ||
reject_reason = 'bad-txns-inputs-duplicate' | ||
expect_disconnect = True | ||
|
||
def get_tx(self): | ||
tx = CTransaction() | ||
tx.vin.append(self.valid_txin) | ||
tx.vin.append(self.valid_txin) | ||
tx.vout.append(CTxOut(1, basic_p2sh)) | ||
tx.calc_sha256() | ||
return tx | ||
|
||
|
||
class NonexistentInput(BadTxTemplate): | ||
reject_reason = None # Added as an orphan tx. | ||
expect_disconnect = False | ||
|
||
def get_tx(self): | ||
tx = CTransaction() | ||
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256 + 1, 0), b"", 0xffffffff)) | ||
tx.vin.append(self.valid_txin) | ||
tx.vout.append(CTxOut(1, basic_p2sh)) | ||
tx.calc_sha256() | ||
return tx | ||
|
||
|
||
class SpendTooMuch(BadTxTemplate): | ||
reject_reason = 'bad-txns-in-belowout' | ||
expect_disconnect = True | ||
|
||
def get_tx(self): | ||
return create_tx_with_script( | ||
self.spend_tx, 0, script_pub_key=basic_p2sh, amount=(self.spend_avail + 1)) | ||
|
||
|
||
class SpendNegative(BadTxTemplate): | ||
reject_reason = 'bad-txns-vout-negative' | ||
expect_disconnect = True | ||
|
||
def get_tx(self): | ||
return create_tx_with_script(self.spend_tx, 0, amount=-1) | ||
|
||
|
||
class InvalidOPIFConstruction(BadTxTemplate): | ||
reject_reason = "mandatory-script-verify-flag-failed (Invalid OP_IF construction)" | ||
expect_disconnect = True | ||
valid_in_block = True | ||
|
||
def get_tx(self): | ||
return create_tx_with_script( | ||
self.spend_tx, 0, script_sig=b'\x64' * 35, | ||
amount=(self.spend_avail // 2)) | ||
|
||
|
||
class TooManySigops(BadTxTemplate): | ||
reject_reason = "bad-txns-too-many-sigops" | ||
block_reject_reason = "bad-blk-sigops, out-of-bounds SigOpCount" | ||
expect_disconnect = False | ||
|
||
def get_tx(self): | ||
lotsa_checksigs = sc.CScript([sc.OP_CHECKSIG] * (MAX_BLOCK_SIGOPS)) | ||
return create_tx_with_script( | ||
self.spend_tx, 0, | ||
script_pub_key=lotsa_checksigs, | ||
amount=1) | ||
|
||
|
||
def iter_all_templates(): | ||
"""Iterate through all bad transaction template types.""" | ||
return BadTxTemplate.__subclasses__() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.