Files

137 lines
4.8 KiB
Python
Raw Permalink Normal View History

2016-06-01 21:41:29 +02:00
#!/usr/bin/env python
"""
*******************************************************************************
2019-01-03 17:00:20 +01:00
* Ledger Ethereum App
* (c) 2016-2019 Ledger
2016-06-01 21:41:29 +02:00
*
* 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.
********************************************************************************
"""
2019-01-16 11:07:31 +01:00
from __future__ import print_function
2016-06-01 21:41:29 +02:00
from ledgerblue.comm import getDongle
from ledgerblue.commException import CommException
2019-01-29 10:41:34 +01:00
from decimal import Decimal
2016-06-01 21:41:29 +02:00
import argparse
import struct
2019-01-16 11:07:31 +01:00
import binascii
from ethBase import Transaction, UnsignedTransaction, unsigned_tx_from_tx
2016-06-01 21:41:29 +02:00
from rlp import encode
2019-01-17 19:00:33 +01:00
# Define here Chain_ID for EIP-155
CHAIN_ID = 0
2019-01-16 11:07:31 +01:00
try:
from rlp.utils import decode_hex, encode_hex, str_to_bytes
except:
# Python3 hack import for pyethereum
2019-01-16 11:07:31 +01:00
from ethereum.utils import decode_hex, encode_hex, str_to_bytes
2019-01-16 11:23:00 +01:00
2016-06-01 21:41:29 +02:00
def parse_bip32_path(path):
2019-01-16 11:23:00 +01:00
if len(path) == 0:
2019-01-16 11:07:31 +01:00
return b""
result = b""
2019-01-16 11:23:00 +01:00
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
2016-06-01 21:41:29 +02:00
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)
2019-01-17 14:00:36 +01:00
parser.add_argument('--startgas', help="startgas", default='21000', type=int)
2019-01-29 10:41:34 +01:00
parser.add_argument('--amount', help="Amount to send in ether", required=True)
parser.add_argument('--to', help="Destination address",
type=str, required=True)
2016-06-01 21:41:29 +02:00
parser.add_argument('--path', help="BIP 32 path to sign with")
parser.add_argument('--data', help="Data to add, hex encoded")
parser.add_argument(
'--chainid', help="Chain ID (1 for Ethereum mainnet, 137 for Polygon, etc)", type=int)
2020-09-22 09:22:49 +02:00
parser.add_argument('--descriptor', help="Optional descriptor")
2016-06-01 21:41:29 +02:00
args = parser.parse_args()
if args.path == None:
# if you want to use the next account -> "44'/60'/1'/0/0"
2019-01-16 11:23:00 +01:00
args.path = "44'/60'/0'/0/0"
2016-06-01 21:41:29 +02:00
if args.data == None:
2019-01-16 11:07:31 +01:00
args.data = b""
2016-06-01 21:41:29 +02:00
else:
2019-01-16 11:23:00 +01:00
args.data = decode_hex(args.data[2:])
2016-06-01 21:41:29 +02:00
# default to Ethereum mainnet
if args.chainid == None:
args.chainid = 1
2019-01-29 10:41:34 +01:00
amount = Decimal(args.amount) * 10**18
2016-06-01 21:41:29 +02:00
tx = UnsignedTransaction(
2016-06-01 21:41:29 +02:00
nonce=int(args.nonce),
gasprice=int(args.gasprice),
startgas=int(args.startgas),
to=decode_hex(args.to[2:]),
value=int(amount),
2019-01-16 11:07:31 +01:00
data=args.data,
chainid=args.chainid,
dummy1=0,
dummy2=0
2016-06-01 21:41:29 +02:00
)
encodedTx = encode(tx, UnsignedTransaction)
# To test an EIP-1559 transaction, uncomment this line
# encodedTx = bytearray.fromhex(
# "02ef0306843b9aca008504a817c80082520894b2bb2b958afa2e96dab3f3ce7162b87daea39017872386f26fc1000080c0")
2016-06-01 21:41:29 +02:00
# To test an EIP-2930 transaction, uncomment this line
#encodedTx = bytearray.fromhex("01f8e60380018402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0a4693c61390000000000000000000000000000000000000000000000000000000000000002f85bf859940000000000000000000000000000000000000102f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a780a09b8adcd2a4abd34b42d56fcd90b949f74ca9696dfe2b427bc39aa280bbf1924ca029af4a471bb2953b4e7933ea95880648552a9345424a1ac760189655ceb1832a")
2016-06-01 21:41:29 +02:00
2020-09-22 09:22:49 +02:00
dongle = getDongle(True)
if args.descriptor != None:
descriptor = binascii.unhexlify(args.descriptor)
apdu = struct.pack(">BBBBB", 0xE0, 0x0A, 0x00, 0x00,
len(descriptor)) + descriptor
2020-09-22 09:22:49 +02:00
dongle.exchange(bytes(apdu))
2016-06-01 21:41:29 +02:00
donglePath = parse_bip32_path(args.path)
2019-01-16 11:07:31 +01:00
apdu = bytearray.fromhex("e0040000")
apdu.append(len(donglePath) + 1 + len(encodedTx))
apdu.append(len(donglePath) // 4)
apdu += donglePath + encodedTx
2016-06-01 21:41:29 +02:00
result = dongle.exchange(bytes(apdu))
2019-01-17 19:00:33 +01:00
# Needs to recover (main.c:1121)
# if (CHAIN_ID*2 + 35) + 1 > 255:
# ecc_parity = result[0] - ((CHAIN_ID*2 + 35) % 256)
# v = (CHAIN_ID*2 + 35) + ecc_parity
# else:
# v = result[0]
2019-01-17 19:00:33 +01:00
# r = int(binascii.hexlify(result[1:1 + 32]), 16)
# s = int(binascii.hexlify(result[1 + 32: 1 + 32 + 32]), 16)
2016-06-01 21:41:29 +02:00
# tx = Transaction(tx.nonce, tx.gasprice, tx.startgas,
# tx.to, tx.value, tx.data, v, r, s)
2016-06-01 21:41:29 +02:00
2019-01-16 11:07:31 +01:00
print("Signed transaction", encode_hex(encode(tx)))