#!/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.
"""Test BIP 34, 65, 66, CSV activation at block 0"""

from test_framework.blocktools import create_coinbase, create_block
from test_framework.messages import msg_block, tx_from_hex
from test_framework.p2p import P2PInterface
from test_framework.script import (
    CScript,
    OP_TRUE,
    OP_DROP,
    OP_CHECKLOCKTIMEVERIFY,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, softfork_active

class BlockV4Test(BitcoinTestFramework):
    def set_test_params(self):
        self.num_nodes = 1
        self.extra_args = [['-whitelist=127.0.0.1', '-con_bip34height=0', '-con_bip65height=0', '-con_bip66height=0', '-con_csv_deploy_start=-1', '-acceptnonstdtxn=1']]
        self.setup_clean_chain = True

    def add_options(self, parser):
        self.add_wallet_options(parser)

    def skip_test_if_missing_module(self):
        self.skip_if_no_wallet()

    def create_transaction(self, txid, addr, *, amount, fee, locktime=0):
        """ Return signed transaction spending the first output of the input txid.
        """

        psbt = self.nodes[0].createpsbt(
            inputs=[{"txid": txid, "vout": 0}],
            outputs=[{addr: amount}, {"fee": fee}],
            locktime=locktime
        )

        for sign in [False, True]:
            for w in self.nodes[0].listwallets():
                wrpc = self.nodes[0].get_wallet_rpc(w)
                psbt = wrpc.walletprocesspsbt(psbt, sign)["psbt"]

        final_psbt = self.nodes[0].finalizepsbt(psbt)
        assert final_psbt["complete"], "Transaction not complete"

        raw_tx = final_psbt['hex']
        tx = tx_from_hex(raw_tx)

        return tx

    def run_test(self):

        # First, quick check that CSV is ACTIVE at genesis
        assert_equal(self.nodes[0].getblockcount(), 0)
        assert softfork_active(self.nodes[0], 'csv')

        peer = self.nodes[0].add_p2p_connection(P2PInterface())

        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info("Test that blocks past the genesis block must be at least version 4")

        # Create a v3 block
        tip = self.nodes[0].getbestblockhash()
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        block = create_block(int(tip, 16), create_coinbase(1), block_time)
        block.nVersion = 3
        block.solve()

        # The best block should not have changed, because...
        assert_equal(self.nodes[0].getbestblockhash(), tip)

        # ... we rejected it because it is v3
        with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000003)'.format(block.hash)]):
            # Send it to the node
            peer.send_and_ping(msg_block(block))

        self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")

        # Generate 100 blocks so that first coinbase matures
        generated_blocks = self.generate(self.nodes[0], 100)
        spendable_coinbase_txid = self.nodes[0].getblock(generated_blocks[0])['tx'][0]
        coinbase_value = self.nodes[0].decoderawtransaction(self.nodes[0].gettransaction(spendable_coinbase_txid)["hex"])["vout"][0]["value"]
        tip = generated_blocks[-1]

        # Construct a v4 block
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        block = create_block(int(tip, 16), create_coinbase(len(generated_blocks) + 1), block_time)
        block.nVersion = 4

        # Create a CLTV transaction
        spendtx = self.create_transaction(txid=spendable_coinbase_txid,
                addr=self.nodeaddress, amount=1.0, fee=coinbase_value-1, locktime=1)
        spendtx.nLockTime = 1
        spendtx.vin[0].scriptSig = CScript([OP_TRUE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] + list(CScript(spendtx.vin[0].scriptSig)))
        spendtx.rehash()

        # Add the CLTV transaction and prepare for sending
        block.vtx.append(spendtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        # Send block and check that it becomes new best block
        peer.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)

if __name__ == '__main__':
    BlockV4Test(__file__).main()
