-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8105c31
Showing
15 changed files
with
461 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
*.so | ||
|
||
build/ | ||
dist/ | ||
*.egg-info/ | ||
|
||
.tox/ | ||
venv/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Copyright © 2020 rexs.io | ||
|
||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# Blocksec2Go-Ethereum | ||
|
||
This repository contains the source code of `blocksec2go-ethereum` Python package which wraps the `blocksec2go` package to allow easier interaction with Ethereum blockchain. | ||
|
||
If you're unsure what Blockchain Security 2 Go is, [you can find more info here](https://github.com/Infineon/Blockchain). | ||
|
||
## Installation | ||
|
||
```bash | ||
pip install blocksec2go-ethereum | ||
``` | ||
|
||
## Usage | ||
|
||
After creating an instance of `Blocksec2Go` you can use it to generate signatures for transaction dicts. When passed raw tx, `sign_transaction()` will return a hex string of RLP-encoded signed transaction that can be directly consumed by `web3.eth.sendRawTransaction()`. | ||
|
||
### Transfer Ether | ||
|
||
Below you will find an example of signing a simple Ether transfer: | ||
|
||
```python | ||
from blocksec2go_ethereum import Blocksec2GoSigner | ||
from web3 import Web3 | ||
|
||
WEB3_ENDPOINT = 'YOUR_ENDPOINT_HERE' | ||
|
||
web3 = Web3(Web3.HTTPProvider(WEB3_ENDPOINT)) | ||
chain_id = web3.eth.chainId | ||
|
||
signer = Blocksec2GoSigner(chain_id=chain_id, key_id=1) | ||
address = signer.get_address() | ||
|
||
nonce = web3.eth.getTransactionCount(address) | ||
raw_tx = { | ||
'to': '0xBaBC446aee039E99d624058b0875E519190C6758', | ||
'nonce': nonce, | ||
'value': Web3.toWei(0.00005, 'ether'), | ||
'gas': 21000, | ||
'gasPrice': Web3.toWei(5, 'gwei'), | ||
} | ||
signed_tx = signer.sign_transaction(raw_tx) | ||
|
||
tx_hash = web3.eth.sendRawTransaction(signed_tx) | ||
print(f'Sent transaction with hash: {tx_hash.hex()}') | ||
``` | ||
|
||
### Call a contract function | ||
|
||
You can also sign any contract calls/creation transactions by leveraging `buildTransaction()`. | ||
|
||
Please note that for some contracts `buildTransaction()` may require explicitly setting `from` field to properly estimate gas. | ||
|
||
```python | ||
import json | ||
|
||
from blocksec2go_ethereum import Blocksec2GoSigner | ||
from web3 import Web3 | ||
|
||
WEB3_ENDPOINT = 'YOUR_ENDPOINT_HERE' | ||
|
||
web3 = Web3(Web3.HTTPProvider(WEB3_ENDPOINT)) | ||
chain_id = web3.eth.chainId | ||
|
||
signer = Blocksec2GoSigner(chain_id=chain_id, key_id=1) | ||
address = signer.get_address() | ||
|
||
with open('artifact.json', 'r') as artifact_file: | ||
artifact = json.loads(artifact_file.read()) | ||
contract = web3.eth.contract(address=artifact['address'], abi=artifact['abi']) | ||
|
||
nonce = web3.eth.getTransactionCount(address) | ||
raw_tx = contract.functions.setValue(42).buildTransaction({'nonce': nonce, 'from': address}) | ||
signed_tx = signer.sign_transaction(raw_tx) | ||
|
||
tx_hash = web3.eth.sendRawTransaction(signed_tx) | ||
print(f'Sent transaction with hash: {tx_hash.hex()}') | ||
``` | ||
|
||
## License | ||
ISC © 2020 rexs.io |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from ._signer import Blocksec2GoSigner # noqa F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import logging | ||
import time | ||
from typing import Tuple | ||
|
||
import blocksec2go | ||
from blocksec2go.comm.pyscard import open_pyscard | ||
from eth_account._utils.transactions import ( | ||
encode_transaction, | ||
serializable_unsigned_transaction_from_dict | ||
) | ||
from eth_typing import ChecksumAddress, HexStr | ||
from web3.types import TxParams | ||
|
||
from . import _utils | ||
from .exceptions import CardNotAvailable | ||
|
||
Signature = Tuple[int, int, int] | ||
|
||
|
||
class Blocksec2GoSigner: | ||
def __init__(self, key_id: int = 1, chain_id: int = 1, connect_retry_count: int = 5): | ||
self._key_id = key_id | ||
self._chain_id = chain_id | ||
self._connect_retry_count = connect_retry_count | ||
self._reader = None | ||
self._pub_key: bytes = bytes(0) | ||
|
||
self._logger = logging.getLogger('security2go_ethereum') | ||
self._init() | ||
|
||
def _init(self): | ||
retries_left = self._connect_retry_count | ||
|
||
while not self._reader and retries_left >= 0: | ||
try: | ||
self._reader = open_pyscard() | ||
except RuntimeError as details: | ||
self._logger.debug(details) | ||
|
||
self._logger.info(f'Reader or card not found. {retries_left} retries left.') | ||
retries_left = retries_left - 1 | ||
time.sleep(1) | ||
|
||
if not self._reader: | ||
self._logger.error('Exceeded connection retry count') | ||
raise CardNotAvailable | ||
|
||
blocksec2go.select_app(self._reader) | ||
self._pub_key = self._get_pub_key() | ||
self._logger.debug(f'Using public key {self._pub_key.hex()}') | ||
self._logger.info(f'Initialized for address {self.get_address()}') | ||
|
||
@property | ||
def chain_id(self): | ||
return self._chain_id | ||
|
||
@property | ||
def key_id(self): | ||
return self._key_id | ||
|
||
def get_address(self) -> ChecksumAddress: | ||
return _utils.address_from_public_key(self._pub_key) | ||
|
||
def sign_transaction(self, raw_tx: TxParams) -> HexStr: | ||
raw_tx.pop('from', None) | ||
raw_tx['chainId'] = self._chain_id | ||
|
||
tx = serializable_unsigned_transaction_from_dict(raw_tx) | ||
tx_hash = tx.hash() | ||
|
||
self._logger.debug(f'Signing transaction hash {tx_hash.hex()}') | ||
sig = self._generate_signature(tx_hash) | ||
signed_tx = encode_transaction(tx, vrs=sig) | ||
|
||
return HexStr(signed_tx.hex()) | ||
|
||
def _generate_signature(self, tx_hash: bytes) -> Signature: | ||
_, _, signature_der = blocksec2go.generate_signature( | ||
self._reader, self._key_id, tx_hash | ||
) | ||
self._logger.debug('Generated signature') | ||
|
||
rs_sig = _utils.sigdecode_der(signature_der) | ||
v = _utils.get_v(rs_sig, tx_hash, self._pub_key, self._chain_id) | ||
r, s = rs_sig | ||
|
||
return v, r, s | ||
|
||
def _get_pub_key(self) -> bytes: | ||
_, _, pub_key = blocksec2go.get_key_info(self._reader, self._key_id) | ||
unprefixed_pub_key = pub_key[1:] | ||
return unprefixed_pub_key |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from typing import Tuple | ||
|
||
import ecdsa | ||
from eth_typing import ChecksumAddress | ||
from web3 import Web3 | ||
|
||
from .exceptions import InvalidSignature | ||
|
||
UnrecoverableSignature = Tuple[int, int] | ||
|
||
|
||
def sigdecode_der(sig: bytes) -> UnrecoverableSignature: | ||
return ecdsa.util.sigdecode_der(sig, 0) | ||
|
||
|
||
def find_recovery_id(sig: UnrecoverableSignature, tx_hash: bytes, pub_key: bytes) -> int: | ||
r, s = sig | ||
vk = ecdsa.VerifyingKey.from_string(pub_key, curve=ecdsa.SECP256k1) | ||
vk_point = vk.pubkey.point | ||
hash_number = ecdsa.util.string_to_number(tx_hash) | ||
|
||
public_keys = ecdsa.ecdsa.Signature(r, s).recover_public_keys(hash_number, ecdsa.SECP256k1.generator) | ||
if public_keys[0].point == vk_point: | ||
return 0 | ||
elif public_keys[1].point == vk_point: | ||
return 1 | ||
raise InvalidSignature | ||
|
||
|
||
def address_from_public_key(public_key: bytes) -> ChecksumAddress: | ||
pk_hash = Web3.keccak(public_key) | ||
address_bytes = pk_hash[-20:] | ||
address = address_bytes.hex() | ||
return Web3.toChecksumAddress(address) | ||
|
||
|
||
def get_v(sig: UnrecoverableSignature, tx_hash: bytes, pub_key: bytes, chain_id: int) -> int: | ||
recovery_id = find_recovery_id(sig, tx_hash, pub_key) | ||
return 35 + recovery_id + (chain_id * 2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class InvalidSignature(BaseException): | ||
pass | ||
|
||
|
||
class CardNotAvailable(BaseException): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"address": "0xFb067a58851A386168411De892bD800F80649433", | ||
"abi": [ | ||
{ | ||
"constant": true, | ||
"inputs": [], | ||
"name": "value", | ||
"outputs": [ | ||
{ | ||
"internalType": "uint256", | ||
"name": "", | ||
"type": "uint256" | ||
} | ||
], | ||
"payable": false, | ||
"stateMutability": "view", | ||
"type": "function", | ||
"signature": "0x3fa4f245" | ||
}, | ||
{ | ||
"constant": false, | ||
"inputs": [ | ||
{ | ||
"internalType": "uint256", | ||
"name": "_value", | ||
"type": "uint256" | ||
} | ||
], | ||
"name": "setValue", | ||
"outputs": [], | ||
"payable": false, | ||
"stateMutability": "nonpayable", | ||
"type": "function", | ||
"signature": "0x55241077" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import json | ||
|
||
from blocksec2go_ethereum import Blocksec2GoSigner | ||
from web3 import Web3 | ||
|
||
WEB3_ENDPOINT = 'YOUR_ENDPOINT_HERE' | ||
|
||
web3 = Web3(Web3.HTTPProvider(WEB3_ENDPOINT)) | ||
chain_id = web3.eth.chainId | ||
|
||
signer = Blocksec2GoSigner(chain_id=chain_id, key_id=1) | ||
address = signer.get_address() | ||
|
||
with open('artifact.json', 'r') as artifact_file: | ||
artifact = json.loads(artifact_file.read()) | ||
contract = web3.eth.contract(address=artifact['address'], abi=artifact['abi']) | ||
|
||
nonce = web3.eth.getTransactionCount(address) | ||
raw_tx = contract.functions.setValue(42).buildTransaction({'nonce': nonce, 'from': address}) | ||
signed_tx = signer.sign_transaction(raw_tx) | ||
|
||
tx_hash = web3.eth.sendRawTransaction(signed_tx) | ||
print(f'Sent transaction with hash: {tx_hash.hex()}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from blocksec2go_ethereum import Blocksec2GoSigner | ||
from web3 import Web3 | ||
|
||
WEB3_ENDPOINT = 'YOUR_ENDPOINT_HERE' | ||
|
||
web3 = Web3(Web3.HTTPProvider(WEB3_ENDPOINT)) | ||
chain_id = web3.eth.chainId | ||
|
||
signer = Blocksec2GoSigner(chain_id=chain_id, key_id=1) | ||
address = signer.get_address() | ||
|
||
nonce = web3.eth.getTransactionCount(address) | ||
raw_tx = { | ||
'to': '0xBaBC446aee039E99d624058b0875E519190C6758', | ||
'nonce': nonce, | ||
'value': Web3.toWei(0.00005, 'ether'), | ||
'gas': 21000, | ||
'gasPrice': Web3.toWei(5, 'gwei'), | ||
} | ||
signed_tx = signer.sign_transaction(raw_tx) | ||
|
||
tx_hash = web3.eth.sendRawTransaction(signed_tx) | ||
print(f'Sent transaction with hash: {tx_hash.hex()}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
appdirs==1.4.3 | ||
attrdict==2.0.1 | ||
attrs==19.3.0 | ||
base58==2.0.0 | ||
blocksec2go==1.2 | ||
certifi==2019.11.28 | ||
cffi==1.14.0 | ||
chardet==3.0.4 | ||
cryptography==2.8 | ||
cytoolz==0.10.1 | ||
distlib==0.3.0 | ||
ecdsa==0.15 | ||
entrypoints==0.3 | ||
eth-abi==2.1.1 | ||
eth-account==0.4.0 | ||
eth-hash==0.2.0 | ||
eth-keyfile==0.5.1 | ||
eth-keys==0.2.4 | ||
eth-rlp==0.1.2 | ||
eth-typing==2.2.1 | ||
eth-utils==1.8.4 | ||
filelock==3.0.12 | ||
flake8==3.7.9 | ||
hexbytes==0.2.0 | ||
idna==2.9 | ||
importlib-metadata==1.5.0 | ||
ipfshttpclient==0.4.12 | ||
jsonschema==3.2.0 | ||
lru-dict==1.1.6 | ||
mccabe==0.6.1 | ||
multiaddr==0.0.9 | ||
netaddr==0.7.19 | ||
packaging==20.1 | ||
parsimonious==0.8.1 | ||
pluggy==0.13.1 | ||
protobuf==3.11.3 | ||
py==1.8.1 | ||
pycodestyle==2.5.0 | ||
pycparser==2.19 | ||
pycryptodome==3.9.7 | ||
pyflakes==2.1.1 | ||
pyparsing==2.4.6 | ||
pyrsistent==0.15.7 | ||
pyscard==1.9.9 | ||
requests==2.23.0 | ||
rlp==1.2.0 | ||
six==1.14.0 | ||
toml==0.10.0 | ||
toolz==0.10.0 | ||
tox==3.14.5 | ||
typing-extensions==3.7.4.1 | ||
urllib3==1.25.8 | ||
varint==1.0.2 | ||
virtualenv==20.0.7 | ||
web3==5.6.0 | ||
websockets==8.1 | ||
zipp==3.0.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[metadata] | ||
license_files = LICENSE | ||
|
||
[flake8] | ||
ignore = E501 |
Oops, something went wrong.