Skip to content

Commit

Permalink
Problem: eip712 feature is not tested (crypto-org-chain#625)
Browse files Browse the repository at this point in the history
* allow show key pubkey

* gen py proto

* add eip712 utils

* fix import keys

* make use of encode_structured_data

* fix import path for test

* separate utils with test

* mv msg send types to json

* fix lint

* update eth_account for regex support array

for more info, see https://github.com/ethereum/eth-account/pull/175/files

* replace subprocess with requests

* group py proto

* avoid multiple instances of same module

replace init under proto with conftest

* simplify

* post with based64 encoded str

for more info, see https://stackoverflow.com/a/606199
  • Loading branch information
mmsqe authored Aug 11, 2022
1 parent b907918 commit 307433f
Show file tree
Hide file tree
Showing 21 changed files with 1,802 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[flake8]
max-line-length = 88
extend-ignore = E203
exclude = .git,__pycache__,./integration_tests/contracts
exclude = .git,__pycache__,./integration_tests/contracts,./integration_tests/**/*_pb2.py
6 changes: 6 additions & 0 deletions integration_tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import os
import sys

import pytest

from .network import setup_cronos, setup_geth

dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(dir + "/protobuf")


def pytest_configure(config):
config.addinivalue_line("markers", "slow: marks tests as slow")
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/cosmoscli.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@ def distribution_reward(self, delegator_addr):
)["total"][0]
return float(coin["amount"])

def address(self, name, bech="acc"):
def address(self, name, bech="acc", field="address"):
output = self.raw(
"keys",
"show",
name,
"-a",
f"--{field}",
home=self.data_dir,
keyring_backend="test",
bech=bech,
Expand Down
356 changes: 356 additions & 0 deletions integration_tests/eip712_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
import base64
import json
from pathlib import Path

import sha3

from .protobuf.cosmos.bank.v1beta1.tx_pb2 import MsgSend
from .protobuf.cosmos.base.v1beta1.coin_pb2 import Coin
from .protobuf.cosmos.crypto.secp256k1.keys_pb2 import PubKey
from .protobuf.cosmos.tx.v1beta1.tx_pb2 import (
AuthInfo,
Fee,
ModeInfo,
SignDoc,
SignerInfo,
TxBody,
TxRaw,
)
from .protobuf.ethermint.crypto.v1.ethsecp256k1.keys_pb2 import PubKey as EPubKey
from .protobuf.ethermint.types.v1.web3_pb2 import ExtensionOptionsWeb3Tx
from .protobuf.google.protobuf.any_pb2 import Any

LEGACY_AMINO = 127
SIGN_DIRECT = 1


def create_message_send(
chain,
sender,
fee,
memo,
params,
):
# EIP712
fee_object = generate_fee(
fee["amount"],
fee["denom"],
fee["gas"],
sender["accountAddress"],
)
types = generate_types()
msg = create_msg_send(
params["amount"],
params["denom"],
sender["accountAddress"],
params["destinationAddress"],
)
messages = generate_message(
str(sender["accountNumber"]),
str(sender["sequence"]),
chain["cosmosChainId"],
memo,
fee_object,
msg,
)
eip_to_sign = create_eip712(types, chain["chainId"], messages)
msg_send = proto_msg_send(
sender["accountAddress"],
params["destinationAddress"],
params["amount"],
params["denom"],
)
tx = create_transaction(
msg_send,
memo,
fee["amount"],
fee["denom"],
fee["gas"],
"ethsecp256",
sender["pubkey"],
sender["sequence"],
sender["accountNumber"],
chain["cosmosChainId"],
)
return {
"signDirect": tx["signDirect"],
"legacyAmino": tx["legacyAmino"],
"eipToSign": eip_to_sign,
}


def generate_fee(amount, denom, gas, fee_payer):
return {
"amount": [
{
"amount": amount,
"denom": denom,
},
],
"gas": gas,
"feePayer": fee_payer,
}


def generate_types():
return json.loads((Path(__file__).parent / "msg_send_types.json").read_text())


def create_msg_send(amount, denom, from_address, to_address):
return {
"type": "cosmos-sdk/MsgSend",
"value": {
"amount": [
{
"amount": amount,
"denom": denom,
},
],
"from_address": from_address,
"to_address": to_address,
},
}


def generate_message(account_number, sequence, chain_cosmos_id, memo, fee, msg):
return generate_message_with_multiple_transactions(
account_number,
sequence,
chain_cosmos_id,
memo,
fee,
[msg],
)


def generate_message_with_multiple_transactions(
account_number,
sequence,
chain_cosmos_id,
memo,
fee,
msgs,
):
return {
"account_number": account_number,
"chain_id": chain_cosmos_id,
"fee": fee,
"memo": memo,
"msgs": msgs,
"sequence": sequence,
}


def create_eip712(types, chain_id, message, name="Cosmos Web3", contract="cosmos"):
return {
"types": types,
"primaryType": "Tx",
"domain": {
"name": name,
"version": "1.0.0",
"chainId": chain_id,
"verifyingContract": contract,
"salt": "0",
},
"message": message,
}


def create_transaction(
message,
memo,
fee,
denom,
gas_limit,
algo,
pub_key,
sequence,
account_number,
chain_id,
):
return create_transaction_with_multiple_messages(
[message],
memo,
fee,
denom,
gas_limit,
algo,
pub_key,
sequence,
account_number,
chain_id,
)


def create_transaction_with_multiple_messages(
messages,
memo,
fee,
denom,
gas_limit,
algo,
pub_key,
sequence,
account_number,
chain_id,
):
body = create_body_with_multiple_messages(messages, memo)
fee_message = create_fee(fee, denom, gas_limit)
pub_key_decoded = base64.b64decode(pub_key.encode("ascii"))
# AMINO
sign_info_amino = create_signer_info(
algo,
pub_key_decoded,
sequence,
LEGACY_AMINO,
)
auth_info_amino = create_auth_info(sign_info_amino, fee_message)
sig_doc_amino = create_sig_doc(
body.SerializeToString(),
auth_info_amino.SerializeToString(),
chain_id,
account_number,
)

hash_amino = sha3.keccak_256()
hash_amino.update(sig_doc_amino.SerializeToString())
to_sign_amino = hash_amino.hexdigest()

# SignDirect
sig_info_direct = create_signer_info(
algo,
pub_key_decoded,
sequence,
SIGN_DIRECT,
)
auth_info_direct = create_auth_info(sig_info_direct, fee_message)
sign_doc_direct = create_sig_doc(
body.SerializeToString(),
auth_info_direct.SerializeToString(),
chain_id,
account_number,
)
hash_direct = sha3.keccak_256()
hash_direct.update(sign_doc_direct.SerializeToString())
to_sign_direct = hash_direct.hexdigest()
return {
"legacyAmino": {
"body": body,
"authInfo": auth_info_amino,
"signBytes": base64.b64decode(to_sign_amino),
},
"signDirect": {
"body": body,
"authInfo": auth_info_direct,
"signBytes": base64.b64decode(to_sign_direct),
},
}


def create_body_with_multiple_messages(messages, memo):
content = []
for message in messages:
content.append(create_any_message(message))
body = TxBody(memo=memo, messages=content)
return body


def create_any_message(msg):
any = Any()
any.Pack(msg["message"], "/")
return any


def create_signer_info(algo, public_key, sequence, mode):
message = None
path = None
if algo == "secp256k1":
message = PubKey(key=public_key)
path = "cosmos.crypto.secp256k1.PubKey"
else:
message = EPubKey(key=public_key)
path = "ethermint.crypto.v1.ethsecp256k1.PubKey"

pubkey = {
"message": message,
"path": path,
}
single = ModeInfo.Single(mode=mode)
mode_info = ModeInfo(single=single)
signer_info = SignerInfo(
mode_info=mode_info,
sequence=sequence,
public_key=create_any_message(pubkey),
)
return signer_info


def create_auth_info(signer_info, fee):
auth_info = AuthInfo(
signer_infos=[signer_info],
fee=fee,
)
return auth_info


def create_sig_doc(body_bytes, auth_info_bytes, chain_id, account_number):
sign_doc = SignDoc(
body_bytes=body_bytes,
auth_info_bytes=auth_info_bytes,
chain_id=chain_id,
account_number=account_number,
)
return sign_doc


def create_fee(fee, denom, gas_limit):
value = Coin(denom=denom, amount=fee)
fee = Fee(gas_limit=int(gas_limit), amount=[value])
return fee


def proto_msg_send(from_address, to_address, amount, denom):
value = Coin(denom=denom, amount=amount)
message = MsgSend(
from_address=from_address,
to_address=to_address,
amount=[value],
)
return {
"message": message,
"path": "cosmos.bank.v1beta1.MsgSend",
}


def signature_to_web3_extension(chain, sender, signature):
message = ExtensionOptionsWeb3Tx(
typed_data_chain_id=chain["chainId"],
fee_payer=sender["accountAddress"],
fee_payer_sig=signature,
)
return {
"message": message,
"path": "ethermint.types.v1.ExtensionOptionsWeb3Tx",
}


def create_tx_raw(body_bytes, auth_info_bytes, signatures):
message = TxRaw(
body_bytes=body_bytes,
auth_info_bytes=auth_info_bytes,
signatures=signatures,
)
return {
"message": message,
"path": "cosmos.tx.v1beta1.TxRaw",
}


def create_tx_raw_eip712(body, auth_info, extension):
any = create_any_message(extension)
body.extension_options.append(any)
return create_tx_raw(
body.SerializeToString(),
auth_info.SerializeToString(),
[bytes()],
)
1 change: 1 addition & 0 deletions integration_tests/msg_send_types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"string"},{"name":"salt","type":"string"}],"Tx":[{"name":"account_number","type":"string"},{"name":"chain_id","type":"string"},{"name":"fee","type":"Fee"},{"name":"memo","type":"string"},{"name":"msgs","type":"Msg[]"},{"name":"sequence","type":"string"}],"Fee":[{"name":"feePayer","type":"string"},{"name":"amount","type":"Coin[]"},{"name":"gas","type":"string"}],"Coin":[{"name":"denom","type":"string"},{"name":"amount","type":"string"}],"Msg":[{"name":"type","type":"string"},{"name":"value","type":"MsgValue"}],"MsgValue":[{"name":"from_address","type":"string"},{"name":"to_address","type":"string"},{"name":"amount","type":"TypeAmount[]"}],"TypeAmount":[{"name":"denom","type":"string"},{"name":"amount","type":"string"}]}
Loading

0 comments on commit 307433f

Please sign in to comment.