Example #1
0
 def __init__(self,
              my_port=50082,
              core_node_host=None,
              core_node_port=None,
              pass_phrase=None):
     self.server_state = STATE_INIT
     print('Initializing server...')
     self.my_ip = self.__get_myip()
     print('Server IP address is set to ... ', self.my_ip)
     self.my_port = my_port
     self.cm = ConnectionManager(self.my_ip, self.my_port,
                                 self.__handle_message)
     self.mpmh = MyProtocolMessageHandler()
     self.core_node_host = core_node_host
     self.core_node_port = core_node_port
     self.bb = BlockBuilder()
     my_genesis_block = self.bb.generate_genesis_block()
     self.bm = BlockchainManager(my_genesis_block.to_dict())
     self.prev_block_hash = self.bm.get_hash(my_genesis_block.to_dict())
     self.tp = TransactionPool()
     self.is_bb_running = False
     self.flag_stop_block_build = False
     self.mpm_store = MessageStore()
     self.km = KeyManager(None, pass_phrase)
     self.rsa_util = RSAUtil()
     self.um = UTXOManager(self.km.my_address())
Example #2
0
def main():

    k_m = KeyManager()
    um = UTXOManager(k_m.my_address())

    i_k_m = KeyManager()
    u_k_m = KeyManager()

    t1 = CoinbaseTransaction(k_m.my_address())
    t2 = CoinbaseTransaction(k_m.my_address())
    t3 = CoinbaseTransaction(k_m.my_address())

    t4 = Transaction([TransactionInput(t1.to_dict(), 0)], [
        TransactionOutput(u_k_m.my_address(), 10.0),
        TransactionOutput(i_k_m.my_address(), 20.0)
    ])

    transactions = []
    transactions.append(t1.to_dict())
    transactions.append(t2.to_dict())
    transactions.append(t3.to_dict())
    transactions.append(t4.to_dict())

    um.extract_utxos(transactions)

    balance = um.my_balance

    print(balance)
Example #3
0
    def initApp(self, my_port, c_host, c_port):
        """
        ClientCoreとの接続含めて必要な初期化処理はここで実行する
        """
        print('SimpleBitcoin client is now activating ...: ')

        self.km = KeyManager() 
        self.um = UTXM(self.km.my_address())
        self.rsa_util = RSAUtil()

        self.c_core = Core(my_port, c_host, c_port, self.update_callback)
        self.c_core.start()

        # テスト用途(本来はこんな処理しない)
        t1 = CoinbaseTransaction(self.km.my_address())
        t2 = CoinbaseTransaction(self.km.my_address())
        t3 = CoinbaseTransaction(self.km.my_address())

        transactions = []
        transactions.append(t1.to_dict())
        transactions.append(t2.to_dict())
        transactions.append(t3.to_dict())
        self.um.extract_utxos(transactions)

        self.update_balance()
Example #4
0
 def load_my_keys():
     # ファイル選択ダイアログの表示
     f2 = Tk()
     f2.withdraw()
     fTyp = [('','*.pem')]
     iDir = os.path.abspath(os.path.dirname(__file__))
     messagebox.showinfo('Load key pair','please choose your key file')
     f_name = filedialog.askopenfilename(filetypes = fTyp,initialdir = iDir)
     
     try:
         file = open(f_name)
         data = file.read()
         target = binascii.unhexlify(data)
         # TODO: 本来は鍵ペアのファイルが不正などの異常系処理を考えるべき
         self.km.import_key_pair(target, p_phrase.get())
     except Exception as e:
         print(e)
     finally:
         # TODO: 所有コインの再確認処理を入れる必要あり
         file.close()
         f.destroy()
         f2.destroy()
         self.um = UTXM(self.km.my_address())
         self.um.my_balance = 0
         self.update_balance()
Example #5
0
        def save_my_pem():
            self.km = KeyManager()
            my_pem = self.km.export_key_pair(p_phrase)
            my_pem_hex = binascii.hexlify(my_pem).decode('ascii')
            # とりあえずファイル名は固定
            path = 'my_key_pair.pem'
            f1 = open(path,'a')
            f1.write(my_pem_hex)
            f1.close()

            f.destroy()
            self.um = UTXM(self.km.my_address())
            self.um.my_balance = 0
            self.update_balance()
Example #6
0
class ServerCore:
    def __init__(self,
                 my_port=50082,
                 core_node_host=None,
                 core_node_port=None,
                 pass_phrase=None):
        self.server_state = STATE_INIT
        print('Initializing server...')
        self.my_ip = self.__get_myip()
        print('Server IP address is set to ... ', self.my_ip)
        self.my_port = my_port
        self.cm = ConnectionManager(self.my_ip, self.my_port,
                                    self.__handle_message)
        self.mpmh = MyProtocolMessageHandler()
        self.core_node_host = core_node_host
        self.core_node_port = core_node_port
        self.bb = BlockBuilder()
        my_genesis_block = self.bb.generate_genesis_block()
        self.bm = BlockchainManager(my_genesis_block.to_dict())
        self.prev_block_hash = self.bm.get_hash(my_genesis_block.to_dict())
        self.tp = TransactionPool()
        self.is_bb_running = False
        self.flag_stop_block_build = False
        self.mpm_store = MessageStore()
        self.km = KeyManager(None, pass_phrase)
        self.rsa_util = RSAUtil()
        self.um = UTXOManager(self.km.my_address())

    def start_block_building(self):
        self.bb_timer = threading.Timer(CHECK_INTERVAL,
                                        self.__generate_block_with_tp)
        self.bb_timer.start()

    def stop_block_building(self):
        print('Thread for __generate_block_with_tp is stopped now')
        self.bb_timer.cancel()

    def start(self):
        self.server_state = STATE_STANDBY
        self.cm.start()
        self.start_block_building()

    def join_network(self):
        if self.core_node_host != None:
            self.server_state = STATE_CONNECTED_TO_CENTRAL
            self.cm.join_network(self.core_node_host, self.core_node_port)
        else:
            print('This server is runnning as Genesis Core Node...')

    def shutdown(self):
        self.server_state = STATE_SHUTTING_DOWN
        self.flag_stop_block_build = True
        print('Shutdown server...')
        self.cm.connection_close()
        self.stop_block_building()

    def get_my_current_state(self):
        return self.server_state

    def send_req_full_chain_to_my_peer(self):
        print('send_req_full_chain_to_my_central called')
        new_message = self.cm.get_message_text(MSG_REQUEST_FULL_CHAIN)
        self.cm.send_msg((self.core_node_host, self.core_node_port),
                         new_message)

    def get_all_chains_for_resolve_conflict(self):
        print('get_all_chains_for_resolve_conflict called')
        new_message = self.cm.get_message_text(MSG_REQUEST_FULL_CHAIN)
        self.cm.send_msg_to_all_peer(new_message)

    def __generate_block_with_tp(self):

        print('Thread for generate_block_with_tp started!')
        while not self.flag_stop_block_build:
            self.is_bb_running = True
            prev_hash = copy.copy(self.prev_block_hash)
            result = self.tp.get_stored_transactions()
            if len(result) == 0:
                print('Transaction Pool is empty ...')
                break
            new_tp = self.bm.remove_useless_transaction(result)
            self.tp.renew_my_transactions(new_tp)
            if len(new_tp) == 0:
                break
            # リワードとしてリストの先頭に自分宛のCoinbaseTransactionを追加する
            total_fee = self.tp.get_total_fee_from_tp()
            # TODO: インセンティブの値をここに直書きするのはイケてないのであとで対処する
            total_fee += 30
            my_coinbase_t = CoinbaseTransaction(self.km.my_address(),
                                                total_fee)
            transactions_4_block = copy.deepcopy(new_tp)
            transactions_4_block.insert(0, my_coinbase_t.to_dict())
            new_block = self.bb.generate_new_block(transactions_4_block,
                                                   prev_hash)
            # タイミングがどうしてもクロスするので念のため保存前に再度確認
            if new_block.to_dict()['previous_block'] == self.prev_block_hash:
                self.bm.set_new_block(new_block.to_dict())
                self.prev_block_hash = self.bm.get_hash(new_block.to_dict())
                msg_new_block = self.cm.get_message_text(
                    MSG_NEW_BLOCK, json.dumps(new_block.to_dict()))
                self.cm.send_msg_to_all_peer(msg_new_block)
                # ブロック生成に成功したらTransaction Poolはクリアする
                index = len(new_tp)
                self.tp.clear_my_transactions(index)
                break
            else:
                print('Bad block. It seems someone already win the PoW.')
                break

        print('Current Blockchain is ... ', self.bm.chain)
        print('Current prev_block_hash is ... ', self.prev_block_hash)
        self.flag_stop_block_build = False
        self.is_bb_running = False
        self.bb_timer = threading.Timer(CHECK_INTERVAL,
                                        self.__generate_block_with_tp)
        self.bb_timer.start()

    def _check_availability_of_transaction(self, transaction):
        """
        Transactionに含まれているTransactionInputの有効性(二重使用)を検証する
        """
        v_result, used_outputs = self.rsa_util.verify_sbc_transaction_sig(
            transaction)
        if v_result is not True:
            print('signature verification error on new transaction')
            return False

        for used_o in used_outputs:
            print('used_o', used_o)
            bm_v_result = self.bm.has_this_output_in_my_chain(used_o)
            tp_v_result = self.tp.has_this_output_in_my_tp(used_o)
            bm_v_result2 = self.bm.is_valid_output_in_my_chain(used_o)
            if bm_v_result:
                print('This TransactionOutput is already used', used_o)
                return False
            if tp_v_result:
                print(
                    'This TransactionOutput is already stored in the TransactionPool',
                    used_o)
                return False
            if bm_v_result2 is not True:
                print('This TransactionOutput is unknown', used_o)
                return False

        return True

    def _check_availability_of_transaction_in_block(self, transaction):
        """
        Transactionの有効性を検証する(Block用)
        """
        v_result, used_outputs = self.rsa_util.verify_sbc_transaction_sig(
            transaction)
        if v_result is not True:
            print('signature verification error on new transaction')
            return False

        print('used_outputs: ', used_outputs)

        for used_o in used_outputs:
            print('used_o: ', used_o)
            bm_v_result = self.bm.has_this_output_in_my_chain(used_o)
            bm_v_result2 = self.bm.is_valid_output_in_my_chain(used_o)
            if bm_v_result2 is not True:
                print('This TransactionOutput is unknown', used_o)
                return False
            if bm_v_result:
                print('This TransactionOutput is already used', used_o)
                return False

        return True

    def get_total_fee_on_block(self, block):
        """
        ブロックに格納されているbasicなTransaction全ての手数料の合計値を算出する
        """
        print('get_total_fee_on_block is called')
        transactions = block['transactions']
        result = 0
        for t in transactions:
            t = json.loads(t)
            is_sbc_t, t_type = self.um.is_sbc_transaction(t)
            if t_type == 'basic':
                total_in = sum(
                    i['transaction']['outputs'][i['output_index']]['value']
                    for i in t['inputs'])
                total_out = sum(o['value'] for o in t['outputs'])
                delta = total_in - total_out
                result += delta

        return result

    def check_transactions_in_new_block(self, block):
        """
        ブロック内のTranactionに不正がないか確認する
        """
        fee_for_block = self.get_total_fee_on_block(block)
        fee_for_block += 30
        print("fee_for_block: ", fee_for_block)

        transactions = block['transactions']

        counter = 0

        for t in transactions:
            t = json.loads(t)
            # basic, coinbase_transaction以外はスルーチェック
            is_sbc_t, t_type = self.um.is_sbc_transaction(t)
            if is_sbc_t:
                if t_type == 'basic':
                    if self._check_availability_of_transaction_in_block(
                            t) is not True:
                        print('Bad Block. Having invalid Transaction')
                        return False
                elif t_type == 'coinbase_transaction':
                    if counter != 0:
                        print('Coinbase Transaction is only for BlockBuilder')
                        return False
                    else:
                        insentive = t['outputs'][0]['value']
                        print('insentive', insentive)
                        if insentive != fee_for_block:
                            print(
                                'Invalid value in fee for CoinbaseTransaction',
                                insentive)
                            return False
            else:
                is_verified = self.rsa_util.verify_general_transaction_sig(t)
                if is_verified is not True:
                    return False

        print('ok. this block is acceptable.')
        return True

    def __core_api(self, request, message):

        if request == 'send_message_to_all_peer':
            new_message = self.cm.get_message_text(MSG_ENHANCED, message)
            self.cm.send_msg_to_all_peer(new_message)
            return 'ok'
        elif request == 'send_message_to_all_edge':
            new_message = self.cm.get_message_text(MSG_ENHANCED, message)
            self.cm.send_msg_to_all_edge(new_message)
            return 'ok'
        elif request == 'api_type':
            return 'server_core_api'
        elif request == 'send_message_to_this_pubkey_address':
            print('send_message_to_this_pubkey_address', message[0])
            msg_type = MSG_ENHANCED
            msg_txt = self.cm.get_message_text(msg_type, message[1])
            check_result, target_host, target_port = self.cm.has_this_edge(
                message[0])
            print('check_result', check_result)
            if check_result:
                print('sending cipher direct message to... ', target_host,
                      target_port)
                self.cm.send_msg((target_host, target_port), msg_txt)
                return 'ok'
            else:
                return None

    def __handle_message(self, msg, is_core, peer=None):
        if peer != None:
            if msg[2] == MSG_REQUEST_FULL_CHAIN:
                print('Send our latest blockchain for reply to : ', peer)
                mychain = self.bm.get_my_blockchain()
                chain_data = pickle.dumps(mychain, 0).decode()
                new_message = self.cm.get_message_text(RSP_FULL_CHAIN,
                                                       chain_data)
                self.cm.send_msg(peer, new_message)
        else:
            if msg[2] == MSG_NEW_TRANSACTION:
                new_transaction = json.loads(msg[4])
                print('received new_transaction', new_transaction)
                is_sbc_t, _ = self.um.is_sbc_transaction(new_transaction)
                current_transactions = self.tp.get_stored_transactions()
                if new_transaction in current_transactions:
                    print('this is already pooled transaction: ',
                          new_transaction)
                    return

                    if not is_sbc_t:
                        print('this is not SimpleBitcoin transaction: ',
                              new_transaction)
                    else:
                        # テスト用に最初のブロックだけ未知のCoinbaseTransactionを許すための暫定処置
                        if self.bm.get_my_chain_length() != 1:
                            checked = self._check_availability_of_transaction(
                                new_transaction)
                            if not checked:
                                print('Transaction Verification Error')
                                return
                    self.tp.set_new_transaction(new_transaction)

                    if not is_core:
                        new_message = self.cm.get_message_text(
                            MSG_NEW_TRANSACTION, json.dumps(new_transaction))
                        self.cm.send_msg_to_all_peer(new_message)
                else:
                    if not is_sbc_t:
                        print('this is not SimpleBitcoin transaction: ',
                              new_transaction)
                    else:
                        # テスト用に最初のブロックだけ未知のCoinbaseTransactionを許すための暫定処置
                        if self.bm.get_my_chain_length() != 1:
                            checked = self._check_availability_of_transaction(
                                new_transaction)
                            if not checked:
                                print('Transaction Verification Error')
                                return
                    self.tp.set_new_transaction(new_transaction)

                    if not is_core:
                        new_message = self.cm.get_message_text(
                            MSG_NEW_TRANSACTION, json.dumps(new_transaction))
                        self.cm.send_msg_to_all_peer(new_message)

            elif msg[2] == MSG_NEW_BLOCK:

                if not is_core:
                    print('block received from unknown')
                    return

                new_block = json.loads(msg[4])
                print('new_block: ', new_block)
                if self.bm.is_valid_block(self.prev_block_hash, new_block):
                    block_check_result = self.check_transactions_in_new_block(
                        new_block)
                    print('block_check_result : ', block_check_result)
                    if block_check_result is not True:
                        print(
                            'previous block hash is ok. but still not acceptable.'
                        )
                        self.get_all_chains_for_resolve_conflict()
                        return
                    # ブロック生成が行われていたら一旦停止してあげる(threadingなのでキレイに止まらない場合あり)
                    if self.is_bb_running:
                        self.flag_stop_block_build = True
                    self.prev_block_hash = self.bm.get_hash(new_block)
                    self.bm.set_new_block(new_block)
                else:
                    # ブロックとして不正ではないがVerifyにコケる場合は自分がorphanブロックを生成している
                    # 可能性がある
                    self.get_all_chains_for_resolve_conflict()

            elif msg[2] == RSP_FULL_CHAIN:

                if not is_core:
                    print('blockchain received from unknown')
                    return
                # ブロックチェーン送信要求に応じて返却されたブロックチェーンを検証し、有効なものか検証した上で
                # 自分の持つチェインと比較し優位な方を今後のブロックチェーンとして有効化する
                new_block_chain = pickle.loads(msg[4].encode('utf8'))
                result, pool_4_orphan_blocks = self.bm.resolve_conflicts(
                    new_block_chain)
                print('blockchain received')
                if result is not None:
                    self.prev_block_hash = result
                    if len(pool_4_orphan_blocks) != 0:
                        # orphanブロック群の中にあった未処理扱いになるTransactionをTransactionPoolに戻す
                        new_transactions = self.bm.get_transactions_from_orphan_blocks(
                            pool_4_orphan_blocks)
                        for t in new_transactions:
                            self.tp.set_new_transaction(t)
                else:
                    print('Received blockchain is useless...')

            elif msg[2] == MSG_ENHANCED:
                # アプリケーションがP2P Network を単なるトランスポートして使うために独自拡張したメッセージはここで処理する。
                # SimpleBitcoin としてはこの種別は使わない
                print('received enhanced message', msg[4])
                has_same = self.mpm_store.has_this_msg(msg[4])

                if has_same is not True:
                    self.mpm_store.add(msg[4])
                    self.mpmh.handle_message(msg[4], self.__core_api, is_core)

    def __get_myip(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        return s.getsockname()[0]
Example #7
0
class SimpleBC_Gui(Frame):

  
    def __init__(self, parent, my_port, c_host, c_port):
        Frame.__init__(self, parent)
        self.parent = parent
        self.parent.protocol('WM_DELETE_WINDOW', self.quit)
        self.coin_balance = StringVar(self.parent, '0')
        self.status_message = StringVar(self.parent, 'Ready')
        self.c_core = None
        self.initApp(my_port, c_host, c_port)
        self.setupGUI()

    def quit(self, event=None):
        """
        アプリの終了
        """
        self.c_core.shutdown()
        self.parent.destroy()


    def initApp(self, my_port, c_host, c_port):
        """
        ClientCoreとの接続含めて必要な初期化処理はここで実行する
        """
        print('SimpleBitcoin client is now activating ...: ')

        self.km = KeyManager() 
        self.um = UTXM(self.km.my_address())
        self.rsa_util = RSAUtil()

        self.c_core = Core(my_port, c_host, c_port, self.update_callback)
        self.c_core.start()

        # テスト用途(本来はこんな処理しない)
        t1 = CoinbaseTransaction(self.km.my_address())
        t2 = CoinbaseTransaction(self.km.my_address())
        t3 = CoinbaseTransaction(self.km.my_address())

        transactions = []
        transactions.append(t1.to_dict())
        transactions.append(t2.to_dict())
        transactions.append(t3.to_dict())
        self.um.extract_utxos(transactions)

        self.update_balance()
      

    def display_info(self, title, info):
        """
        ダイアログボックスを使ったメッセージの表示
        """
        f = Tk()
        label = Label(f, text=title)
        label.pack()
        info_area = Text(f, width=70, height=50)
        info_area.insert(INSERT, info)
        info_area.pack()
          

    def update_callback(self):
        print('update_callback was called!')
        s_transactions = self.c_core.get_stored_transactions_from_bc()
        print(s_transactions)
        self.um.extract_utxos(s_transactions)
        self.update_balance()


    def update_status(self, info):
        """
        画面下部のステータス表示内容を変更する
        """  
        self.status_message.set(info)


    def update_balance(self):
        """
        総額表示の内容を最新状態に合わせて変更する
        """
        bal = str(self.um.my_balance)
        self.coin_balance.set(bal)


    def create_menu(self):
        """
        メニューバーに表示するメニューを定義する
        """
        top = self.winfo_toplevel()
        self.menuBar = Menu(top)
        top['menu'] = self.menuBar

        self.subMenu = Menu(self.menuBar, tearoff=0)
        self.menuBar.add_cascade(label='Menu', menu=self.subMenu)
        self.subMenu.add_command(label='Show My Address', command=self.show_my_address)
        self.subMenu.add_command(label='Load my Keys', command=self.show_input_dialog_for_key_loading)
        self.subMenu.add_command(label='Update Blockchain', command=self.update_block_chain)
        self.subMenu.add_separator()
        self.subMenu.add_command(label='Quit', command=self.quit)

        self.subMenu2 = Menu(self.menuBar, tearoff=0)
        self.menuBar.add_cascade(label='Settings', menu=self.subMenu2)
        self.subMenu2.add_command(label='Renew my Keys', command=self.renew_my_keypairs)

        self.subMenu3 = Menu(self.menuBar, tearoff=0)
        self.menuBar.add_cascade(label='Advance', menu=self.subMenu3)
        self.subMenu3.add_command(label='Show Blockchain', command=self.show_my_block_chain)


    
    def show_my_address(self):
        f = Tk()
        label = Label(f, text='My Address')
        label.pack()
        key_info = Text(f, width=70, height=10)
        my_address = self.km.my_address()
        key_info.insert(INSERT, my_address)
        key_info.pack()


    def show_input_dialog_for_key_loading(self):
    
        def load_my_keys():
            # ファイル選択ダイアログの表示
            f2 = Tk()
            f2.withdraw()
            fTyp = [('','*.pem')]
            iDir = os.path.abspath(os.path.dirname(__file__))
            messagebox.showinfo('Load key pair','please choose your key file')
            f_name = filedialog.askopenfilename(filetypes = fTyp,initialdir = iDir)
            
            try:
                file = open(f_name)
                data = file.read()
                target = binascii.unhexlify(data)
                # TODO: 本来は鍵ペアのファイルが不正などの異常系処理を考えるべき
                self.km.import_key_pair(target, p_phrase.get())
            except Exception as e:
                print(e)
            finally:
                # TODO: 所有コインの再確認処理を入れる必要あり
                file.close()
                f.destroy()
                f2.destroy()
                self.um = UTXM(self.km.my_address())
                self.um.my_balance = 0
                self.update_balance()
    
        f = Tk()
        label0 = Label(f, text='Please enter pass phrase for your key pair')
        frame1 = ttk.Frame(f)
        label1 = ttk.Label(frame1, text='Pass Phrase:')

        p_phrase = StringVar()

        entry1 = ttk.Entry(frame1, textvariable=p_phrase) 
        button1 = ttk.Button(frame1, text='Load', command=load_my_keys)

        label0.grid(row=0,column=0,sticky=(N,E,S,W))
        frame1.grid(row=1,column=0,sticky=(N,E,S,W))
        label1.grid(row=2,column=0,sticky=E)
        entry1.grid(row=2,column=1,sticky=W)
        button1.grid(row=3,column=1,sticky=W)


    def update_block_chain(self):
        self.c_core.send_req_full_chain_to_my_core_node()

    def renew_my_keypairs(self):
        """
        利用する鍵ペアを更新する。
        """
        def save_my_pem():
            self.km = KeyManager()
            my_pem = self.km.export_key_pair(p_phrase)
            my_pem_hex = binascii.hexlify(my_pem).decode('ascii')
            # とりあえずファイル名は固定
            path = 'my_key_pair.pem'
            f1 = open(path,'a')
            f1.write(my_pem_hex)
            f1.close()

            f.destroy()
            self.um = UTXM(self.km.my_address())
            self.um.my_balance = 0
            self.update_balance()
            
        f = Tk()
        f.title('New Key Gene')
        label0 = Label(f, text='Please enter pass phrase for your new key pair')
        frame1 = ttk.Frame(f)
        label1 = ttk.Label(frame1, text='Pass Phrase:')

        p_phrase = StringVar()

        entry1 = ttk.Entry(frame1, textvariable=p_phrase) 
        button1 = ttk.Button(frame1, text='Generate', command=save_my_pem)

        label0.grid(row=0,column=0,sticky=(N,E,S,W))
        frame1.grid(row=1,column=0,sticky=(N,E,S,W))
        label1.grid(row=2,column=0,sticky=E)
        entry1.grid(row=2,column=1,sticky=W)
        button1.grid(row=3,column=1,sticky=W)


    def show_my_block_chain(self):
        """
        自分が保持しているブロックチェーンの中身を確認する
        """
        mychain = self.c_core.get_my_blockchain()
        if mychain is not None:
            mychain_str = pprint.pformat(mychain, indent=2)
            self.display_info('Current Blockchain', mychain_str)
        else:
            self.display_info('Warning', 'Currently Blockchain is empty...')


  
    def setupGUI(self):
        """
        画面に必要なパーツを並べる
        """

        self.parent.bind('<Control-q>', self.quit)
        self.parent.title('SimpleBitcoin GUI')
        self.pack(fill=BOTH, expand=1)

        self.create_menu()

        lf = LabelFrame(self, text='Current Balance')
        lf.pack(side=TOP, fill='both', expand='yes', padx=7, pady=7)

        lf2 = LabelFrame(self, text='')
        lf2.pack(side=BOTTOM, fill='both', expand='yes', padx=7, pady=7)
    
        #所持コインの総額表示領域のラベル
        self.balance = Label(lf, textvariable=self.coin_balance, font='Helvetica 20')
        self.balance.pack()

        #受信者となる相手の公開鍵
        self.label = Label(lf2, text='Recipient Address:')
        self.label.grid(row=0, pady=5)

        self.recipient_pubkey = Entry(lf2, bd=2)
        self.recipient_pubkey.grid(row=0, column=1, pady=5)

        # 送金額
        self.label2 = Label(lf2, text='Amount to pay :')
        self.label2.grid(row=1, pady=5)
    
        self.amountBox = Entry(lf2, bd=2)
        self.amountBox.grid(row=1, column=1, pady=5, sticky='NSEW')

        # 手数料
        self.label3 = Label(lf2, text='Fee (Optional) :')
        self.label3.grid(row=2, pady=5)
    
        self.feeBox = Entry(lf2, bd=2)
        self.feeBox.grid(row=2, column=1, pady=5, sticky='NSEW')

        # 間隔の開け方がよくわからんので空文字で場所確保
        self.label4 = Label(lf2, text='')
        self.label4.grid(row=5, pady=5)

        # 送金実行ボタン
        self.sendBtn = Button(lf2, text='\nSend Coin(s)\n', command=self.sendCoins)
        self.sendBtn.grid(row=6, column=1, sticky='NSEW')

        # 下部に表示するステータスバー
        stbar = Label(self.winfo_toplevel(), textvariable=self.status_message, bd=1, relief=SUNKEN, anchor=W)
        stbar.pack(side=BOTTOM, fill=X)

  
    # 送金実行ボタン押下時の処理実体
    def sendCoins(self):
        sendAtp = self.amountBox.get()
        recipientKey = self.recipient_pubkey.get()
        sendFee = self.feeBox.get()

        utxo_len = len(self.um.utxo_txs)

        if not sendAtp:
            messagebox.showwarning('Warning', 'Please enter the Amount to pay.')
            return
        elif len(recipientKey) <= 1:
            messagebox.showwarning('Warning', 'Please enter the Recipient Address.')
            return
        else:
            result = messagebox.askyesno('Confirmation', 'Sending {} SimpleBitcoins to :\n {}'.format(sendAtp, recipientKey))

        if not sendFee:
            sendFee = 0


        if result:
            if 0 < utxo_len:
                print('Sending {} SimpleBitcoins to reciever:\n {}'.format(sendAtp, recipientKey))
            else:
                messagebox.showwarning('Short of Coin.', 'Not enough coin to be sent...')
                return

            utxo, idx = self.um.get_utxo_tx(0)

            t = Transaction(
                [TransactionInput(utxo, idx)],
                [TransactionOutput(recipientKey, int(sendAtp))]
            )

            counter = 1
            # TransactionInputが送信額を超えるまで繰り返して取得しTransactionとして完成させる
            if type(sendFee) is not str:
                sendFee = int(sendFee)
            while t.is_enough_inputs(sendFee) is not True:
                new_utxo, new_idx = self.um.get_utxo_tx(counter)
                t.inputs.append(TransactionInput(new_utxo, new_idx))
                counter += 1
                if counter > utxo_len:
                    messagebox.showwarning('Short of Coin.', 'Not enough coin to be sent...')
                    break

            # 正常なTransactionが生成できた時だけ秘密鍵で署名を実行する
            if t.is_enough_inputs(sendFee) is True:
                # まずお釣り用Transactionを作る
                change = t.compute_change(sendFee)
                t.outputs.append(TransactionOutput(self.km.my_address(), change))
                to_be_signed = json.dumps(t.to_dict(), sort_keys=True)
                signed = self.km.compute_digital_signature(to_be_signed)
                new_tx = json.loads(to_be_signed)
                new_tx['signature'] = signed
                # TransactionをP2P Networkに送信
                tx_strings = json.dumps(new_tx)
                self.c_core.send_message_to_my_core_node(MSG_NEW_TRANSACTION, tx_strings)
                print('signed new_tx:', tx_strings)
                # 実験的にお釣り分の勘定のため新しく生成したTransactionをUTXOとして追加しておくが
                # 本来はブロックチェーンの更新に合わせて再計算した方が適切
                self.um.put_utxo_tx(t.to_dict())
                to_be_deleted = 0
                del_list = []
                while to_be_deleted < counter:
                    del_tx = self.um.get_utxo_tx(to_be_deleted)
                    del_list.append(del_tx)
                    to_be_deleted += 1

                for dx in del_list:
                    self.um.remove_utxo_tx(dx)

        self.amountBox.delete(0,END)
        self.feeBox.delete(0,END)
        self.recipient_pubkey.delete(0,END)
        self.update_balance()
class SimpleBC_Gui(Frame):

  
    def __init__(self, parent, my_port, c_host, c_port):
        Frame.__init__(self, parent)
        self.parent = parent
        self.parent.protocol('WM_DELETE_WINDOW', self.quit)
        self.coin_balance = StringVar(self.parent, '0')
        self.status_message = StringVar(self.parent, 'Ready')
        self.c_core = None
        self.initApp(my_port, c_host, c_port)
        self.setupGUI()

    def quit(self, event=None):
        """
        アプリの終了
        """
        self.c_core.shutdown()
        self.parent.destroy()


    def initApp(self, my_port, c_host, c_port):
        """
        ClientCoreとの接続含めて必要な初期化処理はここで実行する
        """
        print('SimpleBitcoin client is now activating ...: ')

        self.km = KeyManager() 
        self.um = UTXM(self.km.my_address())
        self.rsa_util = RSAUtil()

        self.c_core = Core(my_port, c_host, c_port, self.update_callback, self.get_message_callback)
        self.c_core.start(self.km.my_address())

        # テスト用途(本来はこんな処理しない)
        t1 = CoinbaseTransaction(self.km.my_address())
        t2 = CoinbaseTransaction(self.km.my_address())
        t3 = CoinbaseTransaction(self.km.my_address())

        transactions = []
        transactions.append(t1.to_dict())
        transactions.append(t2.to_dict())
        transactions.append(t3.to_dict())
        self.um.extract_utxos(transactions)

        self.update_balance()
      

    def display_info(self, title, info):
        """
        ダイアログボックスを使ったメッセージの表示
        """
        f = Tk()
        label = Label(f, text=title)
        label.pack()
        info_area = Text(f, width=70, height=50)
        info_area.insert(INSERT, info)
        info_area.pack()


    def get_message_callback(self, target_message):
        print('get_message_callback called!')
        if target_message['message_type'] == 'cipher_message':
            try:
                encrypted_key = base64.b64decode(binascii.unhexlify(target_message['enc_key']))
                print('encripted_key : ', encrypted_key)
                decrypted_key = self.km.decrypt_with_private_key(encrypted_key)
                print('decrypted_key : ', binascii.hexlify(decrypted_key).decode('ascii'))
                # 流石に名前解決をしないで公開鍵のまま出しても意味なさそうなのでコメントアウト
                #sender = binascii.unhexlify(target_message['sender'])
                aes_util = AESUtil()
                decrypted_message = aes_util.decrypt_with_key(base64.b64decode(binascii.unhexlify(target_message['body'])), decrypted_key)
                print(decrypted_message.decode('utf-8'))
                """
                message = {
                    'from' : sender,
                    'message' : decrypted_message.decode('utf-8')
                }
                message_4_display = pprint.pformat(message, indent=2)
                """
                messagebox.showwarning('You received an instant encrypted message !', decrypted_message.decode('utf-8'))
            except Exception as e:
                print(e, 'error occurred')
        elif target_message['message_type'] == 'engraved':
            sender_name = target_message['sender_alt_name']
            msg_body = base64.b64decode(binascii.unhexlify(target_message['message'])).decode('utf-8')
            timestamp = datetime.datetime.fromtimestamp(int(target_message['timestamp']))
            messagebox.showwarning('You received a new engraved message!', '{} :\n {} \n {}'.format(sender_name, msg_body, timestamp))
            

    def update_callback(self):
        print('update_callback was called!')
        s_transactions = self.c_core.get_stored_transactions_from_bc()
        print(s_transactions)
        self.um.extract_utxos(s_transactions)
        self.update_balance()


    def update_status(self, info):
        """
        画面下部のステータス表示内容を変更する
        """  
        self.status_message.set(info)


    def update_balance(self):
        """
        総額表示の内容を最新状態に合わせて変更する
        """
        bal = str(self.um.my_balance)
        self.coin_balance.set(bal)


    def create_menu(self):
        """
        メニューバーに表示するメニューを定義する
        """
        top = self.winfo_toplevel()
        self.menuBar = Menu(top)
        top['menu'] = self.menuBar

        self.subMenu = Menu(self.menuBar, tearoff=0)
        self.menuBar.add_cascade(label='Menu', menu=self.subMenu)
        self.subMenu.add_command(label='Show My Address', command=self.show_my_address)
        self.subMenu.add_command(label='Load my Keys', command=self.show_input_dialog_for_key_loading)
        self.subMenu.add_command(label='Update Blockchain', command=self.update_block_chain)
        self.subMenu.add_separator()
        self.subMenu.add_command(label='Quit', command=self.quit)

        self.subMenu2 = Menu(self.menuBar, tearoff=0)
        self.menuBar.add_cascade(label='Settings', menu=self.subMenu2)
        self.subMenu2.add_command(label='Renew my Keys', command=self.renew_my_keypairs)

        self.subMenu3 = Menu(self.menuBar, tearoff=0)
        self.menuBar.add_cascade(label='Advance', menu=self.subMenu3)
        self.subMenu3.add_command(label='Send Encrypted Instant Message', command=self.send_instant_message)
        self.subMenu3.add_command(label='Show Logs (Received)', command=self.open_r_log_window)
        self.subMenu3.add_command(label='Show Logs (Send)', command=self.open_s_log_window)
        self.subMenu3.add_command(label='Show Blockchain', command=self.show_my_block_chain)
        self.subMenu3.add_command(label='Engrave Message', command=self.engrave_message)

    
    def show_my_address(self):
        f = Tk()
        label = Label(f, text='My Address')
        label.pack()
        key_info = Text(f, width=70, height=10)
        my_address = self.km.my_address()
        key_info.insert(INSERT, my_address)
        key_info.pack()


    def show_input_dialog_for_key_loading(self):
    
        def load_my_keys():
            # ファイル選択ダイアログの表示
            f2 = Tk()
            f2.withdraw()
            fTyp = [('','*.pem')]
            iDir = os.path.abspath(os.path.dirname(__file__))
            messagebox.showinfo('Load key pair','please choose your key file')
            f_name = filedialog.askopenfilename(filetypes = fTyp,initialdir = iDir)
            
            try:
                file = open(f_name)
                data = file.read()
                target = binascii.unhexlify(data)
                # TODO: 本来は鍵ペアのファイルが不正などの異常系処理を考えるべき
                self.km.import_key_pair(target, p_phrase.get())
            except Exception as e:
                print(e)
            finally:
                # TODO: 所有コインの再確認処理を入れる必要あり
                file.close()
                f.destroy()
                f2.destroy()
                self.um = UTXM(self.km.my_address())
                self.um.my_balance = 0
                self.update_balance()
    
        f = Tk()
        label0 = Label(f, text='Please enter pass phrase for your key pair')
        frame1 = ttk.Frame(f)
        label1 = ttk.Label(frame1, text='Pass Phrase:')

        p_phrase = StringVar()

        entry1 = ttk.Entry(frame1, textvariable=p_phrase) 
        button1 = ttk.Button(frame1, text='Load', command=load_my_keys)

        label0.grid(row=0,column=0,sticky=(N,E,S,W))
        frame1.grid(row=1,column=0,sticky=(N,E,S,W))
        label1.grid(row=2,column=0,sticky=E)
        entry1.grid(row=2,column=1,sticky=W)
        button1.grid(row=3,column=1,sticky=W)


    def update_block_chain(self):
        self.c_core.send_req_full_chain_to_my_core_node()

    def renew_my_keypairs(self):
        """
        利用する鍵ペアを更新する。
        """
        def save_my_pem():
            self.km = KeyManager()
            my_pem = self.km.export_key_pair(p_phrase)
            my_pem_hex = binascii.hexlify(my_pem).decode('ascii')
            # とりあえずファイル名は固定
            path = 'my_key_pair.pem'
            f1 = open(path,'a')
            f1.write(my_pem_hex)
            f1.close()

            f.destroy()
            self.um = UTXM(self.km.my_address())
            self.um.my_balance = 0
            self.update_balance()
            
        f = Tk()
        f.title('New Key Gene')
        label0 = Label(f, text='Please enter pass phrase for your new key pair')
        frame1 = ttk.Frame(f)
        label1 = ttk.Label(frame1, text='Pass Phrase:')

        p_phrase = StringVar()

        entry1 = ttk.Entry(frame1, textvariable=p_phrase) 
        button1 = ttk.Button(frame1, text='Generate', command=save_my_pem)

        label0.grid(row=0,column=0,sticky=(N,E,S,W))
        frame1.grid(row=1,column=0,sticky=(N,E,S,W))
        label1.grid(row=2,column=0,sticky=E)
        entry1.grid(row=2,column=1,sticky=W)
        button1.grid(row=3,column=1,sticky=W)


    def send_instant_message(self):
        def send_message():
            r_pkey = entry1.get()
            print('pubkey', r_pkey)
            new_message = {}
            aes_util = AESUtil()
            cipher_txt = aes_util.encrypt(entry2.get())
            new_message['message_type'] = 'cipher_message'
            new_message['recipient'] = r_pkey
            new_message['sender'] = self.km.my_address()
            new_message['body'] = binascii.hexlify(base64.b64encode(cipher_txt)).decode('ascii')
            key = aes_util.get_aes_key()
            encrypted_key = self.rsa_util.encrypt_with_pubkey(key, r_pkey)
            print('encrypted_key: ', encrypted_key[0])
            new_message['enc_key'] = binascii.hexlify(base64.b64encode(encrypted_key[0])).decode('ascii')
            msg_type = MSG_ENHANCED
            message_strings = json.dumps(new_message)
            self.c_core.send_message_to_my_core_node(msg_type, message_strings)
            f.destroy()
            
        f = Tk()
        f.title('New Message')
        label0 = Label(f, text='Please input recipient address and message')
        frame1 = ttk.Frame(f)
        label1 = ttk.Label(frame1, text='Recipient:')
        pkey = StringVar()
        entry1 = ttk.Entry(frame1, textvariable=pkey)
        label2 = ttk.Label(frame1, text='Message:')
        message = StringVar()
        entry2 = ttk.Entry(frame1, textvariable=message) 
        button1 = ttk.Button(frame1, text='Send Message', command=send_message)

        label0.grid(row=0,column=0,sticky=(N,E,S,W))
        frame1.grid(row=1,column=0,sticky=(N,E,S,W))
        label1.grid(row=2,column=0,sticky=E)
        entry1.grid(row=2,column=1,sticky=W)
        label2.grid(row=3,column=0,sticky=E)
        entry2.grid(row=3,column=1,sticky=W)
        button1.grid(row=4,column=1,sticky=W)  


    def open_r_log_window(self):
        """
        別ウィンドウでログ情報を表示する。これまでに受け取った自分宛のTransactionを時系列で並べる
        """
        s_transactions = self.c_core.get_stored_transactions_from_bc()
        my_transactions = self.um.get_txs_to_my_address(s_transactions)
        
        informations = []
        
        receive_date = None
        sender = None
        value = None
        reason = None
        description = None
        
        for t in my_transactions:
        
            result, t_type = self.um.is_sbc_transaction(t)
            receive_date = datetime.datetime.fromtimestamp(int(t['timestamp']))
            
            if t_type == 'basic':
                reason = base64.b64decode(binascii.unhexlify(t['extra']['reason'])).decode('utf-8')
                description = base64.b64decode(binascii.unhexlify(t['extra']['description'])).decode('utf-8')
                for txout in t['outputs']:
                    recipient = txout['recipient']
                    if recipient == self.km.my_address():
                        value = txout['value']
                for txin in t['inputs']:
                    t_in_txin = txin['transaction']
                    idx = txin['output_index']
                    sender = t_in_txin['outputs'][idx]['recipient']
                    if sender == self.km.my_address():
                        sender = 'Change to myself'
                
            else:
                reason = 'CoinbaseTransaction'
                description = 'CoinbaseTransaction'
                sender = self.km.my_address()
                for txout in t['outputs']:
                    recipient = txout['recipient']
                    if recipient == self.km.my_address():
                        value = txout['value']
                        
            info = {
                'date' : receive_date,
                'From' : sender,
                'Value' : value,
                'reason' : reason,
                'description' : description
            }
            informations.append(info)

        log = pprint.pformat(informations, indent=2)
        if log is not None:
            self.display_info('Log : Received Transaction', log)
        else:
            self.display_info('Warning', 'Currently you received NO Transaction to you...')


    def open_s_log_window(self):
        """
        別ウィンドウでログ情報を表示する。これまでに自分が送信したTransactionを時系列で並べる
        """
        s_transactions = self.c_core.get_stored_transactions_from_bc()
        my_transactions = self.um.get_txs_from_my_address(s_transactions)
        
        informations = []
        
        send_date = None
        recipient = None
        value = None
        reason = None
        description = None
        
        for t in my_transactions:
        
            result, t_type = self.um.is_sbc_transaction(t)
            send_date = datetime.datetime.fromtimestamp(int(t['timestamp']))

            if t_type == 'basic':
                reason = base64.b64decode(binascii.unhexlify(t['extra']['reason'])).decode('utf-8')
                description = base64.b64decode(binascii.unhexlify(t['extra']['description'])).decode('utf-8')
                for txout in t['outputs']:
                    recipient = txout['recipient']
                    if recipient == self.km.my_address():
                        recipient = 'Change to myself'
                    value = txout['value']
                        
                    info = {
                        'date' : send_date,
                        'To' : recipient,
                        'Value' : value,
                        'reason' : reason,
                        'description' : description
                    }
                    informations.append(info)

        log = pprint.pformat(informations, indent=2)
        if log is not None:
            self.display_info('Log : Sent Transaction', log)
        else:
            self.display_info('Warning', 'NO Transaction which was sent from you...')

    def show_my_block_chain(self):
        """
        自分が保持しているブロックチェーンの中身を確認する
        """
        mychain = self.c_core.get_my_blockchain()
        if mychain is not None:
            mychain_str = pprint.pformat(mychain, indent=2)
            self.display_info('Current Blockchain', mychain_str)
        else:
            self.display_info('Warning', 'Currently Blockchain is empty...')

    def engrave_message(self):
        """
        ブロックチェーンにTwitter風のメッセージを格納する
        """
        def send_e_message():
            # P2PによるブロードキャストとTransactionのハイブリッド
            new_message = {}
            msg_txt = entry.get().encode('utf-8')
            
            msg = EngravedTransaction(
                self.km.my_address(),
                'Testman',
                binascii.hexlify(base64.b64encode(msg_txt)).decode('ascii')
            )

            to_be_signed = json.dumps(msg.to_dict(), sort_keys=True)
            signed = self.km.compute_digital_signature(to_be_signed)
            new_tx = json.loads(to_be_signed)
            new_tx['signature'] = signed
            tx_strings = json.dumps(new_tx)
            self.c_core.send_message_to_my_core_node(MSG_NEW_TRANSACTION, tx_strings)
            new_tx2 = copy.deepcopy(new_tx)
            new_tx2['message_type'] = 'engraved'
            tx_strings2 = json.dumps(new_tx2)
            self.c_core.send_message_to_my_core_node(MSG_ENHANCED, tx_strings2)
            f.destroy()
            
        f = Tk()
        f.title('Engrave New Message')
        label0 = Label(f, text='Any idea?')
        frame1 = ttk.Frame(f)
        label = ttk.Label(frame1, text='Message:')
        entry = ttk.Entry(frame1, width=30) 
        button1 = ttk.Button(frame1, text='Engrave this on Blockchain', command=send_e_message)

        label0.grid(row=0,column=0,sticky=(N,E,S,W))
        frame1.grid(row=1,column=0,sticky=(N,E,S,W))
        label.grid(row=2,column=0,sticky=E)
        entry.grid(row=2,column=1,sticky=W)
        button1.grid(row=3,column=1,sticky=W)  

  
    def setupGUI(self):
        """
        画面に必要なパーツを並べる
        """

        self.parent.bind('<Control-q>', self.quit)
        self.parent.title('SimpleBitcoin GUI')
        self.pack(fill=BOTH, expand=1)

        self.create_menu()

        lf = LabelFrame(self, text='Current Balance')
        lf.pack(side=TOP, fill='both', expand='yes', padx=7, pady=7)

        lf2 = LabelFrame(self, text='')
        lf2.pack(side=BOTTOM, fill='both', expand='yes', padx=7, pady=7)
    
        #所持コインの総額表示領域のラベル
        self.balance = Label(lf, textvariable=self.coin_balance, font='Helvetica 20')
        self.balance.pack()

        #受信者となる相手の公開鍵
        self.label = Label(lf2, text='Recipient Address:')
        self.label.grid(row=0, pady=5)

        self.recipient_pubkey = Entry(lf2, bd=2)
        self.recipient_pubkey.grid(row=0, column=1, pady=5)

        # 送金額
        self.label2 = Label(lf2, text='Amount to pay :')
        self.label2.grid(row=1, pady=5)
    
        self.amountBox = Entry(lf2, bd=2)
        self.amountBox.grid(row=1, column=1, pady=5, sticky='NSEW')

        # 手数料
        self.label3 = Label(lf2, text='Fee (Optional) :')
        self.label3.grid(row=2, pady=5)
    
        self.feeBox = Entry(lf2, bd=2)
        self.feeBox.grid(row=2, column=1, pady=5, sticky='NSEW')

        # 送金理由を書く欄(主にURLとか)
        self.label4 = Label(lf2, text='reason (Optional) :')
        self.label4.grid(row=3, pady=5)
    
        self.reasonBox = Entry(lf2, bd=2)
        self.reasonBox.grid(row=3, column=1, pady=5, sticky='NSEW')
        
        # 通信欄(公開メッセージ)
        self.label5 = Label(lf2, text='message (Optional) :')
        self.label5.grid(row=4, pady=5)
    
        self.messageBox = Entry(lf2, bd=2)
        self.messageBox.grid(row=4, column=1, pady=5, sticky='NSEW')

        # 間隔の開け方がよくわからんので空文字で場所確保
        self.label4 = Label(lf2, text='')
        self.label4.grid(row=5, pady=5)

        # 送金実行ボタン
        self.sendBtn = Button(lf2, text='\nSend Coin(s)\n', command=self.sendCoins)
        self.sendBtn.grid(row=6, column=1, sticky='NSEW')

        # 下部に表示するステータスバー
        stbar = Label(self.winfo_toplevel(), textvariable=self.status_message, bd=1, relief=SUNKEN, anchor=W)
        stbar.pack(side=BOTTOM, fill=X)

  
    # 送金実行ボタン押下時の処理実体
    def sendCoins(self):
        sendAtp = self.amountBox.get()
        recipientKey = self.recipient_pubkey.get()
        sendFee = self.feeBox.get()
        reason = binascii.hexlify(base64.b64encode(self.reasonBox.get().encode('utf-8'))).decode('ascii')
        desc = binascii.hexlify(base64.b64encode(self.messageBox.get().encode('utf-8'))).decode('ascii')

        utxo_len = len(self.um.utxo_txs)

        if not sendAtp:
            messagebox.showwarning('Warning', 'Please enter the Amount to pay.')
            return
        elif len(recipientKey) <= 1:
            messagebox.showwarning('Warning', 'Please enter the Recipient Address.')
            return
        else:
            result = messagebox.askyesno('Confirmation', 'Sending {} SimpleBitcoins to :\n {}'.format(sendAtp, recipientKey))

        if not sendFee:
            sendFee = 0
            
        if not reason:
            reason = 'No information'
            
        if not desc:
            desc = 'No description'
            
        extra = {
            'reason': reason,
            'description': desc,
        }

        if result:
            if 0 < utxo_len:
                print('Sending {} SimpleBitcoins to reciever:\n {}'.format(sendAtp, recipientKey))
            else:
                messagebox.showwarning('Short of Coin.', 'Not enough coin to be sent...')
                return

            utxo, idx = self.um.get_utxo_tx(0)

            t = Transaction(
                [TransactionInput(utxo, idx)],
                [TransactionOutput(recipientKey, int(sendAtp))],
                extra
            )

            counter = 1
            # TransactionInputが送信額を超えるまで繰り返して取得しTransactionとして完成させる
            if type(sendFee) is not str:
                sendFee = int(sendFee)
            while t.is_enough_inputs(sendFee) is not True:
                new_utxo, new_idx = self.um.get_utxo_tx(counter)
                t.inputs.append(TransactionInput(new_utxo, new_idx))
                counter += 1
                if counter > utxo_len:
                    messagebox.showwarning('Short of Coin.', 'Not enough coin to be sent...')
                    break

            # 正常なTransactionが生成できた時だけ秘密鍵で署名を実行する
            if t.is_enough_inputs(sendFee) is True:
                # まずお釣り用Transactionを作る
                change = t.compute_change(sendFee)
                t.outputs.append(TransactionOutput(self.km.my_address(), change))
                to_be_signed = json.dumps(t.to_dict(), sort_keys=True)
                signed = self.km.compute_digital_signature(to_be_signed)
                new_tx = json.loads(to_be_signed)
                new_tx['signature'] = signed
                # TransactionをP2P Networkに送信
                msg_type = MSG_NEW_TRANSACTION
                tx_strings = json.dumps(new_tx)
                self.c_core.send_message_to_my_core_node(msg_type, tx_strings)
                print('signed new_tx:', tx_strings)
                # 実験的にお釣り分の勘定のため新しく生成したTransactionをUTXOとして追加しておくが
                # 本来はブロックチェーンの更新に合わせて再計算した方が適切
                self.um.put_utxo_tx(t.to_dict())
                to_be_deleted = 0
                del_list = []
                while to_be_deleted < counter:
                    del_tx = self.um.get_utxo_tx(to_be_deleted)
                    del_list.append(del_tx)
                    to_be_deleted += 1

                for dx in del_list:
                    self.um.remove_utxo_tx(dx)

        self.amountBox.delete(0,END)
        self.feeBox.delete(0,END)
        self.recipient_pubkey.delete(0,END)
        self.reasonBox.delete(0,END)
        self.messageBox.delete(0,END)
        self.update_balance()