#!/usr/bin/env python3
# Copyright (c) 2016 The Bitcoin Core developers
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
    assert_approx,
    assert_equal,
)

class CTTest(BitcoinTestFramework):
    def set_test_params(self):
        self.num_nodes = 3
        self.setup_clean_chain = True
        args = [
            "-anyonecanspendaremine=1",
            "-con_blocksubsidy=0",
            "-con_connect_genesis_outputs=1",
            "-initialfreecoins=2100000000000000",
            "-txindex=1",
            "-blindedaddresses=1",
            "-minrelaytxfee=0.00000100",
            "-blockmintxfee=0.00000100",
            "-fallbackfee=0.00000100",
        ]
        self.extra_args = [
            # node 0 does not accept nor create discounted CTs
            args + ["-acceptdiscountct=0", "-creatediscountct=0"],
            # node 1 accepts but does not create discounted CTs
            args + ["-acceptdiscountct=1", "-creatediscountct=0"],
            # node 2 both accepts and creates discounted CTs
            # check that 'create' overrides 'accept'
            args + ["-acceptdiscountct=0", "-creatediscountct=1"],
        ]

    def skip_test_if_missing_module(self):
        self.skip_if_no_wallet()
        self.skip_if_no_bdb()

    def run_test(self):
        feerate = 1.0

        node0 = self.nodes[0]
        node1 = self.nodes[1]
        node2 = self.nodes[2]

        self.generate(node0, 101)
        balance = node0.getbalance()
        assert_equal(balance['bitcoin'], 21000000)

        self.log.info("Create UTXOs")
        many = {}
        num = 25
        for i in range(num):
            addr = node0.getnewaddress()
            info = node0.getaddressinfo(addr)
            many[info['unconfidential']] = 1
        for i in range(10):
            addr = node1.getnewaddress()
            info = node1.getaddressinfo(addr)
            many[info['unconfidential']] = 1
        for i in range(10):
            addr = node2.getnewaddress()
            info = node2.getaddressinfo(addr)
            many[info['unconfidential']] = 1

        txid = node0.sendmany("", many)
        self.generate(node0, 1)

        self.log.info("Send explicit tx to node 0")
        addr = node0.getnewaddress()
        info = node0.getaddressinfo(addr)
        txid = node0.sendtoaddress(info['unconfidential'], 1.0, "", "", False, None, None, None, None, None, None, feerate)
        tx = node0.gettransaction(txid, True, True)
        decoded = tx['decoded']
        vin = decoded['vin']
        vout = decoded['vout']
        assert_equal(len(vin), 2)
        assert_equal(len(vout), 3)
        assert_equal(tx['fee']['bitcoin'], Decimal('-0.00000326'))
        assert_equal(decoded['vsize'], 326)
        # tx weight can be 1301 or 1302, accept both
        assert_approx(decoded['weight'], 1301.5, 0.5)
        self.generate(node0, 1)
        tx = node1.getrawtransaction(txid, True)
        # tx discountweight can be 1301 or 1302, accept both
        assert_approx(tx['discountweight'], 1301.5, 0.5)
        assert_equal(tx['discountvsize'], 326)

        self.log.info("Send confidential tx to node 0")
        addr = node0.getnewaddress()
        info = node0.getaddressinfo(addr)
        txid = node0.sendtoaddress(info['confidential'], 1.0, "", "", False, None, None, None, None, None, None, feerate)
        tx = node0.gettransaction(txid, True, True)
        decoded = tx['decoded']
        vin = decoded['vin']
        vout = decoded['vout']
        assert_equal(len(vin), 2)
        assert_equal(len(vout), 3)
        assert_equal(tx['fee']['bitcoin'], Decimal('-0.00002575'))
        assert_equal(decoded['vsize'], 2575)
        # tx weight can be 10299 or 10300, accept both
        assert_approx(decoded['weight'], 10299.5, 0.5)
        self.generate(node0, 1)
        tx = node1.getrawtransaction(txid, True)
        # tx discountweight can be 1301 or 1302, accept both
        assert_approx(tx['discountweight'], 1301.5, 0.5)
        assert_equal(tx['discountvsize'], 326) # node1 has discountvsize

        self.log.info("Send explicit tx to node 1")
        addr = node1.getnewaddress()
        info = node1.getaddressinfo(addr)
        txid = node0.sendtoaddress(info['unconfidential'], 1.0, "", "", False, None, None, None, None, None, None, feerate)
        tx = node0.gettransaction(txid, True, True)
        decoded = tx['decoded']
        vin = decoded['vin']
        vout = decoded['vout']
        assert_equal(len(vin), 2)
        assert_equal(len(vout), 3)
        assert_equal(tx['fee']['bitcoin'], Decimal('-0.00000326'))
        assert_equal(decoded['vsize'], 326)
        # tx weight can be 1301 or 1302, accept both
        assert_approx(decoded['weight'], 1301.5, 0.5)
        self.generate(node0, 1)
        tx = node1.getrawtransaction(txid, True)
        # tx weight can be 1301 or 1302, accept both
        assert_approx(tx['discountweight'], 1301.5, 0.5)
        assert_equal(tx['discountvsize'], 326)

        self.log.info("Send confidential (undiscounted) tx to node 1")
        addr = node1.getnewaddress()
        info = node1.getaddressinfo(addr)
        txid = node0.sendtoaddress(info['confidential'], 1.0, "", "", False, None, None, None, None, None, None, feerate)
        tx = node0.gettransaction(txid, True, True)
        decoded = tx['decoded']
        vin = decoded['vin']
        vout = decoded['vout']
        assert_equal(len(vin), 2)
        assert_equal(len(vout), 3)
        assert_equal(tx['fee']['bitcoin'], Decimal('-0.00002575'))
        assert_equal(decoded['vsize'], 2575)
        # tx weight can be 10299 or 10300, accept both
        assert_approx(decoded['weight'], 10299.5, 0.5)
        self.generate(node0, 1)
        tx = node1.getrawtransaction(txid, True)
        # tx discountweight can be 1301 or 1302, accept both
        assert_approx(tx['discountweight'], 1301.5, 0.5)
        assert_equal(tx['discountvsize'], 326) # node1 has discountvsize

        self.log.info("Send confidential (discounted) tx to node 1")
        bitcoin = 'b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23'
        addr = node1.getnewaddress()
        info = node1.getaddressinfo(addr)
        txid = node2.sendtoaddress(info['confidential'], 1.0, "", "", False, None, None, None, None, None, None, feerate)
        # node0 won't accept or relay the tx
        self.sync_mempools([node1, node2])
        assert_equal(node0.getrawmempool(), [])
        self.generate(node2, 1, sync_fun=self.sync_blocks)
        for node in [node2, node1]:
            tx = node.gettransaction(txid, True, True)
            decoded = tx['decoded']
            vin = decoded['vin']
            vout = decoded['vout']
            assert_equal(len(vin), 2)
            assert_equal(len(vout), 3)
            if 'bitcoin' in decoded['fee']:
                assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000326'))
            else:
                assert_equal(decoded['fee'][bitcoin], Decimal('0.00000326'))
            assert_equal(decoded['vsize'], 2575)
            # tx weight can be 10299 or 10300, accept both
            assert_approx(decoded['weight'], 10299.5, 0.5)
            # tx discountweight can be 1301 or 1302, accept both
            assert_approx(decoded['discountweight'], 1301.5, 0.5)
            assert_equal(decoded['discountvsize'], 326)

        # node0 only has vsize
        tx = node0.getrawtransaction(txid, True)
        assert_equal(tx['vsize'], 2575)

        # check liquidv1 min feerate
        feerate = 0.1
        self.log.info(f"Send confidential (discounted) tx to node 1 at {feerate} sats/vb")
        addr = node1.getnewaddress()
        info = node1.getaddressinfo(addr)
        txid = node2.sendtoaddress(info['confidential'], 1.0, "", "", False, None, None, None, None, None, None, feerate)
        self.sync_mempools([node1, node2])
        assert_equal(node0.getrawmempool(), [])
        self.generate(node2, 1, sync_fun=self.sync_blocks)
        for node in [node2, node1]:
            tx = node.gettransaction(txid, True, True)
            assert_equal(tx["confirmations"], 1)
            decoded = tx['decoded']
            vin = decoded['vin']
            vout = decoded['vout']
            assert_equal(len(vin), 2)
            assert_equal(len(vout), 3)
            if 'bitcoin' in decoded['fee']:
                assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000033'))
            else:
                assert_equal(decoded['fee'][bitcoin], Decimal('0.00000033'))
            assert_equal(decoded['vsize'], 2575)
            # tx weight can be 10299 or 10300, accept both
            assert_approx(decoded['weight'], 10299.5, 0.5)
            # tx discountweight can be 1301 or 1302, accept both
            assert_approx(decoded['discountweight'], 1301.5, 0.5)
            assert_equal(decoded['discountvsize'], 326)
        # node0 only has vsize
        tx = node0.getrawtransaction(txid, True)
        assert_equal(tx['vsize'], 2575)

        # check transaction package
        self.log.info("Create discounted package")
        addr = node1.getnewaddress()
        addr2 = node1.getnewaddress()
        utxo = node1.listunspent()[0]
        fee = Decimal('0.00000035')
        inputs = [{"txid": utxo["txid"], "vout": utxo["vout"]}]
        change = Decimal('0.00001000')
        amount = utxo["amount"] - change - fee
        outputs = [
            {addr: amount},
            {addr2: change},
            {"fee": fee}
        ]
        raw = node1.createrawtransaction(inputs, outputs)
        blind = node1.blindrawtransaction(raw, False)
        signed = node1.signrawtransactionwithwallet(blind)
        assert_equal(signed["complete"], True)
        test = node1.testmempoolaccept([signed['hex']])
        assert_equal(test[0]["allowed"], True)
        txid = node1.sendrawtransaction(signed['hex'])
        tx = node1.gettransaction(txid, True, True)
        assert_equal(tx['decoded']['discountvsize'], 257)

        for i in range(24):
            self.log.info(f"Add package descendant {i+1}")
            addr = node1.getnewaddress()
            addr2 = node1.getnewaddress()
            fee = Decimal('0.00000035')
            change = Decimal('0.00001000')
            inputs = [{"txid": txid, "vout": 0}]
            amount -= change + fee
            outputs = [
                {addr: amount},
                {addr2: change},
                {"fee": fee}
            ]
            raw = node1.createrawtransaction(inputs, outputs)
            blind = node1.blindrawtransaction(raw, False)
            signed = node1.signrawtransactionwithwallet(blind)
            assert_equal(signed["complete"], True)
            hex = signed["hex"]
            test = node1.testmempoolaccept([hex])
            assert_equal(test[0]["allowed"], True)
            txid = node1.sendrawtransaction(hex)
            tx = node1.gettransaction(txid, True, True)
            assert_equal(tx['decoded']['discountvsize'], 257)
            assert_equal(len(node1.getrawmempool()), i + 2)

        assert_equal(len(node1.getrawmempool()), 25)
        self.log.info("Mine the package")
        self.generate(node1, 1, sync_fun=self.sync_blocks)
        assert_equal(len(node1.getrawmempool()), 0)
        tx = node1.gettransaction(txid, True, True)
        assert_equal(tx["confirmations"], 1)
        self.log.info("Success")

        self.log.info("Decode and check test vectors")
        # 1 input 2 output pegin
        hex = "020000000101557e4c2f0628dfac37ac4411cb6e35dd225c9ba5addff102862daae41b637c990b00004000ffffffff02016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f0100000000000beb6d0016001421716e7df11103ea809accf1ab2bdd4080449eb7016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f010000000000000042000000000000000002473044022001a6072622c5e93729239647c2fe7f66f272d7aa2812d7438007e334ee474bde02204ffdcfbbd604587577e025ae934cd99e81462338fdc02599a2d38992b389f37001210255a1c00e0dd2116252a5eb25972d0fea758cad0ab7a5d24878f9d6a19727c1a20608afeb0b0000000000206d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000016001441fd857c961affbcbf2e4d1045c566e67d76dfd5fde703020000000e6d76937ac1018b78fe283a8944a814ece2140f11ec5179c7d3c492b825131c6c0a00000000feffffff18e04258d7c83015edf8136fc1bf8f2131d9b68af090260b00d9a0bfdf6010d80b00000000feffffff8efa3a72e3733ba0e9eac82d8a2cb4e793ea62d300f7b30cdd233ffe108e9d6b0c00000000feffffffac11c2632695df72aec04ce04a25de4b37458f601661b7ead396d05182c3f17c0700000000feffffffaeead849b5299e24e4d57561dba357991b0a45520cad3b688e6ca7cb28a40f860b00000000feffffff416d0631f4d5cea49b2792788a61ec794080a73dd8935dbf445144f6c0e0810b0c00000000feffffffe3682c1aaf6a5af8a9e2892aadb43d7d496dce911fd3c12cee8d42491eab00780b00000000feffffffe3682c1aaf6a5af8a9e2892aadb43d7d496dce911fd3c12cee8d42491eab00780600000000fefffffffef923d8b219b40eb7c337c5c2fa70dec7f1dbbe7b6b487ccfac6b191fbfaf790300000000feffffff07ff63b29dd68b4eff0a45c8830f7d4a9908287ec919c9e8d9999510c14b02a10800000000feffffff7671526352f34e9f8acaa93648152ff5409510448aef3657aaaf44023cb23bcf0900000000feffffffabf32bbcae8e67f53871b1ee5d0b5a49810386df6c122ae0f6b1c676b59e17480800000000feffffffb8a915b1d5afe373c2f753373cf98256cf4e8a960d285cb62cd9394f2316a8b40700000000feffffffb353ab262b8d23ba4b67bab0adde47836f9a63d5f597221aba8e7b6516c8e5b50c00000000feffffff0d7e8a9b0100000000160014cdf10594603564acd1ebff1596740d7a27da04a9afb4db0700000000160014e311d2f332c317fd792efd1eca1e3ebd60f5262f4e0a62040000000016001443d7e7908e36b3d674f022da05ec223c3589e93c4e0a620400000000160014506a6a3582c9916d49f946e555dac4090fafa21ba373820000000000160014724716f7fbbe5cce379d28df2bdb57101bf4b0b34e0a6204000000001600149b5ba30ba73140301e2c06013ec8743baae9b5964e0a620400000000160014e9a45eede70eedea3c79e2df76fb2ad176b018298591020000000000160014fc53c7d9e1f378f10f0a02f392bddd5cb4b6e7f64e0a620400000000160014dceadbe1d19b5f01cd297326c052182863e0c75f2c853b0200000000160014c1d31cc004ab8a852d09e0f1b21b9612ca557cdd4e0a6204000000001600147ce8ff3fe3a455de4c7556beae19530015bd2619afeb0b0000000000220020b29f97a86251ea0030726261d7703f4015d2644a8f47c9da24468370f9f2f4614e0a6204000000001600144a617e14b88cd99aecd587b60dbb8f7495302531e7e70c00fd1a0200000520dfb88d3a4d93486a906de316f7d36b9aadbe0665cf4e0000000000000000000051e75174de68a0846322c6807468a8d2e9d3ea3c06cc590d4e4bd4632acd1fbe70ae5866f0550317671357d4351000000e692fd6ac8218da7f8d3e95dc49c9a80ec07e5394ffb3a42f526294a9f8aa3f8733645a9dccd40eb4fb1c9777c28303856afd2c757ea56a0825f8955144ad3241ef8fe75a898e7ede330a18721d6745b9141ef03b7b02e6e58663b07bbfbbc2a9557e4c2f0628dfac37ac4411cb6e35dd225c9ba5addff102862daae41b637c998ec2b24d7489e27acc23b73c6191b2826e2f253745d4749f836d1da7a8430d80e32cbb8c149e8423b32f2e48701de69bca0a5b510715661076a0096ce1c6a800b4c51b4a468dec699b4f013ebc46381a55ea98faf249f936d5fe96ee4f60635134b8302c3fc41d305e116c82633fba821a2f73a9097361bffacc960b006cd69593ae8512f0bdf6121271d75b6ce43d29182e024822c745a0d216df8e69323febca09677d55fb0224c7bb7b01361c09050f5cc1e05965c404c3f2e3b6546b83326d910b78565da835fa54a0d32be2daabebb402d1a2a0e50920e61c3ddaeb0d7bde57e98ef84c8e49613c6e5fa60cf28c60e1334dd8272f8d8a636b4c59109f02760d6712f55ca5c2ba86c81dba5073980b62049fc6099dc1518eaca05c8e3026ecd20ce9d0d50c758aa8fbaeacc3a7503a449b66d740a1dc0af1252c2b5ae28804ff76010000000000"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 1)
        assert_equal(len(decoded['vout']), 2)
        assert_equal(decoded['vsize'], 601)
        assert_equal(decoded['discountvsize'], 601)
        assert decoded["vin"][0]["is_pegin"]

        # 1 input 2 output
        hex = "[encoded data, 9352 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 1)
        assert_equal(len(decoded['vout']), 2)
        assert_equal(decoded['vsize'], 1333)
        assert_equal(decoded['discountweight'], 863)
        assert_equal(decoded['discountvsize'], 216)

        # 1 input 3 output
        hex = "[encoded data, 17958 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 1)
        assert_equal(len(decoded['vout']), 3)
        assert_equal(decoded['vsize'], 2527)
        assert_equal(decoded['discountweight'], 1173)
        assert_equal(decoded['discountvsize'], 294)

        # 2 input 3 output explicit
        hex = "0200000001027a2ac82252ab075b902a5515a60c21992b82839a835c3bdd14693df06d2875332c00000000fdffffff7a2ac82252ab075b902a5515a60c21992b82839a835c3bdd14693df06d2875330700000000fdffffff0301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5dfba001600140dee90a6ee92e97eb19e30961c4c46927efa438101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e1000016001419abecb8f7968b453d635050bab49e0534f3f19c01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000146000066000000000002473044022052edc38055ef30c6693be717c52cca43348204c27fe1ba0ab64251852bfb4c2d0220785b5236ab203f6332f9f1b68d3f98b55b027e81aa5ef1c7c603140787286c060121031abe2442aa1cefbd14cfccadf68c711946f081e7a146b910761c1f8e61819e9b00000002473044022013e0990cc749151a79635eee180e54fa05cf4282a1360dfaf80b561dabd4f8fe0220164516da7d61c5f813046d5ea3951753db500610c9d00043510650fe7e5e60f80121020a0ed1cbf6cc48ed846996f450760cad708e3c36fe2fa0e878e2f0b3107e910c00000000000000"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 2)
        assert_equal(len(decoded['vout']), 3)
        assert_equal(decoded['vsize'], 326)
        assert_equal(decoded['discountweight'], 1302)
        assert_equal(decoded['discountvsize'], 326)

        # 2 input 3 output
        hex = "[encoded data, 18314 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 2)
        assert_equal(len(decoded['vout']), 3)
        assert_equal(decoded['vsize'], 2575)
        assert_equal(decoded['discountweight'], 1302)
        assert_equal(decoded['discountvsize'], 326)

        # 2 input 3 output
        hex = "[encoded data, 18432 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 2)
        assert_equal(len(decoded['vout']), 3)
        assert_equal(decoded['vsize'], 2634)
        assert_equal(decoded['discountweight'], 1538)
        assert_equal(decoded['discountvsize'], 385)

        # 3 input 3 output
        hex = "[encoded data, 18886 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 3)
        assert_equal(len(decoded['vout']), 3)
        assert_equal(decoded['vsize'], 2731)
        assert_equal(decoded['discountweight'], 1860)
        assert_equal(decoded['discountvsize'], 465)

        # 4 input 3 output
        hex = "[encoded data, 19186 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 4)
        assert_equal(len(decoded['vout']), 3)
        assert_equal(decoded['vsize'], 2798)
        assert_equal(decoded['discountweight'], 2130)
        assert_equal(decoded['discountvsize'], 533)

        # 2 input 4 output
        hex = "[encoded data, 27210 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 2)
        assert_equal(len(decoded['vout']), 4)
        assert_equal(decoded['vsize'], 3816)
        assert_equal(decoded['discountweight'], 1764)
        assert_equal(decoded['discountvsize'], 441)

        # 2 input 5 output
        hex = "[encoded data, 36010 characters]"
        decoded = node2.decoderawtransaction(hex)
        assert_equal(len(decoded['vin']), 2)
        assert_equal(len(decoded['vout']), 5)
        assert_equal(decoded['vsize'], 5008)
        assert_equal(decoded['discountweight'], 2034)
        assert_equal(decoded['discountvsize'], 509)


if __name__ == '__main__':
    CTTest().main()
