@alt, what we are doing (kind of hacky but we have plan-b)
We are using the slate field of the deposit operation (since we are moving BTA only not BTS and BTA balances votes dont count ) to store the derivation index of an HDKey that was used to create the shared secret.
Plan-B would be to use the op_data operation in case we have problems in the future (node rejecting an operation of BTA with slate != 0).
@xeroc and @alt, we should combine our efforts on the python library, i think we all three have write the same code.
(order book processing [bids+asks+shorts], address/pubkey creation/decoding, etc).
Here is some ugly code for memo creation/decoding.
The bitcoin library is
https://github.com/vbuterin/pybitcointools#!/usr/bin/python
import sys,os
sys.path[0:0] = [os.path.join(os.path.dirname(os.path.abspath(__file__)), '../lib')]
from hashlib import sha512
import bitcoin as b
from helper import *
if len(sys.argv) < 4:
print 'memo_enc.py message from_pub to_pub'
sys.exit(0)
_message = sys.argv[1]
_from_pub = sys.argv[2]
_to_pub = sys.argv[3]
def compute_encrypted_memo(message, from_pub, to_pub):
print 'Compute encrypted memo'
print message
print from_pub
print to_pub
print hack_info
# Compute shared secret
def compute_shared_secret(pub, priv):
Xk = b.encode_pubkey(b.multiply(pub, priv), 'hex_compressed')[2:]
return sha512(Xk.decode('hex')).digest()
#TODO: remove
priv_key = b.random_key()
ss = compute_shared_secret(decode_pub(to_pub), priv_key)
fpub = decode_pub(from_pub).decode('hex')
if len(message) < 19:
message = message + chr(0)*(19-len(message))
message = message[:51]
memo_data = fpub + chr(0)*8 + message[:19] + '\0'
if len(message) > 19:
memo_data += message[19:19+32]
print 'voy aca'
length = 16 - (len(memo_data) % 16)
memo_data += chr(length)*length
from Crypto.Cipher import AES
obj = AES.new(ss[0:32], AES.MODE_CBC, ss[32:48])
cipher = obj.encrypt(memo_data)
res = {
'shared_secret' : ss.encode('hex'),
'memo_data' : memo_data.encode('hex'),
'one_time_key' : encode_pub(b.encode_pubkey(b.privtopub(priv_key),'hex_compressed')),
'encrypted_memo_data' : cipher.encode('hex')
}
return res
#res = compute_encrypted_memo(_message, _from_pub, _to_pub)
#import simplejson as json
#print json.dumps(res)
#!/usr/bin/python
import sys,os
sys.path[0:0] = [os.path.join(os.path.dirname(os.path.abspath(__file__)), '../lib')]
from hashlib import sha512
import bitcoin as b
from helper import *
if len(sys.argv) < 4:
print 'memo_dec.py one_time_key encripted_memo_data priv_key'
sys.exit(0)
one_time_key = sys.argv[1]
encripted_memo_data = sys.argv[2]
priv_key = sys.argv[3]
# Compute shared secret
def compute_shared_secret(pub, priv):
Xk = b.encode_pubkey(b.multiply(pub, priv), 'hex_compressed')[2:]
return sha512(Xk.decode('hex')).digest()
ss = compute_shared_secret(decode_pub(one_time_key), priv_key)
from Crypto.Cipher import AES
obj = AES.new(ss[0:32], AES.MODE_CBC, ss[32:48])
plain = obj.decrypt(encripted_memo_data.decode('hex'))
inx=0
def extract(_len):
global inx
tmp = plain[inx:inx+_len]
inx += _len
return tmp
_from = plain[inx:inx+33]; inx+=33
_from_sig = plain[inx:inx+8]; inx+=8
_message = plain[inx:inx+19]; inx+=19
_type = plain[inx:inx+1]; inx+=1
if len(plain) > 33+8+19+1:
_message = _message + plain[inx:inx+32]
import string
_message = filter(lambda x: x in string.printable, _message)
res = {
'shared_secret' : ss.encode('hex'),
'memo_data' : plain.encode('hex'),
'from' : encode_pub(_from.encode('hex')),
'from_sig' : _from_sig.encode('hex'),
'message' : _message,
'type' : _type.encode('hex')
}
import simplejson as json
print json.dumps(res)
Some helper code
import bitcoin as b
import base64
from config import *
import binascii
PROD_PREFIX = 'BTS'
DEV_PREFIX = 'DVS'
prefix = DEV_PREFIX if is_test() else PROD_PREFIX
def is_valid_address(addy):
try:
address_to_hash(addy)
return True
except:
return False
def is_valid_pubkey(pubkey):
try:
decode_pub(pubkey)
return True
except:
return False
def address_to_hash(addy):
assert(addy[0:3] == prefix), 'Invalid prefix'
data = b.changebase(addy[3:], 58, 256)
assert(len(data) == 24), 'Invalid length'
hval = data[:-4]
assert(data[-4:] == b.ripemd.new(hval).digest()[:4]), 'Invalid checksum'
return hval.encode('hex')
def pub_to_address(pub):
r = b.ripemd.new(b.hashlib.sha512(pub.decode('hex')).digest()).digest()
c = b.ripemd.new(r).digest()
return prefix+b.changebase(r+c[0:4], 256, 58)
def decode_pub(epub):
print 'EPUB %s'%epub
assert(epub[0:3] == prefix), 'Invalid prefix'
data = b.changebase(epub[3:], 58, 256)
assert(len(data) == 37), 'Invalid length'
hval = data[:-4]
assert(data[-4:] == b.ripemd.new(hval).digest()[:4]), 'Invalid checksum'
return hval.encode('hex')
def encode_pub(hexpub):
h = binascii.unhexlify(hexpub)
return prefix + b.changebase(h + b.ripemd.new(h).digest()[0:4], 256, 58)
def recover_pubkey(msghash, signature):
sig = base64.b64encode(signature.decode('hex'))
return encode_pub(b.encode_pubkey(b.ecdsa_raw_recover(msghash,b.decode_sig(sig)),'hex_compressed'))