Advertisement
Guest User

bitpaint.py

a guest
Oct 15th, 2012
272
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python
  2.  
  3. """
  4. bitpaint.py
  5. ~~~~~~~~~~~
  6. Simple command-line utility to use the Bitcoin blockchain to track and manage so-called "colored coins", or "smart contracts/securities".
  7.  
  8. Caution:
  9. - Private keys are stored in plaintext in your configuration file
  10. - Do not allow the "colored coin" to be used in a transaction with
  11.  multiple inputs. That would mess up the tracking. I suggest avoid
  12.  using holding-addresses in your normal wallet, and just use this
  13.  client to manage your colored coins.
  14. - Since there is no transaction fee included during asset transfer,
  15.  you cannot be sure that the transaction will be confirmed when transferring
  16.  "assets"
  17.  
  18. Donations welcome: 1GsnaaAWYMb7yPwBQ19bSLLMTtsfSyjqg3
  19.  
  20. """
  21.  
  22. # Import libraries
  23. import urllib2, ConfigParser, ctypes, ctypes.util, hashlib, simplejson as json, os
  24. from jsonrpc import ServiceProxy
  25. from optparse import OptionParser
  26.  
  27. ### Start: Generic helpers
  28. def JSONtoAmount(value):
  29.     return long(round(value * 1e8))
  30. def AmountToJSON(amount):
  31.     return float(amount / 1e8)
  32. ### End: Generic helpers
  33.  
  34.  
  35. ### Start: Create/Read Config
  36. # Here we create a configuration file if one does not exist already.
  37. # User is asked for details about his bitcoind so the script can connect.
  38. config_file = "bitpaint.conf"
  39. basic_bitpaint_conf = """[bitcoind]
  40. rpchost = %s
  41. rpcport = %s
  42. rpcuser = %s
  43. rpcpwd = %s
  44.  
  45. [HoldingAddresses]
  46. addresses =
  47. private_keys =
  48. """
  49. # If the config file does not exists, ask user for details and write one
  50. if not os.path.exists(config_file):
  51.     print "Configuration file bitpaint.conf not found. Creating one..."
  52.     host = raw_input("bitcoind rpc host (default: 127.0.0.1): ")
  53.     if len(host) == 0: host = "127.0.0.1"
  54.     port = raw_input("bitcoind rpc port (default: 8332): ")
  55.     if len(port) == 0: port = "8332"
  56.     user = raw_input("bitcoind rpc username (default: <blank>): ")
  57.     pwd = raw_input("bitcoind rpc password (default: <blank>): ")
  58.     f = open(config_file, 'w')
  59.     f.write(basic_bitpaint_conf % (host,port,user,pwd))
  60.     f.close()
  61.  
  62. # Parse the config file
  63. reserved_sections = ['bitcoind', 'HoldingAddresses']
  64. config = ConfigParser.ConfigParser()
  65. config.read(config_file)
  66. rpchost = config.get('bitcoind', 'rpchost')
  67. rpcport = config.get('bitcoind', 'rpcport')
  68. rpcuser = config.get('bitcoind', 'rpcuser')
  69. rpcpwd  = config.get('bitcoind', 'rpcpwd')
  70.  
  71. # Connect to bitcoind
  72. if len(rpcuser) == 0 and len(rpcpwd) == 0:
  73.     bitcoind_connection_string = "http://s:%s" % (rpchost,rpcport)
  74. else:
  75.     bitcoind_connection_string = "http://%s:%s@%s:%s" % (rpcuser,rpcpwd,rpchost,rpcport)
  76. sp = ServiceProxy(bitcoind_connection_string)
  77.  
  78. ### End: Create/Read Config
  79.  
  80.  
  81. ### Start: Address Generation code
  82. # The following code was yoinked from addrgen.py
  83. # Thanks to: Joric/bitcoin-dev, june 2012, public domain
  84. ssl = ctypes.cdll.LoadLibrary (ctypes.util.find_library ('ssl') or 'libeay32')
  85.  
  86. def check_result (val, func, args):
  87.     if val == 0: raise ValueError
  88.     else: return ctypes.c_void_p (val)
  89.  
  90. ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
  91. ssl.EC_KEY_new_by_curve_name.errcheck = check_result
  92.  
  93. class KEY:
  94.     def __init__(self):
  95.         NID_secp256k1 = 714
  96.         self.k = ssl.EC_KEY_new_by_curve_name(NID_secp256k1)
  97.         self.compressed = False
  98.         self.POINT_CONVERSION_COMPRESSED = 2
  99.         self.POINT_CONVERSION_UNCOMPRESSED = 4
  100.  
  101.     def __del__(self):
  102.         if ssl:
  103.             ssl.EC_KEY_free(self.k)
  104.         self.k = None
  105.  
  106.     def generate(self, secret=None):
  107.         if secret:
  108.             self.prikey = secret
  109.             priv_key = ssl.BN_bin2bn(secret, 32, ssl.BN_new())
  110.             group = ssl.EC_KEY_get0_group(self.k)
  111.             pub_key = ssl.EC_POINT_new(group)
  112.             ctx = ssl.BN_CTX_new()
  113.             ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx)
  114.             ssl.EC_KEY_set_private_key(self.k, priv_key)
  115.             ssl.EC_KEY_set_public_key(self.k, pub_key)
  116.             ssl.EC_POINT_free(pub_key)
  117.             ssl.BN_CTX_free(ctx)
  118.             return self.k
  119.         else:
  120.             return ssl.EC_KEY_generate_key(self.k)
  121.  
  122.     def get_pubkey(self):
  123.         size = ssl.i2o_ECPublicKey(self.k, 0)
  124.         mb = ctypes.create_string_buffer(size)
  125.         ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb)))
  126.         return mb.raw
  127.  
  128.     def get_secret(self):
  129.         bn = ssl.EC_KEY_get0_private_key(self.k);
  130.         bytes = (ssl.BN_num_bits(bn) + 7) / 8
  131.         mb = ctypes.create_string_buffer(bytes)
  132.         n = ssl.BN_bn2bin(bn, mb);
  133.         return mb.raw.rjust(32, chr(0))
  134.  
  135.     def set_compressed(self, compressed):
  136.         self.compressed = compressed
  137.         if compressed:
  138.             form = self.POINT_CONVERSION_COMPRESSED
  139.         else:
  140.             form = self.POINT_CONVERSION_UNCOMPRESSED
  141.         ssl.EC_KEY_set_conv_form(self.k, form)
  142.  
  143. def dhash(s):
  144.     return hashlib.sha256(hashlib.sha256(s).digest()).digest()
  145.  
  146. def rhash(s):
  147.     h1 = hashlib.new('ripemd160')
  148.     h1.update(hashlib.sha256(s).digest())
  149.     return h1.digest()
  150.  
  151. b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
  152.  
  153. def base58_encode(n):
  154.     l = []
  155.     while n > 0:
  156.         n, r = divmod(n, 58)
  157.         l.insert(0,(b58_digits[r]))
  158.     return ''.join(l)
  159.  
  160. def base58_decode(s):
  161.     n = 0
  162.     for ch in s:
  163.         n *= 58
  164.         digit = b58_digits.index(ch)
  165.         n += digit
  166.     return n
  167.  
  168. def base58_encode_padded(s):
  169.     res = base58_encode(int('0x' + s.encode('hex'), 16))
  170.     pad = 0
  171.     for c in s:
  172.         if c == chr(0):
  173.             pad += 1
  174.         else:
  175.             break
  176.     return b58_digits[0] * pad + res
  177.  
  178. def base58_decode_padded(s):
  179.     pad = 0
  180.     for c in s:
  181.         if c == b58_digits[0]:
  182.             pad += 1
  183.         else:
  184.             break
  185.     h = '%x' % base58_decode(s)
  186.     if len(h) % 2:
  187.         h = '0' + h
  188.     res = h.decode('hex')
  189.     return chr(0) * pad + res
  190.  
  191. def base58_check_encode(s, version=0):
  192.     vs = chr(version) + s
  193.     check = dhash(vs)[:4]
  194.     return base58_encode_padded(vs + check)
  195.  
  196. def base58_check_decode(s, version=0):
  197.     k = base58_decode_padded(s)
  198.     v0, data, check0 = k[0], k[1:-4], k[-4:]
  199.     check1 = dhash(v0 + data)[:4]
  200.     if check0 != check1:
  201.         raise BaseException('checksum error')
  202.     if version != ord(v0):
  203.         raise BaseException('version mismatch')
  204.     return data
  205.  
  206. def gen_eckey(passphrase=None, secret=None, pkey=None, compressed=False, rounds=1):
  207.     k = KEY()
  208.     if passphrase:
  209.         secret = passphrase.encode('utf8')
  210.         for i in xrange(rounds):
  211.             secret = hashlib.sha256(secret).digest()
  212.     if pkey:
  213.         secret = base58_check_decode(pkey, 128)
  214.         compressed = len(secret) == 33
  215.         secret = secret[0:32]
  216.     k.generate(secret)
  217.     k.set_compressed(compressed)
  218.     return k
  219.  
  220. def get_addr(k):
  221.     pubkey = k.get_pubkey()
  222.     secret = k.get_secret()
  223.     hash160 = rhash(pubkey)
  224.     addr = base58_check_encode(hash160)
  225.     payload = secret
  226.     if k.compressed:
  227.         payload = secret + chr(0)
  228.     pkey = base58_check_encode(payload, 128)
  229.     return addr, pkey
  230. ### End: Address Generation code
  231.  
  232.  
  233. ### Start: Transaction code
  234. def bc_address_to_hash_160(addr):
  235.     bytes = b58decode(addr, 25)
  236.     return bytes[1:21]
  237.  
  238. __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
  239. __b58base = len(__b58chars)
  240.  
  241. def b58decode(v, length):
  242.     """ decode v into a string of len bytes
  243.     """
  244.     long_value = 0L
  245.     for (i, c) in enumerate(v[::-1]):
  246.         long_value += __b58chars.find(c) * (__b58base**i)
  247.     result = ''
  248.     while long_value >= 256:
  249.         div, mod = divmod(long_value, 256)
  250.         result = chr(mod) + result
  251.         long_value = div
  252.     result = chr(long_value) + result
  253.     nPad = 0
  254.     for c in v:
  255.         if c == __b58chars[0]: nPad += 1
  256.         else: break
  257.     result = chr(0)*nPad + result
  258.     if length is not None and len(result) != length:
  259.         return None
  260.     return result
  261.  
  262. def makek():
  263.     # Create a dictionary with address as key and private-key
  264.     # as value
  265.     a = config.get('HoldingAddresses', 'addresses').split("+")
  266.     p = config.get('HoldingAddresses', 'private_keys').split("+")
  267.     k = {}
  268.     for a in zip(a,p):
  269.         k[a[0]] = a[1]
  270.     return k
  271.  
  272. def maketx(inputs, outputs, send=False):
  273.     # Create a transaction, sign it - possibly send it - but
  274.     # in either case return the raw hex
  275.     # inputs: [('a7813e20045b2f2caf612c589adc7e985029167106a300b6a7157084c26967f5', 1, '1PPgZP53BrcG7hyXdWT9fugewmxL1H8LS3'),...]
  276.     # outputs: [('1KRavVCsvaLi7ZzktHSCE3hPUvhPDhQKhz', 8000000),...]
  277.     ip = []
  278.     for txid,vout,_ in inputs:
  279.         ip.append({"txid": txid, "vout": vout})
  280.     op = {}
  281.     for addr,amnt in outputs:
  282.         op[addr] = AmountToJSON(amnt)
  283.     tx = sp.createrawtransaction(ip,op)
  284.     k = makek()
  285.     ip = []
  286.     pkeys = []
  287.     for txid,vout,addr in inputs:
  288.         ip.append({"txid": txid, "vout": vout, "scriptPubKey": '76a914'+bc_address_to_hash_160(addr).encode('hex')+'88ac'})
  289.         pkeys.append(k[addr])
  290.     final_t = sp.signrawtransaction(tx,ip,pkeys)
  291.     if send:
  292.         sp.sendrawtransaction(tx)
  293.     else:
  294.         print final_t['hex']
  295.     return final_t['hex']
  296.  
  297. ### End: Transaction code
  298.  
  299.  
  300. ### Start: Blockchain Inspection/Traversion code
  301. def translate_bctx_to_bitcoindtx(tx_bc):
  302.     tx = {}
  303.     tx['locktime'] = 0
  304.     tx['txid'] = tx_bc['hash']
  305.     tx['version'] = tx_bc['ver']
  306.     tx['vin'] = []
  307.     tx['vout'] = []
  308.     for i in tx_bc['inputs']:
  309.         v = {}
  310.         v['scriptSig'] = {'asm': '', 'hex': ''}
  311.         v['sequence'] = 4294967295
  312.         v['txid'] = json.loads(urllib2.urlopen("http://blockchain.info/rawtx/%s" % (i['prev_out']['tx_index'],)).read())['hash']
  313.         v['vout'] = i['prev_out']['n']
  314.         tx['vin'].append(v)
  315.     for i in range(len(tx_bc['out'])):
  316.         o = tx_bc['out'][i]
  317.         v = {}
  318.         v['n'] = i
  319.         v['scriptPubKey'] = {}
  320.         v['scriptPubKey']['addresses'] = [o['addr']]
  321.         v['scriptPubKey']['asm'] = []
  322.         v['scriptPubKey']['hex'] = []
  323.         v['scriptPubKey']['reqSigs'] = 1,
  324.         v['scriptPubKey']['type'] = 'pubkeyhash'
  325.         v['value'] = float(o['value'])/1e8
  326.         tx['vout'].append(v)
  327.     return tx
  328.  
  329. def gettx(txid):
  330.     # Get the information of a single transaction, using
  331.     # the bitcoind API
  332.     try:
  333.         tx_raw = sp.getrawtransaction(txid)
  334.         tx = sp.decoderawtransaction(tx_raw)
  335.     except:
  336.         print "Error getting transaction "+txid+" details from bitcoind, trying blockchain.info"
  337.         print "http://blockchain.info/rawtx/%s" % (txid,)
  338.         tx_bc = json.loads(urllib2.urlopen("http://blockchain.info/rawtx/%s" % (txid,)).read())
  339.         tx = translate_bctx_to_bitcoindtx(tx_bc)
  340.     return tx
  341.  
  342. def getaddresstxs(address):
  343.     # Get all transactions associated with an address.
  344.     # Uses blockchain.info to get this, bitcoind API
  345.     # apparently has no equivalent function.
  346.     address_info = json.loads(urllib2.urlopen("http://blockchain.info/address/%s?format=json"%(address,) ).read())
  347.     tx_list = []
  348.     for tx in address_info['txs']:
  349.         tx_list.append(tx['hash'])
  350.     return tx_list
  351.  
  352. def getholderschange(txid):
  353.     # Get a list of the new holders and old holders represented by a
  354.     # single transaction, given as the tx-id.
  355.     tid = txid.split(":")
  356.     tx = gettx(tid[0])
  357.     new_holders = []
  358.     old_holders = []
  359.     for i in tx['vin']:
  360.         old_holders.append(i['txid']+":"+str(i['vout']))
  361.     for o in tx['vout']:
  362.         new_holders.append((o['scriptPubKey']['addresses'][0], o['value']))
  363.     return new_holders, old_holders
  364.  
  365. def spentby(tx_out, single_input = False):
  366.     # Return the id of the transaction which spent the given txid/#
  367.     # if single_input is true, it only returns if the tx_out was used as a single
  368.     # input to the transaction.
  369.     # This is because it is not possible to follow a colored coin across a transaction
  370.     # with multiple inputs
  371.     tid = tx_out.split(":")
  372.     tx = gettx(tid[0])
  373.     address = tx['vout'][int(tid[1])]['scriptPubKey']['addresses'][0]
  374.     tx_list = getaddresstxs(address)
  375.     for t in tx_list:
  376.         outputs,inputs = getholderschange(t)
  377.         if single_input and not len(inputs) == 1:
  378.             print t,"used with multiple inputs: tracking lost at address",address
  379.             continue
  380.         for i in inputs:
  381.             if i == tx_out:
  382.                 return t
  383.     return None
  384.  
  385. def rec(prevout_txid, root_tx):
  386.     # Recursive function to traverse the "tree of ownership", depth-first,
  387.     # head-recursion.
  388.     holder_list = []
  389.    
  390.     if prevout_txid == root_tx:
  391.         spent_by = spentby(prevout_txid, single_input = False)
  392.     else:
  393.         spent_by = spentby(prevout_txid, single_input = True)
  394.     if spent_by is None:
  395.         tx_data = gettx(prevout_txid.split(":")[0])
  396.         o = tx_data['vout'][int(prevout_txid.split(":")[1])]
  397.         holder_list.append((o['scriptPubKey']['addresses'][0],o['value'],prevout_txid))
  398.         return holder_list
  399.     tx_data = gettx(spent_by)
  400.  
  401.     for o in tx_data['vout']:
  402.         ntxid = spent_by+":"+str(o['n'])
  403.         hl = rec(ntxid,root_tx)
  404.         for h in hl:
  405.             holder_list.append(h)
  406.  
  407.     return holder_list
  408.  
  409. def get_current_holders(root_tx_out):
  410.     # Get the current holders of the "colored coin" with
  411.     # the given root (a string with txid+":"+n_output)
  412.     return rec(root_tx_out,root_tx_out)
  413.  
  414. def get_unspent(addr):
  415.     # Get the unspent transactions for an address
  416.     # * Following section is disabled because blockchain.info has a bug that
  417.     #   returns the wrong transaction IDs.
  418.     #d = json.loads(urllib2.urlopen('http://blockchain.info/unspent?address=%s' % addr).read())
  419.     #return d
  420.     # * Start of blockchain.info bug workaround:
  421.     txs = getaddresstxs(addr)
  422.     received = []
  423.     sent = []
  424.     for txid in txs:
  425.         tx = gettx(txid)
  426.         for i in tx['vin']:
  427.             prev_out = gettx(i['txid'])
  428.             for po in prev_out['vout']:
  429.                 n = po['n']
  430.                 po = po['scriptPubKey']
  431.                 if po['type'] == 'pubkeyhash':
  432.                     if po['addresses'][0] == addr:
  433.                         sent.append(i['txid']+":"+str(n))
  434.         for o in tx['vout']:
  435.             n = o['n']
  436.             o = o['scriptPubKey']
  437.             if o['type'] == 'pubkeyhash':
  438.                 if o['addresses'][0] == addr:
  439.                     received.append(txid+":"+str(n))
  440.     unspent = []
  441.     for r in received:
  442.         if r not in sent:
  443.             d = {}
  444.             txid,n = r.split(":")
  445.             d['tx_hash'] = txid
  446.             d['tx_output_n'] = int(n)
  447.             d['value'] = int(1e8*gettx(txid)['vout'][int(n)]['value'])
  448.             unspent.append(d)
  449.     return unspent
  450.     # * End of blockchain.info bug workaround
  451.    
  452.  
  453. def get_non_asset_funds(addr):
  454.     unspent = get_unspent(addr)
  455.     asset_txids = []
  456.     for s in config.sections():
  457.         if s in reserved_sections: continue
  458.         for txid in config.get(s, 'txid').split("+"):
  459.             asset_txids.append(txid)
  460.     naf = []
  461.     for u in unspent:
  462.         txid = u['tx_hash']+":"+str(u['tx_output_n'])
  463.         if not txid in asset_txids:
  464.             naf.append(u)
  465.     return naf
  466.  
  467. ### End: Blockchain Inspection/Traversion code
  468.  
  469.  
  470. ### Start: "User-facing" methods
  471. def generate_holding_address():
  472.     # Generate an address, add it to the config file
  473.     addr, pkey = get_addr(gen_eckey())
  474.     addresses = config.get('HoldingAddresses', 'addresses')
  475.     private_keys = config.get('HoldingAddresses', 'private_keys')
  476.     if len(addresses) > 5:
  477.         config.set('HoldingAddresses', 'addresses', '+'.join([addresses,addr]))
  478.         config.set('HoldingAddresses', 'private_keys', '+'.join([private_keys,pkey]))
  479.     else:
  480.         config.set('HoldingAddresses', 'addresses', addr)
  481.         config.set('HoldingAddresses', 'private_keys', pkey)
  482.     config.write(open(config_file,'w'))
  483.     return "Address added: "+addr
  484.  
  485. def update_tracked_coins(name):
  486.     # Update the list of owners of a tracked coin
  487.     # and write to the config file
  488.     root_tx = config.get(name, "root_tx")
  489.     current_holders = get_current_holders(root_tx)
  490.     holding_addresses = ""
  491.     holding_amounts = ""
  492.     holding_txids = ""
  493.     total = 0.0
  494.     for h in current_holders:
  495.         holding_addresses += "+" + h[0]
  496.         holding_amounts += "+" + str(h[1])
  497.         holding_txids += "+" + h[2]
  498.     config.set(name, "holders", holding_addresses[1:])
  499.     config.set(name, "amounts", holding_amounts[1:])
  500.     config.set(name, "txid", holding_txids[1:])
  501.     config.write(open(config_file,'w'))
  502.  
  503. def start_tracking_coins(name,txid):
  504.     # Give a name of a tracked coin, together with a
  505.     # root output that will be used to track it.
  506.     # Write this to the config file, and update the
  507.     # list of owners.
  508.     if name in config.sections():
  509.         return name+" already exists."
  510.     config.add_section(name)
  511.     config.set(name, "root_tx", txid)
  512.     config.set(name, "holders", "")
  513.     config.set(name, "amounts", "")
  514.     config.set(name, "txid", "")
  515.     config.write(open(config_file,'w'))
  516.     update_tracked_coins(name)
  517.  
  518. def show_holders(name):
  519.     holders = config.get(name, "holders").split("+")
  520.     amounts = config.get(name, "amounts").split("+")
  521.     txids = config.get(name,"txid").split("+")
  522.     total = 0.0
  523.     print "*** %s ***" % (name,)
  524.     for h in zip(holders,amounts,txids):
  525.         print h[0],h[1],h[2]
  526.         total += float(h[1])
  527.     print "** Total %s: %f **" % (name,total)
  528.  
  529. def show_my_holdings():
  530.     sections = config.sections()
  531.     my_holding_addresses = config.get('HoldingAddresses', 'addresses').split("+")
  532.     for s in sections:
  533.         if s in reserved_sections: continue
  534.         holders = config.get(s, "holders").split("+")
  535.         amounts = config.get(s, "amounts").split("+")
  536.         txids = config.get(s, "txid").split("+")
  537.         for h in holders:
  538.             if h in my_holding_addresses:
  539.                 total_dividends = 0.0
  540.                 for naf in get_non_asset_funds(h):
  541.                     total_dividends += float(naf['value'])/1e8
  542.                 print s,amounts[holders.index(h)],"( div:",total_dividends,")",h,txids[holders.index(h)]
  543.  
  544. def show_my_holding_addresses():
  545.     my_holding_addresses = config.get('HoldingAddresses', 'addresses').split("+")
  546.     for a in my_holding_addresses:
  547.         print a
  548.  
  549. def transfer_asset(sender, receivers):
  550.     address,txid,n = sender.split(":")
  551.     tx_input = [(txid, int(n), address)]
  552.     tx_outputs = []
  553.     for l in receivers.split(","):
  554.         address,amount = l.split(":")
  555.         tx_outputs.append((address,int(float(amount)*1e8)))
  556.     print maketx(tx_input, tx_outputs)
  557.  
  558. def pay_to_shareholders(name, wallet_acct, total_payment_amount):
  559.     holders = config.get(name, "holders").split("+")
  560.     amounts = config.get(name, "amounts").split("+")
  561.     total = 0.0
  562.     for a in amounts:
  563.         total += float(a)
  564.     payouts = {}
  565.     for h,a in zip(holders,amounts):
  566.         d = total_payment_amount*float(a)/total
  567.         payouts[h] = d
  568.     sp.sendmany(wallet_acct,payouts)
  569.     print "Payouts made:"
  570.     for k in payouts.keys():
  571.         print k,":",payouts[k]
  572.  
  573. def transfer_others(transfer_other_from,transfer_other_to):
  574.     naf = get_non_asset_funds(transfer_other_from)
  575.     fee = int(1e8*0.005)
  576.     total_value = 0
  577.     inputs = []
  578.     for u in naf:
  579.         total_value += u['value']
  580.         i = (u['tx_hash'], u['tx_output_n'], transfer_other_from)
  581.         inputs.append(i)
  582.     outputs = [(transfer_other_to, total_value-fee)]
  583.     maketx(inputs,outputs,send=False)
  584.     print "Paid",float(total_value-fee)/1e8,"to",transfer_other_to
  585.  
  586.  
  587. if __name__ == '__main__':
  588.     # Process command-line options
  589.     parser = OptionParser()
  590.     parser.add_option('-p', '--paint', help='Paint coins for tracking', dest='paint_txid', action='store')
  591.     parser.add_option('-n', '--new-address', help='Create new holding address for colored coins', dest='gen_address', default=False, action='store_true')
  592.     parser.add_option('-l', '--list-colors', help='List of names of painted coins being tracked', dest='list_colors', default=False, action='store_true')
  593.     parser.add_option('-u', '--update-ownership', help='Update ownership info for painted coins', dest='update_name', action='store')
  594.     parser.add_option('-o', '--owners', help='Show owners of painted coins', dest="holders_name", action="store")
  595.     parser.add_option('-m', '--my-holdings', help='Show holdings at my addresses', dest="show_holdings", action="store_true")
  596.     parser.add_option('-a', '--holding-addresses', help='Show my holding addresses', dest="show_addresses", action="store_true")
  597.     parser.add_option('-f', '--transfer-from', help='Asset to transfer to another address. address:txid:n', dest='transfer_from', action="store")
  598.     parser.add_option('-t', '--transfer-to', help='Address to transfer asset to. address:amount,...', dest='transfer_to', action="store")
  599.     parser.add_option('-d', '--pay-holders', help="Pay from your bitcoind wallet to asset holders: <asset_name>:<wallet_acctname>:<payout_amount>", dest="pay_to_holders", action="store")
  600.     parser.add_option('-x', '--transfer-other-from', help='Transfer bitcoins UNRELATED to the tracked address/coins away from this address', dest="transfer_other_from", action="store")
  601.     parser.add_option('-y', '--transfer-other-to', help='Transfer bitcoins UNRELATED to the tracked address/coins to this address', dest="transfer_other_to", action="store")
  602.     opts, args = parser.parse_args()
  603.    
  604.     if opts.gen_address:
  605.         print generate_holding_address()
  606.     if opts.paint_txid:
  607.         name,txid,n = opts.paint_txid.split(":")
  608.         start_tracking_coins(name,txid+":"+n)
  609.     if opts.holders_name:
  610.         show_holders(opts.holders_name)
  611.     if opts.update_name:
  612.         update_tracked_coins(opts.update_name)
  613.     if opts.show_holdings:
  614.         show_my_holdings()
  615.     if opts.show_addresses:
  616.         show_my_holding_addresses()
  617.     if opts.pay_to_holders:
  618.         asset_name, wallet_acct_name, amount = opts.pay_to_holders.split(":")
  619.         pay_to_shareholders(asset_name, wallet_acct_name, float(amount))
  620.     if opts.transfer_from or opts.transfer_to:
  621.         if opts.transfer_to and opts.transfer_from:
  622.             transfer_asset(opts.transfer_from, opts.transfer_to)
  623.         else:
  624.             print "Make sure you give both a source and destination"
  625.     if opts.transfer_other_from or opts.transfer_other_to:
  626.         if opts.transfer_other_to and opts.transfer_other_from:
  627.             transfer_others(opts.transfer_other_from,opts.transfer_other_to)
  628.         else:
  629.             print "Make sure you give both a source and destination"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement