Skip to content

Instantly share code, notes, and snippets.

@Aareon
Last active July 16, 2021 02:53
Show Gist options
  • Save Aareon/2e7ede86d01812ea4c6c21eb130713cf to your computer and use it in GitHub Desktop.
Save Aareon/2e7ede86d01812ea4c6c21eb130713cf to your computer and use it in GitHub Desktop.
Simple Python utility lib for interacting with TNBC
import hashlib
b = 256
q = 2**255 - 19
l = 2**252 + 27742317777372353535851937790883648493
def H(m):
return hashlib.sha512(m).digest()
def expmod(b,e,m):
if e == 0: return 1
t = expmod(b,e/2,m)**2 % m
if e & 1: t = (t*b) % m
return t
def inv(x):
return expmod(x,q-2,q)
d = -121665 * inv(121666)
I = expmod(2,(q-1)/4,q)
def xrecover(y):
xx = (y*y-1) * inv(d*y*y+1)
x = expmod(xx,(q+3)/8,q)
if (x*x - xx) % q != 0: x = (x*I) % q
if x % 2 != 0: x = q-x
return x
By = 4 * inv(5)
Bx = xrecover(By)
B = [Bx % q,By % q]
def edwards(P,Q):
x1 = P[0]
y1 = P[1]
x2 = Q[0]
y2 = Q[1]
x3 = (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2)
y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2)
return [x3 % q,y3 % q]
def scalarmult(P,e):
if e == 0: return [0,1]
Q = scalarmult(P,e/2)
Q = edwards(Q,Q)
if e & 1: Q = edwards(Q,P)
return Q
def encodeint(y):
bits = [(y >> i) & 1 for i in range(b)]
return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)])
def encodepoint(P):
x = P[0]
y = P[1]
bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)])
def bit(h,i):
return (ord(h[i/8]) >> (i%8)) & 1
def publickey(sk):
h = H(sk)
a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
A = scalarmult(B,a)
return encodepoint(A)
def Hint(m):
h = H(m)
return sum(2**i * bit(h,i) for i in range(2*b))
def signature(m,sk,pk):
h = H(sk)
a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
r = Hint(''.join([h[i] for i in range(b/8,b/4)]) + m)
R = scalarmult(B,r)
S = (r + Hint(encodepoint(R) + pk + m) * a) % l
return encodepoint(R) + encodeint(S)
def isoncurve(P):
x = P[0]
y = P[1]
return (-x*x + y*y - 1 - d*x*x*y*y) % q == 0
def decodeint(s):
return sum(2**i * bit(s,i) for i in range(0,b))
def decodepoint(s):
y = sum(2**i * bit(s,i) for i in range(0,b-1))
x = xrecover(y)
if x & 1 != bit(s,b-1): x = q-x
P = [x,y]
if not isoncurve(P): raise Exception("decoding point that is not on curve")
return P
def checkvalid(s,m,pk):
if len(s) != b/4: raise Exception("signature length is wrong")
if len(pk) != b/8: raise Exception("public-key length is wrong")
R = decodepoint(s[0:b/8])
A = decodepoint(pk)
S = decodeint(s[b/8:b/4])
h = Hint(encodepoint(R) + pk + m)
if scalarmult(B,S) != edwards(R,scalarmult(A,h)):
raise Exception("signature does not pass verification")
#!/usr/bin/env python3
import json
from operator import itemgetter
import requests
#from nacl.encoding import HexEncoder
#signing_key = nacl.signing.SigningKey(str.encode(os.environ.get('SIGNING_KEY')), encoder=nacl.encoding.HexEncoder)
#BANK_IP = IP_ADDRESS_OF_THE_BANK
BANKS_LIST_URL = 'https://raw.githubusercontent.com/tnbCrow/MVP/main/trusted_banks.txt'
def generate_block(balance_lock, transactions, signing_key):
''' Generate a block signed with signing key '''
account_number = signing_key.verify_key.encode(encoder=HexEncoder).decode('utf-8')
message = {
'balance_key': balance_lock,
'txs': sorted(transactions, key=itemgetter('recipient'))
}
signature = signing_key.sign(json.dumps(message, separators=(',', ':'), sort_keys=True).encode('utf-8')).signature.hex()
block = {
'account_number': account_number,
'message': message,
'signature': signature
}
return json.dumps(block)
def get_bank_config(ssl=True, bank_ip=BANK_IP):
return requests.get(f'http{"s" if ssl else ""}://{bank_ip}/config?format=json').json()
def get_acc_balance_lock(bank_config, account_num):
return requests.get(f"{bank_config['primary_validator']['protocol']}://{bank_config['primary_validator']['ip_address']}:{bank_config['primary_validator']['port'] or 0}/accounts/{payment_account_number}/balance_lock?format=json").json()['balance_lock']
def create_tx(bank_config, account_num, recipient_acc_num, amount, memo=''):
# Create a transaction, including fee payments to bank and PV
return [
{
'amount': amount,
'memo': memo,
'recipient': recipient_acc_num,
},
{
'amount': int(bank_config['default_transaction_fee']),
'fee': 'BANK',
'recipient': bank_config['account_number'],
},
{
'amount': int(bank_config['primary_validator']['default_transaction_fee']),
'fee': 'PRIMARY_VALIDATOR',
'recipient': bank_config['primary_validator']['account_number'],
}
]
def send_tx(bank_config, balance_lock, txs, signing_key, bank_ip=BANK_IP):
# Generate a block, sign transactions, and send it
data = generate_block(balance_lock, txs, signing_key)
# The headers while sending the block to bank
headers = {
'Connection': 'keep-alive',
'Accept': 'application/json, text/plain, */*',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) TNBAccountManager/1.0.0-alpha.43 Chrome/83.0.4103.122 Electron/9.4.0 Safari/537.36',
'Content-Type': 'application/json',
}
r = requests.request("POST", f'http://{bank_ip}/blocks', headers=headers, data=data) # Send the post request to bank
if r.status_code == 200:
print("Transaction Completed!!")
return True
else:
print('Failed to send transaction: {r.status_code}')
return False
if __name__ == '__main__':
# get a bank ip
try:
r = requests.get(BANKS_LIST_URL)
banks_list = r.text.splitlines()
except:
raise
bank_ip = banks_list[0]
bank_config = get_bank_config() # get the config of the bank
# Get the balance lock of the sender account number
balance_lock = get_acc_balance_lock(bank_config, account_num)
# Create a transaction, including fee payments to bank and PV
txs = create_tx(bank_config, account_num, recipient_acc_num, amount, memo='')
# Generate a block, sign transactions, and send it
success = send_tx(bank_config, balance_lock, txs, signing_key)
@Aareon
Copy link
Author

Aareon commented Jul 15, 2021

My use case keeps me from using PyNaCl so I’ll have to implement the signing algo in a sep file in this gist for signing in pure python.

@Aareon
Copy link
Author

Aareon commented Jul 15, 2021

Thanks to hussu#9966 for his help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment