diff --git a/.gitignore b/.gitignore index 64da271..d9280d1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,6 @@ bin debug dep obj -src_common/glyphs.c -src_common/glyphs.h +src/glyphs.c +src/glyphs.h diff --git a/ethBase.py b/ethBase.py deleted file mode 100644 index 240a4c9..0000000 --- a/ethBase.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -""" -******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -******************************************************************************** -""" - -from rlp.sedes import big_endian_int, binary, Binary -from rlp import Serializable - -try: - from Crypto.Hash import keccak - sha3_256 = lambda x: keccak.new(digest_bits=256, data=x).digest() -except: - import sha3 as _sha3 - sha3_256 = lambda x: _sha3.sha3_256(x).digest() -address = Binary.fixed_length(20, allow_empty=True) - -def sha3(seed): - return sha3_256(str(seed)) - -class Transaction(Serializable): - fields = [ - ('nonce', big_endian_int), - ('gasprice', big_endian_int), - ('startgas', big_endian_int), - ('to', address), - ('value', big_endian_int), - ('data', binary), - ('v', big_endian_int), - ('r', big_endian_int), - ('s', big_endian_int), - ] - - def __init__(self, nonce, gasprice, startgas, to, value, data, v=0, r=0, s=0): - super(Transaction, self).__init__(nonce, gasprice, startgas, to, value, data, v, r, s) - -UnsignedTransaction = Transaction.exclude(['v', 'r', 's']) diff --git a/examples/ethBase.py b/examples/ethBase.py new file mode 100755 index 0000000..95fb143 --- /dev/null +++ b/examples/ethBase.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +""" +******************************************************************************* +* Ledger Ethereum App +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +******************************************************************************** +""" + +from rlp.sedes import big_endian_int, binary, Binary +from rlp import Serializable + +try: + from Crypto.Hash import keccak + + def sha3_256(x): return keccak.new(digest_bits=256, data=x).digest() +except: + import sha3 as _sha3 + + def sha3_256(x): return _sha3.sha3_256(x).digest() +address = Binary.fixed_length(20, allow_empty=True) + + +def sha3(seed): + return sha3_256(str(seed)) + + +class Transaction(Serializable): + fields = [ + ('nonce', big_endian_int), + ('gasprice', big_endian_int), + ('startgas', big_endian_int), + ('to', address), + ('value', big_endian_int), + ('data', binary), + ('v', big_endian_int), + ('r', big_endian_int), + ('s', big_endian_int), + ] + + def __init__(self, nonce, gasprice, startgas, to, value, data, v=0, r=0, s=0): + super(Transaction, self).__init__( + nonce, gasprice, startgas, to, value, data, v, r, s) + +class UnsignedTransaction(Serializable): + fields = [ + ('nonce', big_endian_int), + ('gasprice', big_endian_int), + ('startgas', big_endian_int), + ('to', address), + ('value', big_endian_int), + ('data', binary), + ] + +def unsigned_tx_from_tx(tx): + return UnsignedTransaction( + nonce=tx.nonce, + gasprice=tx.gasprice, + startgas=tx.startgas, + to=tx.to, + value=tx.value, + data=tx.data, + ) \ No newline at end of file diff --git a/getPublicKey.py b/examples/getPublicKey.py old mode 100644 new mode 100755 similarity index 58% rename from getPublicKey.py rename to examples/getPublicKey.py index 01c6351..f602b1d --- a/getPublicKey.py +++ b/examples/getPublicKey.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ ******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,38 +17,44 @@ * limitations under the License. ******************************************************************************** """ +from __future__ import print_function + from ledgerblue.comm import getDongle from ledgerblue.commException import CommException import argparse import struct +import binascii + def parse_bip32_path(path): - if len(path) == 0: - return "" - result = "" - elements = path.split('/') - for pathElement in elements: - element = pathElement.split('\'') - if len(element) == 1: - result = result + struct.pack(">I", int(element[0])) - else: - result = result + struct.pack(">I", 0x80000000 | int(element[0])) - return result + if len(path) == 0: + return b"" + result = b"" + elements = path.split('/') + for pathElement in elements: + element = pathElement.split('\'') + if len(element) == 1: + result = result + struct.pack(">I", int(element[0])) + else: + result = result + struct.pack(">I", 0x80000000 | int(element[0])) + return result + parser = argparse.ArgumentParser() parser.add_argument('--path', help="BIP 32 path to retrieve") args = parser.parse_args() if args.path == None: - args.path = "44'/60'/0'/0/0" + args.path = "44'/60'/0'/0/0" donglePath = parse_bip32_path(args.path) -apdu = "e0020100".decode('hex') + chr(len(donglePath) + 1) + chr(len(donglePath) / 4) + donglePath +apdu = bytearray.fromhex("e0020100") + chr(len(donglePath) + 1).encode() + \ + chr(len(donglePath) // 4).encode() + donglePath dongle = getDongle(True) result = dongle.exchange(bytes(apdu)) offset = 1 + result[0] -address = result[offset + 1 : offset + 1 + result[offset]] +address = result[offset + 1: offset + 1 + result[offset]] -print "Public key " + str(result[1 : 1 + result[0]]).encode('hex') -print "Address 0x" + str(address) +print("Public key", binascii.hexlify(result[1: 1 + result[0]]).decode()) +print("Address 0x", address.decode(), sep='') diff --git a/setSelfAddress.py b/examples/setSelfAddress.py old mode 100644 new mode 100755 similarity index 66% rename from setSelfAddress.py rename to examples/setSelfAddress.py index 0eca2c6..3623750 --- a/setSelfAddress.py +++ b/examples/setSelfAddress.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ ******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,28 +22,31 @@ from ledgerblue.commException import CommException import argparse import struct + def parse_bip32_path(path): - if len(path) == 0: - return "" - result = "" - elements = path.split('/') - for pathElement in elements: - element = pathElement.split('\'') - if len(element) == 1: - result = result + struct.pack(">I", int(element[0])) - else: - result = result + struct.pack(">I", 0x80000000 | int(element[0])) - return result + if len(path) == 0: + return b"" + result = b"" + elements = path.split('/') + for pathElement in elements: + element = pathElement.split('\'') + if len(element) == 1: + result = result + struct.pack(">I", int(element[0])) + else: + result = result + struct.pack(">I", 0x80000000 | int(element[0])) + return result + parser = argparse.ArgumentParser() parser.add_argument('--path', help="BIP 32 path to retrieve") args = parser.parse_args() if args.path == None: - args.path = "44'/60'/0'/0/0" + args.path = "44'/60'/0'/0/0" donglePath = parse_bip32_path(args.path) -apdu = "e0060000".decode('hex') + chr(len(donglePath) + 1) + chr(len(donglePath) / 4) + donglePath +apdu = bytearray.fromhex("e0060000") + chr(len(donglePath) + 1).encode() + \ + chr(len(donglePath) // 4).encode() + donglePath dongle = getDongle(True) dongle.exchange(bytes(apdu)) diff --git a/examples/signTx.py b/examples/signTx.py new file mode 100755 index 0000000..f253661 --- /dev/null +++ b/examples/signTx.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +""" +******************************************************************************* +* Ledger Ethereum App +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +******************************************************************************** +""" +from __future__ import print_function + +from ledgerblue.comm import getDongle +from ledgerblue.commException import CommException +import argparse +import struct +import binascii +from ethBase import Transaction, UnsignedTransaction, unsigned_tx_from_tx +from rlp import encode + +try: + from rlp.utils import decode_hex, encode_hex, str_to_bytes +except: + #Python3 hack import for pyethereum + from ethereum.utils import decode_hex, encode_hex, str_to_bytes + +def parse_bip32_path(path): + if len(path) == 0: + return b"" + result = b"" + elements = path.split('/') + for pathElement in elements: + element = pathElement.split('\'') + if len(element) == 1: + result = result + struct.pack(">I", int(element[0])) + else: + result = result + struct.pack(">I", 0x80000000 | int(element[0])) + return result + + +parser = argparse.ArgumentParser() +parser.add_argument('--nonce', help="Nonce associated to the account", type=int, required=True) +parser.add_argument('--gasprice', help="Network gas price", type=int, required=True) +parser.add_argument('--startgas', help="startgas", default='21000', type=int) +parser.add_argument('--amount', help="Amount to send in ether", type=int, required=True) +parser.add_argument('--to', help="Destination address", type=str, required=True) +parser.add_argument('--path', help="BIP 32 path to sign with") +parser.add_argument('--data', help="Data to add, hex encoded") +args = parser.parse_args() + +if args.path == None: + args.path = "44'/60'/0'/0/0" + +if args.data == None: + args.data = b"" +else: + args.data = decode_hex(args.data[2:]) + +amount = args.amount * 10**18 + +tx = Transaction( + nonce=int(args.nonce), + gasprice=int(args.gasprice), + startgas=int(args.startgas), + to=decode_hex(args.to[2:]), + value=int(amount), + data=args.data, +) + +encodedTx = encode(unsigned_tx_from_tx(tx), UnsignedTransaction) + +donglePath = parse_bip32_path(args.path) +apdu = bytearray.fromhex("e0040000") +apdu.append(len(donglePath) + 1 + len(encodedTx)) +apdu.append(len(donglePath) // 4) +apdu += donglePath + encodedTx + +dongle = getDongle(True) +result = dongle.exchange(bytes(apdu)) + +v = result[0] +r = int(binascii.hexlify(result[1:1 + 32]), 16) +s = int(binascii.hexlify(result[1 + 32: 1 + 32 + 32]), 16) + +tx = Transaction(tx.nonce, tx.gasprice, tx.startgas, + tx.to, tx.value, tx.data, v, r, s) + +print("Signed transaction", encode_hex(encode(tx))) diff --git a/examples/splitEther.py b/examples/splitEther.py new file mode 100755 index 0000000..1b8f034 --- /dev/null +++ b/examples/splitEther.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +""" +******************************************************************************* +* Ledger Ethereum App +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +******************************************************************************** +""" +from __future__ import print_function + +from ledgerblue.comm import getDongle +from ledgerblue.commException import CommException +import argparse +import struct +import requests +import json +from decimal import Decimal +from rlp import encode +from rlp.utils import decode_hex, encode_hex, str_to_bytes +from ethBase import Transaction, UnsignedTransaction, sha3 + +# https://etherscan.io/address/0x5dc8108fc79018113a58328f5283b376b83922ef#code +SPLIT_CONTRACT_FUNCTION = decode_hex("9c709343") +SPLIT_CONTRACT_ADDRESS = "5dc8108fc79018113a58328f5283b376b83922ef" + + +def rpc_call(http, url, methodDebug): + req = http.get(url) + if req.status_code == 200: + result = json.loads(req.text) + if 'error' in result: + raise Exception("Server error - " + methodDebug + + " - " + result['error']['message']) + return result + else: + raise Exception("Server error - " + methodDebug + + " got status " + req.status) + + +def parse_bip32_path(path): + if len(path) == 0: + return b"" + result = b"" + elements = path.split('/') + for pathElement in elements: + element = pathElement.split('\'') + if len(element) == 1: + result = result + struct.pack(">I", int(element[0])) + else: + result = result + struct.pack(">I", 0x80000000 | int(element[0])) + return result + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--nonce', help="Nonce associated to the account (default : query account)") +parser.add_argument( + '--gasprice', help="Network gas price (default : query network)") +parser.add_argument('--startgas', help="startgas", default='80000') +parser.add_argument('--startgas-delta', + help="difference applied to startgas if gasprice is automatically fetched", default='1000') +parser.add_argument( + '--amount', help="Amount to send in ether (default : query amount, use maximum)") +parser.add_argument( + '--to', help="BIP 32 destination path (default : default ETC path)") +parser.add_argument( + '--split-to-eth', help="Split to the ETH chain (default : spit to ETC chain)", action='store_true') +parser.add_argument( + '--path', help="BIP 32 path to sign with (default : default ETH path)") +parser.add_argument( + '--broadcast', help="Broadcast generated transaction (default : false)", action='store_true') +args = parser.parse_args() + +if args.path == None: + if args.split_to_eth: # sign from ETC + #args.path = "44'/60'/160720'/0'/0" + args.path = "44'/60'/0'/0" + else: # sign from ETH + args.path = "44'/60'/0'/0" + +if args.to == None: + if args.split_to_eth: # target ETH + args.to = "44'/60'/0'/0" + else: # target ETC transitional + args.to = "44'/60'/160720'/0'/0" + +dongle = getDongle(True) + +donglePath = parse_bip32_path(args.to) +apdu = bytearray.fromhex("e0060000") + chr(len(donglePath) + 1).encode() + \ + chr(len(donglePath) // 4).encode() + donglePath +dongle.exchange(bytes(apdu)) + +apdu = bytearray.fromhex("e0020000") + chr(len(donglePath) + 1).encode() + \ + chr(len(donglePath) // 4).encode() + donglePath +result = dongle.exchange(bytes(apdu)) +publicKey = str(result[1: 1 + result[0]]) +encodedPublicKey = sha3(publicKey[1:])[12:] + +if (args.nonce == None) or (args.amount == None): + donglePathFrom = parse_bip32_path(args.path) + apdu = bytearray.fromhex("e0020000") + chr(len(donglePathFrom) + 1).encode() + \ + chr(len(donglePathFrom) // 4).encode() + donglePathFrom + result = dongle.exchange(bytes(apdu)) + publicKeyFrom = str(result[1: 1 + result[0]]) + encodedPublicKeyFrom = sha3(publicKeyFrom[1:])[12:] + + +http = None +if (args.gasprice == None) or (args.nonce == None) or (args.amount == None) or (args.broadcast): + http = requests.session() + +if args.gasprice == None: + print("Fetching gas price") + result = rpc_call( + http, "https://api.etherscan.io/api?module=proxy&action=eth_gasPrice", "gasPrice") + args.gasprice = int(result['result'], 16) + print("Gas price:", str(args.gasprice)) + +if args.nonce == None: + print("Fetching nonce") + result = rpc_call(http, "https://api.etherscan.io/api?module=proxy&action=eth_getTransactionCount&address=0x" + + encodedPublicKeyFrom.encode('hex'), "getTransactionCount") + args.nonce = int(result['result'], 16) + print("Nonce for 0x", encodedPublicKeyFrom.encode('hex'), " ", args.nonce, sep='') + +if args.amount == None: + print("Fetching balance") + result = rpc_call(http, "https://api.etherscan.io/api?module=account&action=balance&address=0x" + + encodedPublicKeyFrom.encode('hex'), "getBalance") + amount = int(result['result']) + print("Balance for 0x", encodedPublicKeyFrom.encode('hex'), " ", str(amount), sep='') + amount -= (int(args.startgas) - int(args.startgas_delta)) * \ + int(args.gasprice) + amount = 2 + if amount < 0: + raise Exception("Remaining amount too small to pay for contract fees") +else: + amount = Decimal(args.amount) * 10**18 + +print("Amount transferred", str((Decimal(amount) / 10 ** 18)), + "to", encodedPublicKey.encode('hex')) + +txData = SPLIT_CONTRACT_FUNCTION +txData += "\x00" * 31 +if (args.split_to_eth): + txData += "\x01" +else: + txData += "\x00" +txData += "\x00" * 12 +txData += encodedPublicKey + +tx = Transaction( + nonce=int(args.nonce), + gasprice=int(args.gasprice), + startgas=int(args.startgas), + to=decode_hex(SPLIT_CONTRACT_ADDRESS), + value=int(amount), + data=txData +) + +encodedTx = encode(tx, UnsignedTransaction) + +donglePath = parse_bip32_path(args.path) +apdu = "e0040000".decode('hex') + chr(len(donglePath) + 1 + + len(encodedTx)) + chr(len(donglePath) / 4) + donglePath + encodedTx + +result = dongle.exchange(bytes(apdu)) + +v = result[0] +r = int(str(result[1:1 + 32]).encode('hex'), 16) +s = int(str(result[1 + 32: 1 + 32 + 32]).encode('hex'), 16) + +tx = Transaction(tx.nonce, tx.gasprice, tx.startgas, + tx.to, tx.value, tx.data, v, r, s) +serializedTx = encode(tx) + +print("Signed transaction", serializedTx.encode('hex')) + +if (args.broadcast): + result = rpc_call(http, "https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=0x" + + serializedTx.encode('hex'), "sendRawTransaction") + print(result) diff --git a/signTx.py b/signTx.py deleted file mode 100644 index eaa46c0..0000000 --- a/signTx.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -""" -******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -******************************************************************************** -""" -from ledgerblue.comm import getDongle -from ledgerblue.commException import CommException -import argparse -import struct -from decimal import Decimal -from ethBase import Transaction, UnsignedTransaction -from rlp import encode -from rlp.utils import decode_hex, encode_hex, str_to_bytes - -def parse_bip32_path(path): - if len(path) == 0: - return "" - result = "" - elements = path.split('/') - for pathElement in elements: - element = pathElement.split('\'') - if len(element) == 1: - result = result + struct.pack(">I", int(element[0])) - else: - result = result + struct.pack(">I", 0x80000000 | int(element[0])) - return result - -parser = argparse.ArgumentParser() -parser.add_argument('--nonce', help="Nonce associated to the account") -parser.add_argument('--gasprice', help="Network gas price") -parser.add_argument('--startgas', help="startgas", default='21000') -parser.add_argument('--amount', help="Amount to send in ether") -parser.add_argument('--to', help="Destination address") -parser.add_argument('--path', help="BIP 32 path to sign with") -parser.add_argument('--data', help="Data to add, hex encoded") -args = parser.parse_args() - -if args.path == None: - args.path = "44'/60'/0'/0/0" - -if args.data == None: - args.data = "" -else: - args.data = decode_hex(args.data[2:]) - -amount = Decimal(args.amount) * 10**18 - -tx = Transaction( - nonce=int(args.nonce), - gasprice=int(args.gasprice), - startgas=int(args.startgas), - to=decode_hex(args.to[2:]), - value=int(amount), - data=args.data -) - -encodedTx = encode(tx, UnsignedTransaction) - -donglePath = parse_bip32_path(args.path) -apdu = "e0040000".decode('hex') + chr(len(donglePath) + 1 + len(encodedTx)) + chr(len(donglePath) / 4) + donglePath + encodedTx - -dongle = getDongle(True) -result = dongle.exchange(bytes(apdu)) - -v = result[0] -r = int(str(result[1:1 + 32]).encode('hex'), 16) -s = int(str(result[1 + 32: 1 + 32 + 32]).encode('hex'), 16) - -tx = Transaction(tx.nonce, tx.gasprice, tx.startgas, tx.to, tx.value, tx.data, v, r, s) - -print "Signed transaction " + encode_hex(encode(tx)) diff --git a/splitEther.py b/splitEther.py deleted file mode 100644 index fb790df..0000000 --- a/splitEther.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python -""" -******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -******************************************************************************** -""" -from ledgerblue.comm import getDongle -from ledgerblue.commException import CommException -import argparse -import struct -import requests -import json -from decimal import Decimal -from rlp import encode -from rlp.utils import decode_hex, encode_hex, str_to_bytes -from ethBase import Transaction, UnsignedTransaction, sha3 - -# https://etherscan.io/address/0x5dc8108fc79018113a58328f5283b376b83922ef#code -SPLIT_CONTRACT_FUNCTION = decode_hex("9c709343") -SPLIT_CONTRACT_ADDRESS = "5dc8108fc79018113a58328f5283b376b83922ef" - -def rpc_call(http, url, methodDebug): - req = http.get(url) - if req.status_code == 200: - result = json.loads(req.text) - if 'error' in result: - raise Exception("Server error - " + methodDebug + " - " + result['error']['message']) - return result - else: - raise Exception("Server error - " + methodDebug + " got status " + req.status) - - -def parse_bip32_path(path): - if len(path) == 0: - return "" - result = "" - elements = path.split('/') - for pathElement in elements: - element = pathElement.split('\'') - if len(element) == 1: - result = result + struct.pack(">I", int(element[0])) - else: - result = result + struct.pack(">I", 0x80000000 | int(element[0])) - return result - -parser = argparse.ArgumentParser() -parser.add_argument('--nonce', help="Nonce associated to the account (default : query account)") -parser.add_argument('--gasprice', help="Network gas price (default : query network)") -parser.add_argument('--startgas', help="startgas", default='80000') -parser.add_argument('--startgas-delta', help="difference applied to startgas if gasprice is automatically fetched", default='1000') -parser.add_argument('--amount', help="Amount to send in ether (default : query amount, use maximum)") -parser.add_argument('--to', help="BIP 32 destination path (default : default ETC path)") -parser.add_argument('--split-to-eth', help="Split to the ETH chain (default : spit to ETC chain)", action='store_true') -parser.add_argument('--path', help="BIP 32 path to sign with (default : default ETH path)") -parser.add_argument('--broadcast', help="Broadcast generated transaction (default : false)", action='store_true') -args = parser.parse_args() - -if args.path == None: - if args.split_to_eth: #sign from ETC - #args.path = "44'/60'/160720'/0'/0" - args.path = "44'/60'/0'/0" - else: #sign from ETH - args.path = "44'/60'/0'/0" - -if args.to == None: - if args.split_to_eth: #target ETH - args.to = "44'/60'/0'/0" - else: #target ETC transitional - args.to = "44'/60'/160720'/0'/0" - -dongle = getDongle(True) - -donglePath = parse_bip32_path(args.to) -apdu = "e0060000".decode('hex') + chr(len(donglePath) + 1) + chr(len(donglePath) / 4) + donglePath -dongle.exchange(bytes(apdu)) - -apdu = "e0020000".decode('hex') + chr(len(donglePath) + 1) + chr(len(donglePath) / 4) + donglePath -result = dongle.exchange(bytes(apdu)) -publicKey = str(result[1 : 1 + result[0]]) -encodedPublicKey = sha3(publicKey[1:])[12:] - -if (args.nonce == None) or (args.amount == None): - donglePathFrom = parse_bip32_path(args.path) - apdu = "e0020000".decode('hex') + chr(len(donglePathFrom) + 1) + chr(len(donglePathFrom) / 4) + donglePathFrom - result = dongle.exchange(bytes(apdu)) - publicKeyFrom = str(result[1 : 1 + result[0]]) - encodedPublicKeyFrom = sha3(publicKeyFrom[1:])[12:] - - -http = None -if (args.gasprice == None) or (args.nonce == None) or (args.amount == None) or (args.broadcast): - http = requests.session() - -if args.gasprice == None: - print "Fetching gas price" - result = rpc_call(http, "https://api.etherscan.io/api?module=proxy&action=eth_gasPrice", "gasPrice") - args.gasprice = int(result['result'], 16) - print "Gas price " + str(args.gasprice) - -if args.nonce == None: - print "Fetching nonce" - result = rpc_call(http, "https://api.etherscan.io/api?module=proxy&action=eth_getTransactionCount&address=0x" + encodedPublicKeyFrom.encode('hex'), "getTransactionCount") - args.nonce = int(result['result'], 16) - print "Nonce for 0x" + encodedPublicKeyFrom.encode('hex') + " " + str(args.nonce) - -if args.amount == None: - print "Fetching balance" - result = rpc_call(http, "https://api.etherscan.io/api?module=account&action=balance&address=0x" + encodedPublicKeyFrom.encode('hex'), "getBalance") - amount = int(result['result']) - print "Balance for " + encodedPublicKeyFrom.encode('hex') + " " + str(amount) - amount -= (int(args.startgas) - int(args.startgas_delta)) * int(args.gasprice) - if amount < 0: - raise Exception("Remaining amount too small to pay for contract fees") -else: - amount = Decimal(args.amount) * 10**18 - -print "Amount transferred " + str((Decimal(amount) / 10 ** 18)) + " to " + encodedPublicKey.encode('hex') - -txData = SPLIT_CONTRACT_FUNCTION -txData += "\x00" * 31 -if (args.split_to_eth): - txData += "\x01" -else: - txData += "\x00" -txData += "\x00" * 12 -txData += encodedPublicKey - -tx = Transaction( - nonce=int(args.nonce), - gasprice=int(args.gasprice), - startgas=int(args.startgas), - to=decode_hex(SPLIT_CONTRACT_ADDRESS), - value=int(amount), - data=txData -) - -encodedTx = encode(tx, UnsignedTransaction) - -donglePath = parse_bip32_path(args.path) -apdu = "e0040000".decode('hex') + chr(len(donglePath) + 1 + len(encodedTx)) + chr(len(donglePath) / 4) + donglePath + encodedTx - -result = dongle.exchange(bytes(apdu)) - -v = result[0] -r = int(str(result[1:1 + 32]).encode('hex'), 16) -s = int(str(result[1 + 32: 1 + 32 + 32]).encode('hex'), 16) - -tx = Transaction(tx.nonce, tx.gasprice, tx.startgas, tx.to, tx.value, tx.data, v, r, s) -serializedTx = encode(tx) - -print "Signed transaction " + serializedTx.encode('hex') - -if (args.broadcast): - result = rpc_call(http, "https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=0x" + serializedTx.encode('hex'), "sendRawTransaction") - print result diff --git a/src/chainConfig.h b/src/chainConfig.h index be11263..d87123d 100644 --- a/src/chainConfig.h +++ b/src/chainConfig.h @@ -1,9 +1,27 @@ +/******************************************************************************* +* Ledger Ethereum App +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#ifndef _CHAIN_CONFIG_H_ +#define _CHAIN_CONFIG_H_ + +#include + #include "os.h" -#ifndef CHAIN_CONFIG_H - -#define CHAIN_CONFIG_H - typedef enum chain_kind_e { CHAIN_KIND_ETHEREUM, CHAIN_KIND_ETHEREUM_CLASSIC, @@ -32,7 +50,6 @@ typedef struct chain_config_s { const char* coinName; // ticker uint32_t chainId; chain_kind_t kind; - #ifdef TARGET_BLUE const char* header_text; unsigned int color_header; @@ -41,5 +58,4 @@ typedef struct chain_config_s { } chain_config_t; -#endif - +#endif /* _CHAIN_CONFIG_H_ */ diff --git a/src/main.c b/src/main.c index 637b12d..067fab5 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,12 @@ * limitations under the License. ********************************************************************************/ +#include +#include +#include + #include "os.h" #include "cx.h" -#include "stdbool.h" #include "ethUstream.h" #include "ethUtils.h" #include "uint256.h" @@ -25,16 +28,12 @@ #include "chainConfig.h" #include "os_io_seproxyhal.h" -#include "string.h" #include "glyphs.h" - -#define __NAME3(a, b, c) a##b##c -#define NAME3(a, b, c) __NAME3(a, b, c) +#include "utils.h" unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - unsigned int io_seproxyhal_touch_settings(const bagl_element_t *e); unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e); unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e); @@ -167,21 +166,8 @@ static const char const CONTRACT_ADDRESS[] = "New contract"; static const char const SIGN_MAGIC[] = "\x19" "Ethereum Signed Message:\n"; -const unsigned char hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - chain_config_t *chainConfig; -void array_hexstr(char *strbuf, const void *bin, unsigned int len) { - while (len--) { - *strbuf++ = hex_digits[((*((char *)bin)) >> 4) & 0xF]; - *strbuf++ = hex_digits[(*((char *)bin)) & 0xF]; - bin = (const void *)((unsigned int)bin + 1); - } - *strbuf = 0; // EOS -} - - const bagl_element_t* ui_menu_item_out_over(const bagl_element_t* e) { // the selection rectangle is after the none|touchable e = (const bagl_element_t*)(((unsigned int)e)+sizeof(bagl_element_t)); @@ -238,9 +224,6 @@ const bagl_element_t ui_idle_blue[] = { {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_SETTINGS, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_settings, NULL, NULL}, {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, BAGL_FONT_SYMBOLS_0_DASHBOARD, 0, COLOR_APP, 0xFFFFFF, io_seproxyhal_touch_exit, NULL, NULL}, - // BADGE_.GIF - //{{BAGL_ICON , 0x00, 135, 178, 50, 50, 0, 0, BAGL_FILL, 0 , COLOR_BG_1, 0 ,0 } , &NAME3(C_blue_badge_, CHAINID, ), 0, 0, 0, NULL, NULL, NULL }, - {{BAGL_LABELINE , 0x00, 0, 270, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_LIGHT_16_22PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Open your wallet", 0, 0, 0, NULL, NULL, NULL}, {{BAGL_LABELINE , 0x00, 0, 308, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Connect your Ledger Blue and open your", 0, 0, 0, NULL, NULL, NULL}, {{BAGL_LABELINE , 0x00, 0, 331, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "preferred wallet to view your accounts.", 0, 0, 0, NULL, NULL, NULL}, @@ -318,7 +301,6 @@ const ux_menu_entry_t menu_about[] = { }; const ux_menu_entry_t menu_main[] = { - //{NULL, NULL, 0, &NAME3(C_nanos_badge_, CHAINID, ), "Use wallet to", "view accounts", 33, 12}, {NULL, NULL, 0, NULL, "Use wallet to", "view accounts", 0, 0}, {menu_settings, NULL, 0, NULL, "Settings", NULL, 0, 0}, {menu_about, NULL, 0, NULL, "About", NULL, 0, 0}, @@ -971,17 +953,6 @@ const bagl_element_t ui_data_parameter_blue[] = { {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0,18, BAGL_FILL, 0x41ccb4, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_11_14PX|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, 0 }, "CONFIRM", 0, 0x3ab7a2, COLOR_BG_1, io_seproxyhal_touch_data_ok, NULL, NULL}, }; -int local_strchr(char *string, char ch) { - unsigned int length = strlen(string); - unsigned int i; - for (i=0; icomponent.userid > 0) { @@ -1114,33 +1085,6 @@ unsigned int ui_address_nanos_button(unsigned int button_mask, unsigned int butt } #endif // #if defined(TARGET_NANOS) -uint32_t getV(txContent_t *txContent) { - uint32_t v = 0; - if (txContent->vLength == 1) { - v = txContent->v[0]; - } - else - if (txContent->vLength == 2) { - v = (txContent->v[0] << 8) | txContent->v[1]; - } - else - if (txContent->vLength == 3) { - v = (txContent->v[0] << 16) | (txContent->v[1] << 8) | txContent->v[2]; - } - else - if (txContent->vLength == 4) { - v = (txContent->v[0] << 24) | (txContent->v[1] << 16) | - (txContent->v[2] << 8) | txContent->v[3]; - } - else - if (txContent->vLength != 0) { - PRINTF("Unexpected v format\n"); - THROW(EXCEPTION); - } - return v; - -} - void io_seproxyhal_send_status(uint32_t sw) { G_io_apdu_buffer[0] = ((sw >> 8) & 0xff); G_io_apdu_buffer[1] = (sw & 0xff); @@ -1429,13 +1373,6 @@ uint32_t set_result_get_publicKey() { return tx; } -void convertUint256BE(uint8_t *data, uint32_t length, uint256_t *target) { - uint8_t tmp[32]; - os_memset(tmp, 0, 32); - os_memmove(tmp + 32 - length, data, length); - readu256BE(tmp, target); -} - uint32_t splitBinaryParameterPart(char *result, uint8_t *parameter) { uint32_t i; for (i=0; i<8; i++) { diff --git a/src/tokens.c b/src/tokens.c index 052ffe7..284e900 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/tokens.h b/src/tokens.h index cca47e1..3a8333d 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,10 @@ * limitations under the License. ********************************************************************************/ -#include "os.h" +#ifndef _TOKENS_H_ +#define _TOKENS_H_ + +#include typedef struct tokenDefinition_t { uint8_t address[20]; @@ -66,3 +69,5 @@ extern tokenDefinition_t const TOKENS_GOCHAIN[NUM_TOKENS_GOCHAIN]; extern tokenDefinition_t const TOKENS_MIX[NUM_TOKENS_MIX]; extern tokenDefinition_t const TOKENS_REOSC[NUM_TOKENS_REOSC]; extern tokenDefinition_t const TOKENS_HPB[NUM_TOKENS_HPB]; + +#endif /* _TOKENS_H_ */ diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..e40c8ed --- /dev/null +++ b/src/utils.c @@ -0,0 +1,78 @@ +/******************************************************************************* +* Ledger Ethereum App +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include + +#include "ethUstream.h" +#include "uint256.h" + +static const unsigned char hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + +void array_hexstr(char *strbuf, const void *bin, unsigned int len) { + while (len--) { + *strbuf++ = hex_digits[((*((char *)bin)) >> 4) & 0xF]; + *strbuf++ = hex_digits[(*((char *)bin)) & 0xF]; + bin = (const void *)((unsigned int)bin + 1); + } + *strbuf = 0; // EOS +} + +void convertUint256BE(uint8_t *data, uint32_t length, uint256_t *target) { + uint8_t tmp[32]; + os_memset(tmp, 0, 32); + os_memmove(tmp + 32 - length, data, length); + readu256BE(tmp, target); +} + +int local_strchr(char *string, char ch) { + unsigned int length = strlen(string); + unsigned int i; + for (i=0; ivLength == 1) { + v = txContent->v[0]; + } + else + if (txContent->vLength == 2) { + v = (txContent->v[0] << 8) | txContent->v[1]; + } + else + if (txContent->vLength == 3) { + v = (txContent->v[0] << 16) | (txContent->v[1] << 8) | txContent->v[2]; + } + else + if (txContent->vLength == 4) { + v = (txContent->v[0] << 24) | (txContent->v[1] << 16) | + (txContent->v[2] << 8) | txContent->v[3]; + } + else + if (txContent->vLength != 0) { + PRINTF("Unexpected v format\n"); + THROW(EXCEPTION); + } + return v; +} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..d918475 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,33 @@ +/******************************************************************************* +* Ledger Ethereum App +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include + +#include "uint256.h" + +void array_hexstr(char *strbuf, const void *bin, unsigned int len); + +void convertUint256BE(uint8_t *data, uint32_t length, uint256_t *target); + +int local_strchr(char *string, char ch); + +uint32_t getV(txContent_t *txContent); + +#endif /* _UTILS_H_ */ \ No newline at end of file diff --git a/src_common/ethUstream.c b/src_common/ethUstream.c index 585d2fa..09e5de6 100644 --- a/src_common/ethUstream.c +++ b/src_common/ethUstream.c @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ * limitations under the License. ********************************************************************************/ +#include + #include "ethUstream.h" #include "ethUtils.h" diff --git a/src_common/ethUstream.h b/src_common/ethUstream.h index 1f708da..c630596 100644 --- a/src_common/ethUstream.h +++ b/src_common/ethUstream.h @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,14 @@ * limitations under the License. ********************************************************************************/ +#ifndef _ETHUSTREAM_H_ +#define _ETHUSTREAM_H_ + +#include +#include + #include "os.h" #include "cx.h" -#include struct txContext_t; @@ -96,3 +101,5 @@ parserStatus_e processTx(txContext_t *context, uint8_t *buffer, parserStatus_e continueTx(txContext_t *context); void copyTxData(txContext_t *context, uint8_t *out, uint32_t length); uint8_t readTxByte(txContext_t *context); + +#endif /* _ETHUSTREAM_H_ */ \ No newline at end of file diff --git a/src_common/ethUtils.c b/src_common/ethUtils.c index dc2f68e..438daf9 100644 --- a/src_common/ethUtils.c +++ b/src_common/ethUtils.c @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,9 +23,12 @@ * @date 8th of March 2016 */ +#include +#include +#include + #include "os.h" #include "cx.h" -#include #include "ethUtils.h" #include "chainConfig.h" diff --git a/src_common/ethUtils.h b/src_common/ethUtils.h index b716938..9cdda12 100644 --- a/src_common/ethUtils.h +++ b/src_common/ethUtils.h @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,11 @@ * limitations under the License. ********************************************************************************/ -#include "os.h" +#ifndef _ETHUTILS_H_ +#define _ETHUTILS_H_ + +#include + #include "cx.h" /** @@ -46,3 +50,5 @@ void getEthAddressStringFromBinary(uint8_t *address, uint8_t *out, bool adjustDecimals(char *src, uint32_t srcLength, char *target, uint32_t targetLength, uint8_t decimals); + +#endif /* _ETHUTILS_H_ */ \ No newline at end of file diff --git a/src_common/uint256.c b/src_common/uint256.c index 16f1960..54ac8ba 100644 --- a/src_common/uint256.c +++ b/src_common/uint256.c @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #include #include + #include "uint256.h" static const char HEXDIGITS[] = "0123456789abcdef"; diff --git a/src_common/uint256.h b/src_common/uint256.h index 39daa50..2db6832 100644 --- a/src_common/uint256.h +++ b/src_common/uint256.h @@ -1,6 +1,6 @@ /******************************************************************************* -* Ledger Blue -* (c) 2016 Ledger +* Ledger Ethereum App +* (c) 2016-2019 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,9 @@ // Adapted from https://github.com/calccrypto/uint256_t +#ifndef _UINT256_H_ +#define _UINT256_H_ + #include #include @@ -63,3 +66,5 @@ bool tostring128(uint128_t *number, uint32_t base, char *out, uint32_t outLength); bool tostring256(uint256_t *number, uint32_t base, char *out, uint32_t outLength); + +#endif /* _UINT256_H_ */ \ No newline at end of file