Smart=BTC

Pretty safe bitcoin wallet with keys on a smartcard

The Program smartbtc.py

#!/usr/bin/env python3
# SmartBTC
# ** bitcointransactions with pkcs11-smartcard **
# version: 4 sept 2016
# author:  J.v.d.Bosch
#
# SmartBTC is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# any later version.  See: http://www.gnu.org/licenses/gpl-3.0.html
#
# SmartBTC is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.
#
# Versionhistory:
# first version 4 sept 2016
# 12 jan 2017: added pyc.setopt(pycurl.USERAGENT, "Mozilla/5.0") ... tot prevent webserver-access-error
#
#
# uses python-bitcoinlib
# Bitcoinvalues are in mBTC

import os, sys, re
import json, tempfile, struct
import datetime, pycurl, io
from io import BytesIO
from tkinter import Tk, Toplevel, Label, Entry, Button, StringVar, N, W, S, E
from tkinter import ttk, messagebox, PhotoImage, END
from os.path import expanduser
import subprocess, hashlib, getpass
from hashlib import sha256
import base64, binascii

import bitcoin
from bitcoin import SelectParams
from bitcoin.core import b2x, x, lx, COIN, COutPoint, CMutableTxOut
from bitcoin.core import CMutableTxIn, CMutableTransaction
from bitcoin.core import Hash160, ValidationError
from bitcoin.core.script import SignatureHash, SIGHASH_ALL
from bitcoin.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY
from bitcoin.core.script import OP_CHECKSIG
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.core.scripteval import VerifySignature, VerifySignatureError
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
from bitcoin.core.key import CECKey, CPubKey

#### SET THIS TO THE FULL PATH OF THE OPENSC-PKCS11 LIBRARY
p11module = "/usr/local/lib/opensc-pkcs11.so"

# set to 1 for debug information
debug = 1

# a global variable for the items from the configurationfile
confDict = {}
homedir = os.getenv('HOME')

# secp256k1 parameter
orderN = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141


def Err(msg):
    #
    # display Error, and exit
    #
    messagebox.showwarning("Error:", msg)
    exit(-1)


def Info(msg):
    #
    # display Info
    #
    messagebox.showwarning("Info:", msg)


def YesNo(msg):
    # window to ask yes or no
    yesno = messagebox.askquestion("Please Press Yes or No", msg)
    return yesno


class ConfDialog:
    # window asks to enter the configurationfile-id
    # file ~/.smartbtc/<cofiguration-id>.conf
    def __init__(self, parent):
        top = self.top = Toplevel(parent)
        top.title("Choose Configfile")
        top.lift()
        self.myLabel = Label(top,
                             text='***Enter configfile-id (without .conf):***')
        self.myLabel.pack()
        self.myEntryBox = Entry(top)
        self.myEntryBox.bind("<Return>", self.sendid)
        self.myEntryBox.pack()
        self.mySubmitButton = Button(top, text='Submit')
        self.mySubmitButton.bind("<Button-1>", self.sendid)
        self.mySubmitButton.pack()
        self.myEntryBox.focus_set()
        self.confid = ""

    def sendid(self, event):
        self.confid = self.myEntryBox.get()
        self.top.destroy()


def readConfig(id):
    #
    # read the configuration files
    # set appropriate defaults if possible
    #

    global confDict  # Dictionairy
    global homedir

    # global.conf
    conffile = homedir + "/" + ".smartbtc/" + "global.conf"

    try:
        conffile = open(conffile, "r")
        # read configuration in confDict key-value pairs
        for line in conffile.readlines():
            if not line.startswith('#') and ('=' in line):
                # split on first '=', strip off white spaces
                key, value = line.split('=', 1)
                key = key.strip()
                value = value.strip()
                if (key):
                    confDict[key] = value

    except IOError:
        Err("Cannot open configfile")

    if 'Transactionfee' in confDict:
        fee = float(confDict['Transactionfee'])
    else:
        confDict['Transactionfee'] = 0.0

    if not ('Provider-Url-Testnet' in confDict):
        if not ('Provider-Url-Mainnet' in confDict):
            Err("No Provider-Url= in the config file")

    if not ('SendTx-Url-Testnet' in confDict):
        if not ('SendTx-Url-Mainnet' in confDict):
            Err("No SendTx-Url= in the config file")
    
    if 'MaxUnspentLines' in confDict:
        maxunspentlines = int(confDict['MaxUnspentLines'])
    else:
        confDict['MaxUnspentLines'] = 5

    if 'BTCtransactionsDir' in confDict:
        txdir = confDict['BTCtransactionsDir']
        if txdir.find(homedir) == -1:
            txdir = homedir + "/" + confDict['BTCtransactionsDir']
        try:
           os.stat(txdir)
        except:
           os.mkdir(txdir)
    else:
        Err('No BTCtransactionsDir= in the config file')


    # config files per key
    conffile = homedir + "/" + ".smartbtc/" + id + ".conf"

    try:
        conffile = open(conffile, "r")
        # read configuration in confDict key-value pairs
        for line in conffile.readlines():
            if not line.startswith('#') and ('=' in line):
                # split on first '=', strip off white spaces
                key, value = line.split('=', 1)
                key = key.strip()
                value = value.strip()
                if (key):
                    confDict[key] = value

    except IOError:
        Err("Cannot open configfile")

    if 'Network' in confDict:
        network = confDict['Network']
        if ((network != 'mainnet') and (network != 'testnet')):
            Err('Networkname error in config-file, should be mainnet or testnet')
    else:
        Err('No Network= in config-file (should be mainnet testnet)')

    SelectParams(network)

    if 'Bitcoinaddress' in confDict:
        bitcoinAddress = confDict['Bitcoinaddress']
        if not (checkbtcaddr(bitcoinAddress)):
            Err('Bitcoinaddress error in config-file')

    if 'Key-ID' not in confDict:
        Err("No Key-ID= in config file")

    if 'Public-key' in confDict:
        pubkey = confDict['Public-key']
        if (len(pubkey) != 130):
            Err("Wrong public key length (!=130) in config-file")
        # check pubkey, first 2 bytes (in hex) should be: 04
        if (pubkey[0] != '0' or pubkey[1] != '4'):
            Err("Wrong public key in config file")
    else:
        Err('No Public-key= in the config file')

    if (debug):
        print(confDict)


def get_utxo(provider_unspent_url):
    #
    # get the Unspent Transaction Outputs (UTXO's)
    # and initialize amountfrom
    # returns:
    #     1) unspents as floats in mbtcval[txid:<vout>]
    #     2) empty list amountfrom

    bitcoinAddress = confDict['Bitcoinaddress']

    mbtcval = {}
    amountfrom = {}

    req = provider_unspent_url+"/"+bitcoinAddress

    # SOME CODE DEPENDS ON THE UNSPENT_URL_PROVIDER: (only needed for smartbit)
    if provider_unspent_url.find("smartbit") > 0:
        req = req + "/unspent" 
        smartbit = True
    else:
        smartbit = False

    if (debug):
        print ("unspent request:", req)

    buf = BytesIO()
    pyc = pycurl.Curl()
    pyc.setopt(pycurl.URL, req)
    pyc.setopt(pycurl.CONNECTTIMEOUT, 50)
    pyc.setopt(pycurl.FAILONERROR, True)
    pyc.setopt(pycurl.USERAGENT, "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0")
    pyc.setopt(pycurl.HTTPHEADER, ['Accept: text/html', 'Accept-Charset: UTF-8'])
    # Set the WRITEFUNCTION and point it to the write string buffer
    pyc.setopt(pyc.WRITEFUNCTION, buf.write)
    # catch the output
    try:
        pyc.perform()
    except IOError:
        Err("Network error, cannot request unspent data")

    respdata = buf.getvalue()
    buf.close()
    if (debug):
        print(respdata)

    # decode from bytes
    jsonobj = json.loads(respdata.decode())

    if (smartbit == False):
       # the provider unspent output should be in json format:
       #     {
       #       "data": {
       #            "unspent": [
       #                  { "amount":  0.00193371,
       #                    "confirmations": 43,
       #                    "n":1,
        jdata = jsonobj["data"]["unspent"]
        amountstr = 'amount'
        txstr = u'tx'
    else:   # smartbit has txid and value in json
       # the provider unspent output should be in json format:
       #     {
       #       "unspent": [
       #            { "value":  0.00193371,
       #               "confirmations": 43,
       #               "n":1,
        jdata = jsonobj["unspent"]
        amountstr = 'value'
        txstr = u'txid'

    for tx in jdata: 
        if (debug):
           print(tx)
        if amountstr in tx:
           mbtcvalue = tx[amountstr]
        else:
           if (debug):
               print("no amounts in unspent")
        if 'n' in tx:
           n = tx["n"]
        else:
           n = ""
           if (debug):
               print("no vout-number n in unspent")
        if 'confirmations' in tx:
           confi = tx["confirmations"]
           if confi < 6:
                # you should not really trust this unspent
                rg =  "r"   # display in red
           else:
                rg = "g"    # display in green
        # mbtcvalue is in btc, transform it in milli -btc
        txid = tx[txstr] + ":" + str(n) + rg
        mbtcvalue = float(mbtcvalue)*1000
        mbtcval[txid] = mbtcvalue
        if (debug):
            print(txid, "==>unspent value:", mbtcvalue)

    # init amountfrom
    for txid in mbtcval:
        amountfrom[txid] = 0.0

    if (debug):
        print(mbtcval)

    return mbtcval, amountfrom


def send_tx(network):
    #
    # Send Transaction tx (in hex) to the provider
    # blockr.io,smartbit:  curl -d '{"hex":"TX_HASH"}' http://btc.blockr.io/api/v1/tx/push
    # webbbtc.com: curl http://test.webbtc.com/relay_tx.json -X \
    #                     POST -d "wait=10&tx=<TX_IN_HEX>"

    try:
        with open(txfilename.get(), "r") as fin:
            tx_hex = fin.readline()
        fin.close()
    except IOError:
        Err('Cannot read transaction file')

    if (network == 'testnet'):
        sendtx_url = confDict['SendTx-Url-Testnet']
    if (network == 'mainnet'):
        sendtx_url = confDict['SendTx-Url-Mainnet']

    buf = BytesIO()
    pyc = pycurl.Curl()

    pyc.setopt(pycurl.URL, sendtx_url)
    pyc.setopt(pycurl.CONNECTTIMEOUT, 50)
    pyc.setopt(pycurl.FAILONERROR, True)
    pyc.setopt(pycurl.USERAGENT, "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0")
    pyc.setopt(pycurl.HTTPHEADER, ['Accept: text/html', 'Accept-Charset: UTF-8'])
    # Set the WRITEFUNCTION to catch the output in buf
    pyc.setopt(pycurl.WRITEFUNCTION, buf.write)

    if (debug):
        print("Send Tx URL:", sendtx_url)

# DEPENDS ON THE SENDTX_URL_PROVIDER: (now blockr and webbtc, smartbit,
# TODO: could add more provider
    if sendtx_url.find("blockr") > 0:
        pyc.setopt(pycurl.POSTFIELDS, '{"hex":"%s"}' % (tx_hex))
        if (debug):
            print("Postdata:", '{"hex":"%s"}' % (tx_hex))
    elif sendtx_url.find("webbtc") > 0:
        pyc.setopt(pycurl.POSTFIELDS, '"wait=10&tx=%s"' % (tx_hex))
        if (debug):
            print("Postdata:", '"wait=10&tx=%s"' % (tx_hex))
    elif sendtx_url.find("smartbit") > 0:
        pyc.setopt(pycurl.POSTFIELDS, '{"hex":"%s"}' % (tx_hex))
        if (debug):
            print("Postdata:", '{"hex":"%s"}' % (tx_hex))
    else:
        msg = "No provider for sending transaction to the network"
        Err(msg)
# END

    pyc.setopt(pycurl.POST, 1)

    if (debug):
        pyc.setopt(pycurl.VERBOSE, True)

    try:
        pyc.perform()
    except pycurl.error as err:
        msg = "Error in sending transaction to the network" + str(err)
        Err(msg)

    response = buf.getvalue()
    buf.close()
    if (debug):
        print(response)

# DEPENDS ON THE SEND_URL_PROVIDER: (now blockr,webbtc,smartbit
    if sendtx_url.find("blockr") > 0:
        jsonobj = json.loads(response.decode())
        if "status" in jsonobj:
            status = jsonobj["status"]
        else:
            status = ""
        if "data" in jsonobj:    
            hash = jsonobj["data"]    # txid of the new transaction
        else:
            hash = ""
    elif sendtx_url.find("smartbit") > 0:
        jsonobj = json.loads(response.decode())
        if "success" in jsonobj:
            status = jsonobj["success"]
        else:
            status = ""
        if "txid" in jsonobj:    
            hash = jsonobj["txid"]    # txid of the new transaction
        else:
            hash = ""
    elif sendtx_url.find("webbtc") > 0:
        jsonobj = json.loads(response.decode())
        if "success" in jsonobj:
            status = jsonobj["success"]
        else:
            status = ""
        if "hash" in jsonobj:    
            hash = jsonobj["hash"]    # txid of the new transaction
# END

    if (status == 1 or status == "success"):
        msg = "Success in sending transaction to the network"
        Info(msg)
        exit()
    else:
        msg = "Failure in sending transaction to the network"
        Err(msg)


def decode_base58(bc, length):
    digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    n = 0
    for char in bc:
        if char in digits58:
            n = n * 58 + digits58.index(char)
        else:
            n = 0 
            pass
    return n.to_bytes(length, 'big')


# Next 3 functions are from Pybitcointools written by Vitalik Butern
# to prevent the "non-mandatory-script-verify-flag Error bug"
# The *pkcs11-tool* I am using still has this bug/error when using it for 
# bitcoins.  Pybitcointools solved it, see
# https://github.com/vbuterin/pybitcointools/issues/89


def get_code_string(base):
    # Base switching
    code_strings = {
        2: '01',
        10: '0123456789',
        16: '0123456789abcdef',
        32: 'abcdefghijklmnopqrstuvwxyz234567',
        58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
        256: ''.join([chr(x) for x in range(256)])
    }
    if base in code_strings:
        return code_strings[base]
    else:
        Err("DER Enoding error")


def encode(val, base, minlen=0):
    # from pybitcointools, used by for der_encode
    base, minlen = int(base), int(minlen)
    code_string = get_code_string(base)
    result_bytes = bytes()
    while val > 0:
        curcode = code_string[val % base]
        result_bytes = bytes([ord(curcode)]) + result_bytes
        val //= base

    pad_size = minlen - len(result_bytes)

    padding_element = b'\x00' if base == 256 else b'1' \
        if base == 58 else b'0'
    if (pad_size > 0):
        result_bytes = padding_element*pad_size + result_bytes

    result_string = ''.join([chr(y) for y in result_bytes])
    result = result_bytes if base == 256 else result_string

    return result


def der_encode_sig(r, s):
    # Takes (r, s) as ints and returns der encoded sig
    s = orderN-s if s > orderN // 2 else s    # BIP62 low s
    b1, b2 = encode(r, 256), encode(s, 256)
    if bytearray(b1)[0] & 0x80:  # if leading byte interpreted as negative
        b1 = b'\x00' + b1        # add null bytes
    if bytearray(b2)[0] & 0x80:
        b2 = b'\x00' + b2
    left  = b'\x02' + encode(len(b1), 256, 1) + b1
    right = b'\x02' + encode(len(b2), 256, 1) + b2
    sigbin = b'\x30' + encode(len(left+right), 256, 1) + left + right
    return sigbin


def checkbtcaddr(bc):
    # see http://rosettacode.org/wiki/Bitcoin/address_validation#Python
    bcbytes = decode_base58(bc, 25)
    return bcbytes[-4:] == sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]


def verdeel(a2p, mbtcval, amountfrom):
    #
    # distribute amount to pay (a2p +fee) over unspents (mbtcval) in mBTC
    # result returned in in amountfrom
    #

    global fee,paybackamount

    if (debug):
        print("verdeel:", a2p)

    sumutxo = 0.0  # sum of *all* unspents in mili BTC

    for txid in mbtcval:
        sumutxo += float(mbtcval[txid])
    if sumutxo < a2p + fee:
        Info("Not enough unspents available (amount-to-pay to high!)")
        return

    r2p = a2p  # rest-to-pay (without fee)

    totutxo = 0.0  #  total amount of *used* unspents
    numutxos = 0   #  total number of unspents

    for txid in mbtcval:
        amountfrom[txid] = 0

    for txid in mbtcval:
        if r2p <= (float(mbtcval[txid]) - fee):
            # enough in this unspent for the rest (incl. fee)
            amountfrom[txid] = r2p
            totutxo += float(mbtcval[txid])
            numutxos += 1
            r2p = 0
            # no more unspents needed
            break
        elif r2p < float(mbtcval[txid]):
            # enough in this unspent for the rest but *without* fee
            amountfrom[txid] = float(mbtcval[txid])  # take the whole unspent
            totutxo += float(mbtcval[txid])
            numutxos += 1
            r2p -= float(mbtcval[txid])
            if r2p <  0.0000:     # could happen...
                r2p = 0.0011    # the next unspent is needed for the fee, so keep a small rest-to-pay
                fee = fee - 0.0011
        else: 
            # take whole unspent, but its not enough 
            amountfrom[txid] = float(mbtcval[txid])  # take the whole unspent
            totutxo += float(mbtcval[txid])
            numutxos += 1
            r2p -= float(mbtcval[txid])
            
    # if its a complex transaction, make the fee somewhat higher
    if (numutxos > 6):
        fee = fee * 3 
    elif (numutxos > 3):
        fee = fee * 2 

    paybackamount = totutxo - a2p - fee

    if (debug):
        for txid in amountfrom:
            print(txid, "==>", amountfrom[txid])
        print("number of unspents:", numutxos)
        print("payback from last unspent:", paybackamount)

    # update GUI
    for txid in mbtcval:
        setamounts()

    return amountfrom


def setamounts():
    #
    # update values in GUI
    #

    global fee
    global homedir
    i = 0
    maxunspentlines = int(confDict['MaxUnspentLines'])
    for txid in mbtcval:
        amountfrom_entry[i].delete(0, END)
        if amountfrom[txid] < 0.00001:
            amountfrom_entry[i].insert(0, 'not used')
        elif amountfrom[txid] == 0.0011:
            amountfrom_entry[i].insert(0, 'needed for fee')
        else:
            amountfrom_entry[i].insert(0, str(amountfrom[txid]))
        i += 1
        if (i == maxunspentlines): break

    returntransaction_entry.delete(0, END)
    returntransaction_entry.insert(0, str(paybackamount))
    transactionfee_entry.delete(0, END)
    transactionfee_entry.insert(0, str(fee))
    now = datetime.datetime.now().strftime("%y-%m-%d-%H-%M-%S")
    txdir = confDict['BTCtransactionsDir']
    if txdir.find(homedir) == -1:
        txfilename.set(homedir + "/" + txdir + "/tx"+str(now)+".hex")
    else:
        txfilename.set(txdir + "/tx"+str(now)+".hex")


def buildtx(mbtcval, amountfrom, btcToAddress):
    #
    # create unsigned transaction with spended amount
    # to be called *after* verdeel
    #

    txin = []
    txout = []

    # spended amount mBTC from userinput to satoshi's in blockchain
    # mBTC    = 0.001 BTC      = 1/1000 BTC
    # satoshi = 0.00000001 BTC = 1/100.000 mBTC
    # COIN (from bitcoinlibrary = 1 BTC)  = 100.000.000 satoshis
    # COIN and factor are type int. Satoshi values are tpe int.
    # mBTC values are type float
    factor = int(COIN*0.001)  # 1 mBTC to satoshi

    # build list of tx-input structures (source-tx + empty scriptSig.)
    a2p = 0.0  # amount-to-pay in mBTC
    sumutxo = 0.0  # sum of unspents for returnvalue
    for txid in amountfrom:
        if amountfrom[txid] > 0.0:  # collect only the transactions with inputs
            # that are really used in verdeel() and compute the sum of unspents
            sumutxo += float(mbtcval[txid])
            a2p += amountfrom[txid]
            # create txin structure from source-tx + empty scriptSig
            # convert transaction id from little-endian (the blockchain
            # representation) to bytes with lx()
            srctx, voutstr = txid.split(":")
            srctx = lx(srctx)
            vout = int(voutstr[:-1])
            # txin structure: the source-tx + empty scriptSig.
            txin.append(CMutableTxIn(COutPoint(srctx, vout)))
            if (debug):
                print("input txid:", txid)
                print("amount:", float(mbtcval[txid]))

    # build txout, the first (vout 0) with amount to pay in satoshi
    topay_tx = int(a2p*factor)
    txout.append(CMutableTxOut(topay_tx, 
                 CBitcoinAddress(btcToAddress).to_scriptPubKey()))

    # second txout (vout 1) with return transaction (to my own bitcoinaddress)
    # paybackamount retam = sum of unspents - amount-to-pay - fee
    # only return to my own bitcoinaddress the restamount if > fee
    # (if not, it is added to the fee)
    retam = sumutxo - a2p - fee

    # fee is in mBTC
    # fee_tx should be feexx
    # _tx values are in Satoshis
    fee_tx = int(fee * factor)
    return_tx = int(retam*factor)

    feexx = sumutxo*factor - topay_tx - return_tx

    if (debug):
        print("topay_tx:", topay_tx, "Satoshi")
        print("return_tx:", return_tx, "Satoshi")
        print("fee_tx:", fee_tx, "Satoshi")
        print("feexx:", feexx, "Satoshi")
        print("sumutxo:", sumutxo, "mBtc")

    # should never happen:
    assert (feexx < fee_tx*1.01) and (feexx > fee_tx*0.99)
    maxfee = 4* float(confDict['Transactionfee'])
    assert (fee < maxfee) 

    if retam > fee:
        # only return to my own bitcoinaddress the restamount if its > fee
        # (if not, it is added to the fee)
        bitcoinAddress = confDict["Bitcoinaddress"]
        txout.append(CMutableTxOut(return_tx, 
                 CBitcoinAddress(bitcoinAddress).to_scriptPubKey()))

    # return the unsigned transaction
    return (txin, txout)


class PinDialog:
    def __init__(self, parent):
        top = self.top = Toplevel(parent)
        top.title("PIN Entry")
        top.lift()
        self.myLabel = Label(top, text='*** Please Enter your PIN: ***')
        self.myLabel.pack()
        self.myEntryBox = Entry(top, show="*")
        self.myEntryBox.bind("<Return>", self.sendpin)
        self.myEntryBox.pack()
        self.mySubmitButton = Button(top, text='Submit')
        self.mySubmitButton.bind("<Button-1>", self.sendpin)
        self.mySubmitButton.pack()
        self.myEntryBox.focus_set()
        self.pin = ""

    def sendpin(self, event):
        self.pin = self.myEntryBox.get()
        self.top.destroy()


def signtx(mbtcval, amountfrom, btcToAddress):
    #
    # check ToAddress,buildtx and sign tx
    #


    if not btcToAddress:
        # nothing to sign
        Info("Cannot sign: target-address is missing")
        return

    if not (checkbtcaddr(btcToAddress)):
        # nothing to sign
        Info('Cannot sign, error in bitcoin target-address')
        return

    txin, txout = buildtx(mbtcval, amountfrom, btcToAddress)

    if (debug):
        print("signtx:", txin, txout)

    sign(txin, txout)


def sign(txin, txout):
    # By signing, we prove that we own the amount from output and can spend it.

    pin = ''

    # create the *complete* unsigned transaction
    tx = CMutableTransaction(txin, txout)

    # create scriptPubKey for the transaction unlocking script
    # This is the standard bitcoin transaction "pay-to-pubkey-hash"
    bpubkey = confDict['Public-key']
    bpubkey.strip()
    bpubkey = x(bpubkey)  # convert hexstring to bytes
    txin_scriptPubKey = CScript([OP_DUP, OP_HASH160, Hash160(bpubkey),
                                OP_EQUALVERIFY, OP_CHECKSIG])

    ix = -1
    for trns in txin:
        ix += 1
        # Calculate the signature hash for transaction tx[ix], type SIGHASH_ALL:
        sighash = SignatureHash(txin_scriptPubKey, tx, ix, SIGHASH_ALL)
        if (debug):
            print("sighash-hex:", b2x(sighash))

        # writing a temporary file for the unsigned input
        ftmpin = tempfile.NamedTemporaryFile(delete=False)
        ftmpin.write(sighash)
        ftmpin.close()

        # temporary file for the output (signed sighash)
        ftmpout = tempfile.NamedTemporaryFile(delete=False)

        if (debug):
            print(ftmpin.name)
            print(ftmpout.name)

        # start pinentry dialog if pin unknown
        # parent-window is root
        if not pin:
            pinDialog = PinDialog(root)
            root.wait_window(pinDialog.top)
            pin = pinDialog.pin
            root.deiconify()
            root.lift()

        key_id = confDict["Key-ID"]
        # sign sighash, output r,s format
        command = ("pkcs11-tool --module " + p11module + 
                   " --mechanism ECDSA --pin " + pin + 
                   " --sign --signature-format rs --id " + key_id + 
                   "  --input-file " + ftmpin.name + " --output-file "
                   + ftmpout.name)
 
        if (debug):
            print(command)
        return_code = subprocess.call(command, shell=True)
        if (return_code != 0):
            Err("Error in signing with smartcard")

        # reading the file in  binary mode, signature is rs format
        fd = open(ftmpout.name, "rb")
        sigrs = fd.read()
        fd.close()
        if (debug):
            print("sigrs=",sigrs)
            print("sigrslen:",len(sigrs))

        # BIP 66
        # convert the byte sequence of 72 bytes into a tuple of 2 ints
        rbytes = sigrs[:32]
        sbytes = sigrs[32:]
        r = int.from_bytes(rbytes,byteorder='big')  # the ecdsa byteorder
        s = int.from_bytes(sbytes,byteorder='big')
        if (debug):
            print ("r=",r)
            print ("s=",s)

        sig = der_encode_sig(r,s)

        # delete temp files
        # if not (debug):
        os.unlink(ftmpin.name)
        os.unlink(ftmpout.name)

        if (debug):
            print("sig=", sig)

        # append the type of signature to the end (=SIGHASH_ALL)
        sig = sig + bytes([SIGHASH_ALL])

        #### Build the scriptSig of our transaction input and the public key
        #### put it  the txin at index ix
        txin[ix].scriptSig = CScript([sig, bpubkey])

    # Verify if the scriptSig satisfies the scriptPubkey (only first is enough)
    if (debug):
        print("txin[0].scriptSig=", txin[0].scriptSig)
        try:
            VerifyScript(txin[0].scriptSig, txin_scriptPubKey, tx, 0,
                         (SCRIPT_VERIFY_P2SH,))
            print("Scriptsig OK")
        except ValidationError:
            print("Error: bad scriptSig")

    if (debug):
        # Print the transaction to standard output in hex
        print(b2x(tx.serialize()))

    fout = open(txfilename.get(), "w")
    # writing the file in hex
    fout.write(b2x(tx.serialize()))
    fout.close()
    msg = "Signed transaction file written: "+txfilename.get()
    Info(msg)

###################  main  ########################

root = Tk()
# Ask configfile-id (~/.smartbtc/id.conf)
root.iconify()
confDialog = ConfDialog(root)
root.wait_window(confDialog.top)
id = confDialog.confid
root.deiconify()
if (debug):
    print("conf-id =", id)

# read the configfile
# set some globals from the config file
readConfig(id)
network = confDict['Network']
fee = float(confDict['Transactionfee'])
maxunspentlines = int(confDict['MaxUnspentLines'])

if (debug):
    print("Bitcoinaddress:", confDict['Bitcoinaddress'])
    print("Network=", network)
    print("Public-key:", confDict['Public-key'])
    print("Key-ID=", confDict['Key-ID'])
    print("fee:", fee)
    print("maxunspentlines:", maxunspentlines)


if (network == 'testnet'):
    provider_url = confDict['Provider-Url-Testnet']
    sendtx_url = confDict['SendTx-Url-Testnet']
if (network == 'mainnet'):
    provider_url = confDict['Provider-Url-Mainnet']
    sendtx_url = confDict['SendTx-Url-Mainnet']

if (debug):
    print("Provider-Url:", provider_url)
    print("Provider-SendTx-Url:", sendtx_url)

# get the unspends from provider

mbtcval, amountfrom = get_utxo(provider_url)
paybackamount = 0.0


######### GUI ##########

amount = StringVar()
amount.set('0')
toAddress = StringVar()
toAddress.set('')
returntransaction = StringVar()
returntransaction.set('0')
transactionfee = StringVar()
transactionfee.set('0')
# transaction filename (default)
txfilename = StringVar()
root.title("*** SMART=BITCOIN ***")
mainwin = ttk.Frame(root, padding="3 3 12 12")
r = 0
mainwin.grid(column=0, row=r, sticky=(N, W, E, S))
mainwin.columnconfigure(0, weight=1)
mainwin.rowconfigure(0, weight=1)

btcimgfile = homedir + "/" + ".smartbtc/" + "bitcoin64.png"
try:
    bitcoinpic = PhotoImage(file=btcimgfile)
    ttk.Label(mainwin, image=bitcoinpic).grid(column=3, row=r, sticky=(N, E))
except:
    if (debug):
        print("Could not load picturefile bitcoin64.png")
    pass

bitcoinAddress = confDict['Bitcoinaddress']
ttk.Label(mainwin, text="MY BITCOIN ADDRESS: ",
          font="TkHeadingFont").grid(column=1, row=r, sticky=(W, E))
ttk.Label(mainwin, text=bitcoinAddress,
          foreground='blue').grid(column=2, row=r, sticky=W)

r += 1  # next row
# list the Unspent Transaction Outputs (UTXO's)

r += 1
s = "======================"
ttk.Label(mainwin,
    text= s + " BITCOIN TRANSACTION (Values in mBTC) " + s,
    font="TkHeadingFont").grid(column=1, row=r, columnspan=2, sticky=(W, E))

r += 1
totalunspent = 0
i = 0   # first unspentline number
iu = 0  # unspent number
for txid in mbtcval:
    iu += 1 
    i += 1
    if i <= maxunspentlines:
        # only display if maxunspentlines for display is not reached
        ttk.Label(mainwin,
              text="Unspent (" + str(i) + "):").grid(column=1, row=r, sticky=E)
        ttk.Label(mainwin, text=txid).grid(column=2, row=r, sticky=(W, E))
        mbtcvalue = str(mbtcval[txid])
        pos = mbtcvalue.index('.')
        mbtcvalue = mbtcvalue[0:pos+3]
        rg = txid[-1]
        if (rg == 'g') :
        # display amount in red if not enough confimations
            ttk.Label(mainwin, text=mbtcvalue+" mBTC",
                  foreground = 'green').grid(column=3, row=r, sticky=E)
        elif (rg == 'r') :
            ttk.Label(mainwin, text=mbtcvalue+" mBTC",
                  foreground = 'red').grid(column=3, row=r, sticky=E)
        else:
            if (debug):
                print("No confirmationcolor red/green")
            ttk.Label(mainwin, text=mbtcvalue+" mBTC",
                  foreground = 'black').grid(column=3, row=r, sticky=E)
        r += 1
    totalunspent = totalunspent + float(mbtcval[txid])

totalunspstr = str(totalunspent)
ttk.Label(mainwin, text="*****Total Unspent in "+str(iu)+" unspents:").grid(column=1, 
          row=r, sticky=E)
ttk.Label(mainwin, text=totalunspstr, foreground="green").grid(column=2,
          row=r, sticky=(W, E))
ttk.Label(mainwin, text="mBTC").grid(column=3, row=r, sticky=E)
r += 1
ttk.Label(mainwin, text="Target Bitcoin Address:",font="Helvetica 10 bold").grid(column=1, row=r, sticky=E)
toAddress_entry = ttk.Entry(mainwin, width=34, textvariable=toAddress)
toAddress_entry.grid(column=2, row=r, sticky=(W, E))

r += 1
ttk.Label(mainwin, text="Amount to pay in mBTC:",font="Helvetica 10 bold").grid(column=1, row=r, sticky=E)
amount_entry = ttk.Entry(mainwin, width=20, textvariable=amount)
amount_entry.grid(column=2, row=r,  sticky=(W, E))
ttk.Label(mainwin, text="mBTC").grid(column=3, row=r, sticky=E)

r += 1
#  should be pressed after amount and to-address is given by the user
ttk.Button(mainwin, text="CREATE TRANSACTION",
           command=(lambda: verdeel(float(amount.get()), mbtcval,
           amountfrom))).grid(column=2, row=r, sticky=W)

# show the distribution from the amount-to-pay over the unspents
amountfrom_entry = []
i = 1  # spentline
for txid in amountfrom:
    if (i <= maxunspentlines):
        r += 1
        ttk.Label(mainwin,
            text="Amount from:"+str(i)+": ").grid(column=1, row=r, sticky=E)
        entry = ttk.Entry(mainwin, width=7)
        amountfrom_entry.append(entry)
        entry.delete(0, END)
        entry.insert(0, amountfrom[txid])
        entry.grid(column=2, row=r, sticky=(W, E))
        ttk.Label(mainwin, text="mBTC").grid(column=3, row=r, sticky=E)
        i += 1

r += 1
# Transaction Fee
ttk.Label(mainwin, text="Transaction Fee").grid(column=1, row=r, sticky=E)
transactionfee_entry = ttk.Entry(mainwin, width=7, textvariable=fee)
transactionfee_entry.grid(column=2, row=r, sticky=(W, E))
ttk.Label(mainwin, text="mBTC").grid(column=3, row=r, sticky=E)

r += 1
# Returntransaction, amount to pay back to myself
ttk.Label(mainwin, text="Returntransaction").grid(column=1, row=r, sticky=E)
returntransaction_entry = ttk.Entry(mainwin, width=7, 
                                    textvariable=str(paybackamount))
returntransaction_entry.grid(column=2, row=r, sticky=(W, E))
ttk.Label(mainwin, text="mBTC").grid(column=3, row=r, sticky=E)

r += 1
# sign transaction
ttk.Button(mainwin, text="SIGN TRANSACTION",
           command=(lambda: signtx(mbtcval, amountfrom,
           toAddress.get()))).grid(column=2, row=r, sticky=(W, E))
#for child in mainwin.winfo_children():
#    child.grid_configure(padx=5, pady=5)

r += 1
# push transaction
ttk.Button(mainwin, text="PUSH TRANSACTION TO NETWORK",
           command=(lambda: send_tx(network))).grid(column=2, row=r, sticky=(W, E))
#for child in mainwin.winfo_children():
#    child.grid_configure(padx=5, pady=5)

r += 1
ttk.Label(mainwin, text="transaction filename:").grid(column=1, row=r, sticky=E)
txfilename_entry = ttk.Entry(mainwin, width=20, textvariable=txfilename)
txfilename_entry.grid(column=2, row=r,  sticky=(W, E))

for child in mainwin.winfo_children():
    child.grid_configure(padx=3, pady=3)

root.mainloop()