Skip to content

Instantly share code, notes, and snippets.

@fbsobreira
Created June 22, 2019 00:42
Show Gist options
  • Save fbsobreira/259eed793cffe4551854250884049f03 to your computer and use it in GitHub Desktop.
Save fbsobreira/259eed793cffe4551854250884049f03 to your computer and use it in GitHub Desktop.
Test case TronWatchMarket Ledger
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
sys.path.append("./examples/proto")
import logging
import time
from ledgerblue.comm import getDongle
from base import parse_bip32_path
from pprint import pprint
'''
Tron Protobuf
'''
from api import api_pb2 as api
from core import Contract_pb2 as contract
from api.api_pb2_grpc import WalletStub
from core import Tron_pb2 as tron
from google.protobuf.any_pb2 import Any
import grpc
import base58
import binascii
import struct
# Start Channel and WalletStub
channel = grpc.insecure_channel("grpc.trongrid.io:50051")
stub = WalletStub(channel)
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger()
print('''
TronWatch.Market
''')
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
def apduMessage(INS, P1, P2, PATH, MESSAGE):
hexString = ""
if PATH:
hexString = "E0{:02x}{:02x}{:02x}{:02x}{:02x}{}".format(INS,P1,P2,(len(PATH)+len(MESSAGE))//2+1,len(PATH)//4//2,PATH+MESSAGE)
else:
hexString = "E0{:02x}{:02x}{:02x}{:02x}{}".format(INS,P1,P2,len(MESSAGE)//2,MESSAGE)
print(hexString)
return bytearray.fromhex(hexString)
def ledgerSign(PATH, tx, tokenSignature=[]):
raw_tx = tx.raw_data.SerializeToString().hex()
# Sign in chunks
chunkList = list(chunks(raw_tx,400))
if len(tokenSignature)>0:
chunkList.extend(tokenSignature)
# P1 = P1_FIRST = 0x00
if len(chunkList)>1:
result = dongle.exchange(apduMessage(0x04,0x00,0x00, PATH, chunkList[0]))
else:
result = dongle.exchange(apduMessage(0x04,0x10,0x00, PATH, chunkList[0]))
for i in range(1,len(chunkList)-1-len(tokenSignature)):
# P1 = P1_MODE = 0x80
result = dongle.exchange(apduMessage(0x04,0x80,0x00, None, chunkList[i]))
for i in range(0,len(tokenSignature)-1):
result = dongle.exchange(apduMessage(0x04,0xA0 | (0x00+i), 0x00, None, tokenSignature[i]))
# P1 = P1_LAST = 0x90
if len(chunkList)>1:
if len(tokenSignature)>0:
result = dongle.exchange(apduMessage(0x04,0xA0 | 0x08 | (0x00+len(tokenSignature)-1),0x00, None, chunkList[len(chunkList)-1]))
else:
result = dongle.exchange(apduMessage(0x04,0x90,0x00, None, chunkList[len(chunkList)-1]))
return raw_tx, result
def address_hex(address):
return base58.b58decode_check(address).hex().upper()
# Start Ledger
dongle = getDongle(True)
exchangeAddress = {}
exchangeAddress['address'] = 'TTg3AAJBYsDNjx5Moc5EPNsgJSa4anJQ3M'
exchangeAddress['addressHex'] = address_hex(exchangeAddress['address'])
testAccount = {}
donglePath = parse_bip32_path("44'/195'/0'/0/0")
result = dongle.exchange(apduMessage(0x02,0x00,0x00,donglePath, ""))
size=result[0]
if size == 65 :
size=result[size+1]
if size == 34 :
testAccount['address'] = result[67:67+size].decode()
testAccount['addressHex'] = address_hex(testAccount['address'])
else:
logger.error('Error... Address Size: {:d}'.format(size))
else:
logger.error('Error... Public Key Size: {:d}'.format(size))
def toFixed(value):
if isinstance(value, str):
return value.zfill(64)
elif value is None:
return "0".zfill(64)
return '{:064x}'.format(value)
def tokenToUint(tokenType, tokenAddress):
if tokenType == "TRX":
return "0".zfill(64)
elif tokenType == "TRC10":
if isinstance(tokenAddress, str) or tokenAddress<=1000000:
raise ValueError('Invalid TRC10 tokenId (param tokenAddress): ')
return '{:064x}'.format(tokenAddress)
elif tokenType == "TRC20":
address = address_hex(tokenAddress)
return address[2:].zfill(64)
else:
raise ValueError('Invalid tokenType: Expecting TRX, TRC10, or TRC20')
def validateMakerOffer(makerOffer, skipDateCheck=None):
if makerOffer is None: raise ValueError("Missing param makerOffer")
if makerOffer['quote'] is None:
raise ValueError("Missing param makerOffer.quote, use null or 0 for TRX")
if makerOffer['maker'] is None:
raise ValueError("Missing param makerOffer.maker")
if makerOffer['amount'] is None:
raise ValueError("Missing param makerOffer.amount")
if makerOffer['affiliate'] is None:
raise ValueError("Missing param makerOffer.affiliate, use null for no affiliate")
if makerOffer['rateNumerator'] is None:
raise ValueError("Missing param makerOffer.rateNumerator")
if makerOffer['rateDenominator'] is None:
raise ValueError("Missing param makerOffer.rateDenominator")
if makerOffer['creationDate'] is None:
raise ValueError("Missing param makerOffer.creationDate")
if skipDateCheck is None and (makerOffer['creationDate'] > (1000000 + int(time.time()) ) ):
raise ValueError("makerOffer.creationDate out of range, use `Math.round(Date.now() / 1000)`")
if makerOffer['expirationDate'] is None:
raise ValueError("Missing param makerOffer.expirationDate")
if makerOffer['isBuy'] is None:
raise ValueError("Missing param makerOffer.isBuy")
if makerOffer['autoWithdraw'] is None:
raise ValueError("Missing param makerOffer.autoWithdraw")
def parseMakerUints(makerOffer):
uints = []
uints.append(tokenToUint(makerOffer['baseType'], makerOffer['base']))
uints.append(tokenToUint(makerOffer['quoteType'], makerOffer['quote']))
uints.append(toFixed(makerOffer['amount']))
uints.append(toFixed(makerOffer['rateNumerator']))
uints.append(toFixed(makerOffer['rateDenominator']))
uints.append(toFixed(makerOffer['creationDate']))
uints.append(toFixed(makerOffer['expirationDate']))
return uints
def getMakerHash(makerOffer):
validateMakerOffer(makerOffer)
uints = parseMakerUints(makerOffer)
'''ABI
[{"constant":true,"inputs":[{"name":"_maker","type":"address"},{"name":"_isMakerBuy","type":"bool"},{"name":"_makerAffiliate","type":"address"},{"name":"_uints","type":"uint256[7]"},{"name":"_autoWithdraw","type":"bool"}],"name":"getMakerHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}]
'''
data = '{:08x}{}{:064x}{}{}{}{}{}{}{}{}{:064x}'.format(
0xe51fabf7,
address_hex(makerOffer['maker'])[2:].zfill(64),
makerOffer['isBuy'],
address_hex(makerOffer['affiliate'])[2:].zfill(64),
uints[0],
uints[1],
uints[2],
uints[3],
uints[4],
uints[5],
uints[6],
makerOffer['autoWithdraw'],
)
tx = stub.TriggerContract(contract.TriggerSmartContract(
owner_address=bytes.fromhex(testAccount['addressHex']),
contract_address=bytes.fromhex(exchangeAddress['addressHex']),
data=bytes.fromhex(data)
))
tx.transaction.raw_data.fee_limit = 1000000000
pprint(tx)
return tx.constant_result[0].hex()
hash = getMakerHash(
{
"orderID": 14302,
"amountRemaining": 1,
"amountFilled": 0,
"affiliate": "TSWkgawBp6YmZmEJZSb5br44hZjU7H1BxF",
"signature": "0x1b,0x9a14e370b9e0aab7ca822d416f2c29fc369bbeb66b97bf44b848070491582199,0x0c84cd090ff7b84bb2acbd606ce4b08fcdfcf2707d2d2db6823e4659ff9465f1",
"hash": "0xc1d8afe8f63ad4cf873518bab3b91512e764af6e91ebf46d85ef3ab37f6ed4a7",
"maker": testAccount['address'],
"baseType": "TRC10",
"base": 1000933,
"quoteType": "TRX",
"quote": "TRX",
"amount": 1,
"rateNumerator": 276100,
"rateDenominator": 1,
"creationDate": 1558881044,
"expirationDate": 9008758135785,
"price": 276100.000000000000000000,
"side": "BUY",
"isBuy": True,
"status": "OPEN",
"autoWithdraw": 1
}
)
message = bytes.fromhex(hash)
# Magic define
SIGN_MAGIC = b'\x19TRON Signed Message:\n'
encodedTx = struct.pack(">I", len(message))TronWatchMarket Ledger
encodedTx += messageTronWatchMarket LedgerTronWatchMarket Ledger
signedMessage = SIGN_MAGIC + str(len(message)).encode() + message
result = dongle.exchange(apduMessage(0x08,0x00,0x00, donglePath, encodedTx.hex()))
logger.debug('- Hash: {}'.format(hash))
logger.debug('- Signed Message: {}'.format(signedMessage))
logger.debug('- Signature: {}'.format(result.hex()))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment