diff --git a/README.md b/README.md index 68df11b..22bd2ff 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,13 @@ Ethereum wallet application framework for Ledger Blue and Nano S This follows the specification available in the doc/ folder -To use the generic wallet (disabling all data transaction) refer to signTx.py (requires at least ethereum==1.1.0) or Ledger Ethereum Wallet Chrome application available on Github at https://github.com/LedgerHQ/ledger-wallet-ethereum-chrome or the Chrome Web store at https://chrome.google.com/webstore/detail/ledger-wallet-ethereum/hmlhkialjkaldndjnlcdfdphcgeadkkm +To use the generic wallet (which disables all data transaction) refer to signTx.py (requires rlp package) or Ledger Ethereum Wallet Chrome application available on Github at https://github.com/LedgerHQ/ledger-wallet-ethereum-chrome or the Chrome Web store at https://chrome.google.com/webstore/detail/ledger-wallet-ethereum/hmlhkialjkaldndjnlcdfdphcgeadkkm -An example application reusing the Ethereum parsing framework to implement some advanced use case (transfering The DAO (RIP) tokens) is also provided for historical reasons - not supported on Nano S and probably not compiling on Blue with the new SDK. +Other examples are provided reusing the Ethereum parsing framwork to implement some advanced use cases : -For more information you can refer to https://medium.com/@Ledger/dynamic-secure-applications-with-bolos-and-ledger-blue-a-use-case-with-ethereum-and-the-dao-6be91260e89f#.204qgmogo + * An ETH/ETC splitting contract - see src_chainsplit, splitEther.py + * An unsupported use case (transferring The DAO (RIP) tokens), using deprecated code - see src_daosend + + +For more information about the parsing framework you can refer to https://medium.com/@Ledger/dynamic-secure-applications-with-bolos-and-ledger-blue-a-use-case-with-ethereum-and-the-dao-6be91260e89f#.204qgmogo diff --git a/ethBase.py b/ethBase.py new file mode 100644 index 0000000..240a4c9 --- /dev/null +++ b/ethBase.py @@ -0,0 +1,51 @@ +#!/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/signTx.py b/signTx.py index 4659ee5..eaa46c0 100644 --- a/signTx.py +++ b/signTx.py @@ -22,12 +22,10 @@ from ledgerblue.commException import CommException import argparse import struct from decimal import Decimal -from ethereum.transactions import Transaction, UnsignedTransaction +from ethBase import Transaction, UnsignedTransaction from rlp import encode from rlp.utils import decode_hex, encode_hex, str_to_bytes -from ethereum import utils - def parse_bip32_path(path): if len(path) == 0: return "" diff --git a/splitEther.py b/splitEther.py index 62fc218..fb790df 100644 --- a/splitEther.py +++ b/splitEther.py @@ -24,11 +24,9 @@ import struct import requests import json from decimal import Decimal -from ethereum.transactions import Transaction, UnsignedTransaction from rlp import encode from rlp.utils import decode_hex, encode_hex, str_to_bytes - -from ethereum import utils +from ethBase import Transaction, UnsignedTransaction, sha3 # https://etherscan.io/address/0x5dc8108fc79018113a58328f5283b376b83922ef#code SPLIT_CONTRACT_FUNCTION = decode_hex("9c709343") @@ -39,7 +37,7 @@ def rpc_call(http, url, methodDebug): if req.status_code == 200: result = json.loads(req.text) if 'error' in result: - raise Exception("Server error " + methodDebug + " " + result['error']['message']) + raise Exception("Server error - " + methodDebug + " - " + result['error']['message']) return result else: raise Exception("Server error - " + methodDebug + " got status " + req.status) @@ -92,14 +90,14 @@ 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 = utils.sha3(publicKey[1:])[12:] +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 = utils.sha3(publicKeyFrom[1:])[12:] + encodedPublicKeyFrom = sha3(publicKeyFrom[1:])[12:] http = None