Skip to content

Instantly share code, notes, and snippets.

@morcos
Created May 1, 2015 17:18
Show Gist options
  • Save morcos/61084ac8a33363278638 to your computer and use it in GitHub Desktop.
Save morcos/61084ac8a33363278638 to your computer and use it in GitHub Desktop.
RPC test for mempool only CLTV
#!/usr/bin/env python2
# Copyright (c) 2015 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Test CLTV code
#
from test_framework import BitcoinTestFramework
from bitcoinrpc.authproxy import JSONRPCException
from util import *
from mininode import CTransaction
from blocktools import create_coinbase, create_block
from binascii import hexlify, unhexlify
import cStringIO
import time
RPC_VERIFY_REJECTED = -26
class CLTVTest(BitcoinTestFramework):
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 1)
def setup_network(self):
self.nodes = []
self.is_network_split = False
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-relaypriority=0"]))
def run_test(self):
# Construct a CLTV P2SH
P2SH = "2NCJq1yiHGpUHKxm2fnPPXJYMmhEX22pm5h" # P2SH of "105 OP_CHECKLOCKTIMEVERIFY OP_DROP"
# Associated ScriptSig to spend the P2SH
# 6 bytes of OP_TRUE and push 4-byte redeem script of "105 OP_CHECKLOCKTIMEVERIFY OP_DROP"
SCRIPT_SIG = "0651040169b175"
# Mine 100 blocks so we can spend block at height 1 in next block which will be height 101
self.nodes[0].generate(100)
assert(self.nodes[0].getblockcount() == 100)
# Get the coinbase txid of block at height 1 and create a
# transaction spending that to our script
coinbase_txid = self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0]
inputs = [{ "txid" : coinbase_txid, "vout" : 0 }]
outputs = { P2SH : 50 }
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
signresult = self.nodes[0].signrawtransaction(rawtx)
txid = self.nodes[0].sendrawtransaction(signresult["hex"])
# Mine this transaction
self.nodes[0].generate(1)
# Construct a spending transaction
inputs = [{ "txid" : txid, "vout" : 0}]
new_address = self.nodes[0].getnewaddress()
outputs = { new_address : 50 }
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
# Decompose the transaction so we can hand modify
## version + #txin + txout hash + txout index
part1_tx = rawtx[0:8] + rawtx[8:10] + rawtx[10:74] + rawtx[74:82]
## ScriptSig(0 len) + sequence
# unused rawtx[82:84] + rawtx[84:92]
part2_tx = rawtx[92:-8]
## locktime
# unused rawtx[-8:]
# Modify sequence and insert our scriptSig
# We can now create different tx's by appending different locktimes
seq = "00000000"
mod_tx = part1_tx + SCRIPT_SIG + seq + part2_tx
# Construct a valid spending transaction
valid_tx = mod_tx + "69000000"
# Try to spend it too early
try:
self.nodes[0].sendrawtransaction(valid_tx)
raise AssertionError("Transaction shouldn't have been accepted, locktime in the future")
except JSONRPCException as e:
assert (e.error['code'] == RPC_VERIFY_REJECTED)
# Construct a transaction with low enough lock time that should fail CLTV
cltv_fail_tx = mod_tx + "65000000"
try:
self.nodes[0].sendrawtransaction(cltv_fail_tx)
raise AssertionError("Transaction shouldn't have been accepted, CLTV script fails")
except JSONRPCException as e:
assert (e.error['code'] == RPC_VERIFY_REJECTED)
# Verify that this is a only a standardness failure
# by submitting cltv_fail_tx in a block
assert(self.nodes[0].getblockcount() == 101)
# Create a CTransaction with cltv_fail_tx
t = CTransaction()
t.deserialize(cStringIO.StringIO(unhexlify(cltv_fail_tx)))
t.rehash()
# Create a CBlock which contains that transaction
hashprev = int("0x" + self.nodes[0].getbestblockhash() + "L", 0)
block = create_block(hashprev, create_coinbase(101), time.time()+100)
block.nVersion = 3
block.vtx.append(t)
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
block.solve()
# Submit the block and verify it's accepted
blockhex = hexlify(block.serialize())
self.nodes[0].submitblock(blockhex)
assert(self.nodes[0].getblockcount() == 102)
# Invalidate that block, and verify transaction isn't added to mempool
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
assert(len(self.nodes[0].getrawmempool()) == 0)
assert(self.nodes[0].getblockcount() == 101)
# Mine to block 105, valid_tx should be accepted to mempool now
self.nodes[0].generate(4)
assert(self.nodes[0].getblockcount() == 105)
self.nodes[0].sendrawtransaction(valid_tx)
assert(len(self.nodes[0].getrawmempool()) == 1)
if __name__ == '__main__':
CLTVTest().main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment