2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2021-07-28 13:57:16 +02:00
# Copyright (c) 2015-2021 The Bitcoin Core developers
2016-03-19 20:58:06 +01:00
# Distributed under the MIT software license, see the accompanying
2015-04-28 18:41:54 +02:00
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-18 00:34:40 +01:00
""" Test BIP66 (DER SIG).
2021-08-05 12:06:23 +02:00
Test the DERSIG soft - fork activation on regtest .
2017-01-18 00:34:40 +01:00
"""
2015-04-28 18:41:54 +02:00
2021-05-25 14:39:45 +02:00
from test_framework . blocktools import (
create_block ,
create_coinbase ,
)
2018-07-07 00:10:35 +02:00
from test_framework . messages import msg_block
2020-07-19 09:47:05 +02:00
from test_framework . p2p import P2PInterface
2015-05-02 12:53:35 +02:00
from test_framework . script import CScript
2018-07-07 00:10:35 +02:00
from test_framework . test_framework import BitcoinTestFramework
2018-08-24 21:26:42 +02:00
from test_framework . util import (
assert_equal ,
)
2021-05-25 14:39:45 +02:00
from test_framework . wallet import (
MiniWallet ,
MiniWalletMode ,
)
2015-04-28 18:41:54 +02:00
2017-06-28 22:56:37 +02:00
# A canonical signature consists of:
2015-04-28 18:41:54 +02:00
# <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
def unDERify ( tx ) :
2017-01-18 00:34:40 +01:00
"""
2015-04-28 18:41:54 +02:00
Make the signature in vin 0 of a tx non - DER - compliant ,
by adding padding after the S - value .
2017-01-18 00:34:40 +01:00
"""
2015-04-28 18:41:54 +02:00
scriptSig = CScript ( tx . vin [ 0 ] . scriptSig )
newscript = [ ]
for i in scriptSig :
if ( len ( newscript ) == 0 ) :
2016-04-10 16:54:28 +02:00
newscript . append ( i [ 0 : - 1 ] + b ' \0 ' + i [ - 1 : ] )
2015-04-28 18:41:54 +02:00
else :
newscript . append ( i )
tx . vin [ 0 ] . scriptSig = CScript ( newscript )
2017-06-28 22:56:37 +02:00
2021-08-27 16:53:11 +02:00
DERSIG_HEIGHT = 102
2017-06-28 22:56:37 +02:00
class BIP66Test ( BitcoinTestFramework ) :
2017-06-10 00:21:21 +02:00
def set_test_params ( self ) :
2015-04-28 18:41:54 +02:00
self . num_nodes = 1
2020-02-25 17:38:56 +01:00
self . extra_args = [ [
2021-08-27 16:53:11 +02:00
f ' -testactivationheight=dersig@ { DERSIG_HEIGHT } ' ,
2020-02-25 17:38:56 +01:00
' -whitelist=noban@127.0.0.1 ' ,
' -par=1 ' , # Use only one script thread to get the exact log msg for testing
] ]
2017-06-28 22:56:37 +02:00
self . setup_clean_chain = True
2020-03-13 16:10:36 +01:00
self . rpc_timeout = 240
2015-04-28 18:41:54 +02:00
2021-05-25 14:39:45 +02:00
def create_tx ( self , input_txid ) :
utxo_to_spend = self . miniwallet . get_utxo ( txid = input_txid , mark_as_spent = False )
return self . miniwallet . create_self_transfer ( from_node = self . nodes [ 0 ] , utxo_to_spend = utxo_to_spend ) [ ' tx ' ]
2018-09-09 19:32:37 +02:00
2019-07-02 20:23:04 +02:00
def test_dersig_info ( self , * , is_active ) :
2019-05-20 20:58:09 +02:00
assert_equal ( self . nodes [ 0 ] . getblockchaininfo ( ) [ ' softforks ' ] [ ' bip66 ' ] ,
2019-07-02 20:23:04 +02:00
{
2019-05-20 20:58:09 +02:00
" active " : is_active ,
" height " : DERSIG_HEIGHT ,
" type " : " buried " ,
2019-07-02 20:23:04 +02:00
} ,
)
2015-04-28 18:41:54 +02:00
def run_test ( self ) :
2020-09-04 03:05:26 +02:00
peer = self . nodes [ 0 ] . add_p2p_connection ( P2PInterface ( ) )
2021-05-25 14:39:45 +02:00
self . miniwallet = MiniWallet ( self . nodes [ 0 ] , mode = MiniWalletMode . RAW_P2PK )
2017-08-24 21:36:02 +02:00
2019-07-02 20:23:04 +02:00
self . test_dersig_info ( is_active = False )
2017-06-28 22:56:37 +02:00
self . log . info ( " Mining %d blocks " , DERSIG_HEIGHT - 2 )
2021-08-19 17:10:24 +02:00
self . coinbase_txids = [ self . nodes [ 0 ] . getblock ( b ) [ ' tx ' ] [ 0 ] for b in self . generate ( self . miniwallet , DERSIG_HEIGHT - 2 ) ]
2017-06-28 22:56:37 +02:00
self . log . info ( " Test that a transaction with non-DER signature can still appear in a block " )
2021-05-25 14:39:45 +02:00
spendtx = self . create_tx ( self . coinbase_txids [ 0 ] )
2015-04-28 18:41:54 +02:00
unDERify ( spendtx )
spendtx . rehash ( )
2017-06-28 22:56:37 +02:00
tip = self . nodes [ 0 ] . getbestblockhash ( )
block_time = self . nodes [ 0 ] . getblockheader ( tip ) [ ' mediantime ' ] + 1
2021-11-15 18:29:13 +01:00
block = create_block ( int ( tip , 16 ) , create_coinbase ( DERSIG_HEIGHT - 1 ) , block_time , txlist = [ spendtx ] )
2015-04-28 18:41:54 +02:00
block . solve ( )
2021-08-05 12:06:23 +02:00
assert_equal ( self . nodes [ 0 ] . getblockcount ( ) , DERSIG_HEIGHT - 2 )
2019-05-20 20:58:09 +02:00
self . test_dersig_info ( is_active = False ) # Not active as of current tip and next block does not need to obey rules
2020-09-04 03:05:26 +02:00
peer . send_and_ping ( msg_block ( block ) )
2021-08-05 12:06:23 +02:00
assert_equal ( self . nodes [ 0 ] . getblockcount ( ) , DERSIG_HEIGHT - 1 )
2019-05-20 20:58:09 +02:00
self . test_dersig_info ( is_active = True ) # Not active as of current tip, but next block must obey rules
2017-06-28 22:56:37 +02:00
assert_equal ( self . nodes [ 0 ] . getbestblockhash ( ) , block . hash )
self . log . info ( " Test that blocks must now be at least version 3 " )
tip = block . sha256
block_time + = 1
2021-11-15 16:13:56 +01:00
block = create_block ( tip , create_coinbase ( DERSIG_HEIGHT ) , block_time , version = 2 )
2015-04-28 18:41:54 +02:00
block . solve ( )
2021-06-11 08:09:33 +02:00
with self . nodes [ 0 ] . assert_debug_log ( expected_msgs = [ f ' { block . hash } , bad-version(0x00000002) ' ] ) :
2020-09-04 03:05:26 +02:00
peer . send_and_ping ( msg_block ( block ) )
2018-08-24 21:26:42 +02:00
assert_equal ( int ( self . nodes [ 0 ] . getbestblockhash ( ) , 16 ) , tip )
2020-09-04 03:05:26 +02:00
peer . sync_with_ping ( )
2017-06-28 22:56:37 +02:00
self . log . info ( " Test that transactions with non-DER signatures cannot appear in a block " )
2021-08-27 16:36:17 +02:00
block . nVersion = 4
2017-06-28 22:56:37 +02:00
2021-05-25 14:39:45 +02:00
spendtx = self . create_tx ( self . coinbase_txids [ 1 ] )
2016-07-22 01:27:55 +02:00
unDERify ( spendtx )
spendtx . rehash ( )
2017-06-28 22:56:37 +02:00
# First we show that this tx is valid except for DERSIG by getting it
2018-06-23 22:16:54 +02:00
# rejected from the mempool for exactly that reason.
assert_equal (
2021-01-07 16:47:35 +01:00
[ {
' txid ' : spendtx . hash ,
' wtxid ' : spendtx . getwtxid ( ) ,
' allowed ' : False ,
' reject-reason ' : ' non-mandatory-script-verify-flag (Non-canonical DER signature) ' ,
} ] ,
self . nodes [ 0 ] . testmempoolaccept ( rawtxs = [ spendtx . serialize ( ) . hex ( ) ] , maxfeerate = 0 ) ,
2018-06-23 22:16:54 +02:00
)
2017-06-28 22:56:37 +02:00
2018-06-23 22:16:54 +02:00
# Now we verify that a block with this transaction is also invalid.
2016-07-22 01:27:55 +02:00
block . vtx . append ( spendtx )
block . hashMerkleRoot = block . calc_merkle_root ( )
block . solve ( )
2021-06-11 08:09:33 +02:00
with self . nodes [ 0 ] . assert_debug_log ( expected_msgs = [ f ' CheckInputScripts on { block . vtx [ - 1 ] . hash } failed with non-mandatory-script-verify-flag (Non-canonical DER signature) ' ] ) :
2020-09-04 03:05:26 +02:00
peer . send_and_ping ( msg_block ( block ) )
2018-08-24 21:26:42 +02:00
assert_equal ( int ( self . nodes [ 0 ] . getbestblockhash ( ) , 16 ) , tip )
2020-09-04 03:05:26 +02:00
peer . sync_with_ping ( )
2017-06-28 22:56:37 +02:00
2021-08-27 16:36:17 +02:00
self . log . info ( " Test that a block with a DERSIG-compliant transaction is accepted " )
2021-05-25 14:39:45 +02:00
block . vtx [ 1 ] = self . create_tx ( self . coinbase_txids [ 1 ] )
2017-06-28 22:56:37 +02:00
block . hashMerkleRoot = block . calc_merkle_root ( )
2015-04-28 18:41:54 +02:00
block . solve ( )
2017-06-28 22:56:37 +02:00
2019-05-20 20:58:09 +02:00
self . test_dersig_info ( is_active = True ) # Not active as of current tip, but next block must obey rules
2020-09-04 03:05:26 +02:00
peer . send_and_ping ( msg_block ( block ) )
2019-07-02 20:23:04 +02:00
self . test_dersig_info ( is_active = True ) # Active as of current tip
2017-06-28 22:56:37 +02:00
assert_equal ( int ( self . nodes [ 0 ] . getbestblockhash ( ) , 16 ) , block . sha256 )
2015-04-28 18:41:54 +02:00
2019-07-02 20:23:04 +02:00
2015-04-28 18:41:54 +02:00
if __name__ == ' __main__ ' :
BIP66Test ( ) . main ( )