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

import os
import json
import configparser

from test_framework.test_particl import (
    ParticlTestFramework,
    isclose,
    getIndexAtProperty,
)
from test_framework.test_framework import SkipTest
from test_framework.util import assert_raises_rpc_error
from test_framework.authproxy import JSONRPCException


class USBDeviceTest(ParticlTestFramework):
    def set_test_params(self):
        self.setup_clean_chain = True
        self.num_nodes = 3
        self.extra_args = [ ['-debug','-noacceptnonstdtxn','-reservebalance=10000000', '-txindex'] for i in range(self.num_nodes)]

    def skip_test_if_missing_module(self):
        self.skip_if_no_wallet()

    def setup_network(self, split=False):
        self.add_nodes(self.num_nodes, extra_args=self.extra_args)
        self.start_nodes()

        self.connect_nodes_bi(0, 1)
        self.connect_nodes_bi(0, 2)
        self.connect_nodes_bi(1, 2)
        self.sync_all()

    def run_test(self):

        # Check that particl has been built with USB device enabled
        config = configparser.ConfigParser()
        if not self.options.configfile:
            self.options.configfile = os.path.dirname(__file__) + "/../config.ini"
        config.read_file(open(self.options.configfile))

        if not config["components"].getboolean("ENABLE_USBDEVICE"):
            raise SkipTest("particld has not been built with usb device enabled.")

        nodes = self.nodes

        self.import_genesis_coins_a(nodes[0])

        ro = nodes[1].listdevices()
        assert(len(ro) == 1)
        assert(ro[0]['vendor'] == 'Debug')
        assert(ro[0]['product'] == 'Device')

        ro = nodes[1].getdeviceinfo()
        assert(ro['device'] == 'debug')

        ro = nodes[1].getdevicepublickey('0')
        assert(ro['address'] == 'praish9BVxVdhykpqBYEs6L65AQ7iKd9z1')
        assert(ro['path'] == "m/44'/1'/0'/0")

        ro = nodes[1].getdevicepublickey('0/1')
        assert(ro['address'] == 'peWvjy33QptC2Gz3ww7jTTLPjC2QJmifBR')
        assert(ro['path'] == "m/44'/1'/0'/0/1")

        ro = nodes[1].getdevicexpub("m/44'/1'/0'", "")
        assert(ro == 'pparszKXPyRegWYwPacdPduNPNEryRbZDCAiSyo8oZYSsbTjc6FLP4TCPEX58kAeCB6YW9cSdR6fsbpeWDBTgjbkYjXCoD9CNoFVefbkg3exzpQE')

        message = 'This is just a test message'
        sig = nodes[1].devicesignmessage('0/1', message)
        assert(True == nodes[1].verifymessage('peWvjy33QptC2Gz3ww7jTTLPjC2QJmifBR', sig, message))

        ro = nodes[1].initaccountfromdevice('test_acc')
        assert(ro['extkey'] == 'pparszKXPyRegWYwPacdPduNPNEryRbZDCAiSyo8oZYSsbTjc6FLP4TCPEX58kAeCB6YW9cSdR6fsbpeWDBTgjbkYjXCoD9CNoFVefbkg3exzpQE')
        assert(ro['path'] == "m/44'/1'/0'")

        ro = nodes[1].extkey('list', 'true')
        assert(len(ro) == 1)
        assert(ro[0]['path'] == "m/44h/1h/0h")
        assert(ro[0]['epkey'] == 'pparszKXPyRegWYwPacdPduNPNEryRbZDCAiSyo8oZYSsbTjc6FLP4TCPEX58kAeCB6YW9cSdR6fsbpeWDBTgjbkYjXCoD9CNoFVefbkg3exzpQE')
        assert(ro[0]['label'] == 'test_acc')
        assert(ro[0]['hardware_device'] == '0xffff 0x0001')

        ro = nodes[1].extkey('account')
        n = getIndexAtProperty(ro['chains'], 'use_type', 'stealth_spend')
        assert(n > -1)
        assert(ro['chains'][n]['path'] == "m/0h/444445h")


        addr1_0 = nodes[1].getnewaddress('lbl1_0')
        ro = nodes[1].filteraddresses()
        assert(len(ro) == 1)
        assert(ro[0]['path'] == 'm/0/0')
        assert(ro[0]['owned'] == 'true')
        assert(ro[0]['label'] == 'lbl1_0')

        va_addr1_0 = nodes[1].getaddressinfo(addr1_0)
        assert(va_addr1_0['ismine'] == True)
        assert(va_addr1_0['iswatchonly'] == False)
        assert(va_addr1_0['isondevice'] == True)
        assert(va_addr1_0['path'] == 'm/0/0')

        try:
            nodes[1].getnewstealthaddress()
            raise AssertionError('Should have failed.')
        except JSONRPCException as e:
            pass

        extaddr1_0 = nodes[1].getnewextaddress()

        txnid0 = nodes[0].sendtoaddress(addr1_0, 6)
        txnid1 = nodes[0].sendtoaddress(extaddr1_0, 6)

        self.stakeBlocks(1)
        block_txns = nodes[0].getblock(nodes[0].getblockhash(nodes[0].getblockcount()))['tx']
        assert(txnid0 in block_txns)
        assert(txnid1 in block_txns)

        ro = nodes[1].getwalletinfo()
        assert(isclose(ro['balance'], 12.0))

        addr0_0 = nodes[0].getnewaddress()
        hexRaw = nodes[1].createrawtransaction([], {addr0_0:10})
        hexFunded = nodes[1].fundrawtransaction(hexRaw)['hex']
        txDecoded = nodes[1].decoderawtransaction(hexFunded)

        ro = nodes[1].devicesignrawtransactionwithwallet(hexFunded)