Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
web3
ipfsapi
ipfsapi
pysha3
secp256k1
61 changes: 60 additions & 1 deletion servicesExternal/web3_single.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from config import config
from lib.etherum_abi_perso import toSolidityBytes32 as etherumToSolid

import secp256k1
import sha3

class Singleton(type):
_instances = {}
Expand Down Expand Up @@ -182,6 +183,50 @@ def areSameAddressesNoChecksum(self, address1: str, address2: str) -> bool:
return False
return address1.toLowerCase() == address2.toLowerCase()

def keccak(byteMessage: bytes) -> str:
return sha3.keccak_256(byteMessage).digest()

def messageHash(message: bytes) -> str:
'''
Returns the keccak-256 hash of message, prefixed with the header used by the eth_sign RPC call
ethereumjs-util equivalent: https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/index.md#hashpersonalmessage
'''
if not isinstance(message, bytes):
message = message.encode('utf8')
prefix = '\u0019Ethereum Signed Message:\n' + str(len(message))
prefix = prefix.encode('utf8')
return keccak(prefix + message)

def legendreCheck(r: int) -> bool:
# Legendre symbol check; the secp256k1 library does not seem to do this
SECP256K1P = 2**256 - 4294968273
xc = r * r * r + 7
return pow(xc, (SECP256K1P - 1) // 2, SECP256K1P) == 1

def ecrecoverToPub(hash: str, signature: str, chaincode: int = 27) -> str:
'''
ECDSA public key recovery from signature
ethereumjs-util equivalent: https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/index.md#ecrecover
'''
assert len(hash) == 32 # Keccak-256 in Solidity returns a bytestring of length 32, so our hash must meet this condition
assert len(signature) == 65
assert legendreCheck(int.from_bytes(signature[0:32], 'big'))
hint = signature[-1] - chaincode
pk = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS)
recover_sig = pk.ecdsa_recoverable_deserialize(signature[:-1], hint)
pk.public_key = pk.ecdsa_recover(hash, recover_sig, raw=True)
pub = pk.serialize(compressed=False)[1:]
assert len(pub) == 64
return pub

def pubToAddress(pubkey: str) -> str:
'''
Converts a public key into a hex String
ethereumjs-util equivalent: https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/index.md#buffertohex
'''
return '0x' + (keccak(pubkey[0:])[12:]).hex()


def isHexStrictBytes32(self, hex: str) -> bool:
'''
Check if a string is a hex byte
Expand Down Expand Up @@ -315,3 +360,17 @@ def resultToArray(self, obj: Any) -> List[Any]:
for i in range(len(obj)):
result.append(obj[i])
return result

def isValidSignatureForSolidity(self, signature: str, hash: str, payee: str) -> bool:
'''
Check if the provided signature is valid for Solidity
:param signature: Ethereum signature
:param hash: transaction message hash
:param payee: payee address
'''
try:
pubkey = ecrecoverToPub(hash, signature) # if transaction message is not already hashed then use hash = messageHash(message)
return payee == pubToAddress(pubkey)
except:
return False