Skip to content

Commit 307433f

Browse files
authored
Problem: eip712 feature is not tested (#625)
* 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
1 parent b907918 commit 307433f

File tree

21 files changed

+1802
-7
lines changed

21 files changed

+1802
-7
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[flake8]
22
max-line-length = 88
33
extend-ignore = E203
4-
exclude = .git,__pycache__,./integration_tests/contracts
4+
exclude = .git,__pycache__,./integration_tests/contracts,./integration_tests/**/*_pb2.py

integration_tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import os
2+
import sys
3+
14
import pytest
25

36
from .network import setup_cronos, setup_geth
47

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

612
def pytest_configure(config):
713
config.addinivalue_line("markers", "slow: marks tests as slow")

integration_tests/cosmoscli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,12 @@ def distribution_reward(self, delegator_addr):
223223
)["total"][0]
224224
return float(coin["amount"])
225225

226-
def address(self, name, bech="acc"):
226+
def address(self, name, bech="acc", field="address"):
227227
output = self.raw(
228228
"keys",
229229
"show",
230230
name,
231-
"-a",
231+
f"--{field}",
232232
home=self.data_dir,
233233
keyring_backend="test",
234234
bech=bech,

integration_tests/eip712_utils.py

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
import base64
2+
import json
3+
from pathlib import Path
4+
5+
import sha3
6+
7+
from .protobuf.cosmos.bank.v1beta1.tx_pb2 import MsgSend
8+
from .protobuf.cosmos.base.v1beta1.coin_pb2 import Coin
9+
from .protobuf.cosmos.crypto.secp256k1.keys_pb2 import PubKey
10+
from .protobuf.cosmos.tx.v1beta1.tx_pb2 import (
11+
AuthInfo,
12+
Fee,
13+
ModeInfo,
14+
SignDoc,
15+
SignerInfo,
16+
TxBody,
17+
TxRaw,
18+
)
19+
from .protobuf.ethermint.crypto.v1.ethsecp256k1.keys_pb2 import PubKey as EPubKey
20+
from .protobuf.ethermint.types.v1.web3_pb2 import ExtensionOptionsWeb3Tx
21+
from .protobuf.google.protobuf.any_pb2 import Any
22+
23+
LEGACY_AMINO = 127
24+
SIGN_DIRECT = 1
25+
26+
27+
def create_message_send(
28+
chain,
29+
sender,
30+
fee,
31+
memo,
32+
params,
33+
):
34+
# EIP712
35+
fee_object = generate_fee(
36+
fee["amount"],
37+
fee["denom"],
38+
fee["gas"],
39+
sender["accountAddress"],
40+
)
41+
types = generate_types()
42+
msg = create_msg_send(
43+
params["amount"],
44+
params["denom"],
45+
sender["accountAddress"],
46+
params["destinationAddress"],
47+
)
48+
messages = generate_message(
49+
str(sender["accountNumber"]),
50+
str(sender["sequence"]),
51+
chain["cosmosChainId"],
52+
memo,
53+
fee_object,
54+
msg,
55+
)
56+
eip_to_sign = create_eip712(types, chain["chainId"], messages)
57+
msg_send = proto_msg_send(
58+
sender["accountAddress"],
59+
params["destinationAddress"],
60+
params["amount"],
61+
params["denom"],
62+
)
63+
tx = create_transaction(
64+
msg_send,
65+
memo,
66+
fee["amount"],
67+
fee["denom"],
68+
fee["gas"],
69+
"ethsecp256",
70+
sender["pubkey"],
71+
sender["sequence"],
72+
sender["accountNumber"],
73+
chain["cosmosChainId"],
74+
)
75+
return {
76+
"signDirect": tx["signDirect"],
77+
"legacyAmino": tx["legacyAmino"],
78+
"eipToSign": eip_to_sign,
79+
}
80+
81+
82+
def generate_fee(amount, denom, gas, fee_payer):
83+
return {
84+
"amount": [
85+
{
86+
"amount": amount,
87+
"denom": denom,
88+
},
89+
],
90+
"gas": gas,
91+
"feePayer": fee_payer,
92+
}
93+
94+
95+
def generate_types():
96+
return json.loads((Path(__file__).parent / "msg_send_types.json").read_text())
97+
98+
99+
def create_msg_send(amount, denom, from_address, to_address):
100+
return {
101+
"type": "cosmos-sdk/MsgSend",
102+
"value": {
103+
"amount": [
104+
{
105+
"amount": amount,
106+
"denom": denom,
107+
},
108+
],
109+
"from_address": from_address,
110+
"to_address": to_address,
111+
},
112+
}
113+
114+
115+
def generate_message(account_number, sequence, chain_cosmos_id, memo, fee, msg):
116+
return generate_message_with_multiple_transactions(
117+
account_number,
118+
sequence,
119+
chain_cosmos_id,
120+
memo,
121+
fee,
122+
[msg],
123+
)
124+
125+
126+
def generate_message_with_multiple_transactions(
127+
account_number,
128+
sequence,
129+
chain_cosmos_id,
130+
memo,
131+
fee,
132+
msgs,
133+
):
134+
return {
135+
"account_number": account_number,
136+
"chain_id": chain_cosmos_id,
137+
"fee": fee,
138+
"memo": memo,
139+
"msgs": msgs,
140+
"sequence": sequence,
141+
}
142+
143+
144+
def create_eip712(types, chain_id, message, name="Cosmos Web3", contract="cosmos"):
145+
return {
146+
"types": types,
147+
"primaryType": "Tx",
148+
"domain": {
149+
"name": name,
150+
"version": "1.0.0",
151+
"chainId": chain_id,
152+
"verifyingContract": contract,
153+
"salt": "0",
154+
},
155+
"message": message,
156+
}
157+
158+
159+
def create_transaction(
160+
message,
161+
memo,
162+
fee,
163+
denom,
164+
gas_limit,
165+
algo,
166+
pub_key,
167+
sequence,
168+
account_number,
169+
chain_id,
170+
):
171+
return create_transaction_with_multiple_messages(
172+
[message],
173+
memo,
174+
fee,
175+
denom,
176+
gas_limit,
177+
algo,
178+
pub_key,
179+
sequence,
180+
account_number,
181+
chain_id,
182+
)
183+
184+
185+
def create_transaction_with_multiple_messages(
186+
messages,
187+
memo,
188+
fee,
189+
denom,
190+
gas_limit,
191+
algo,
192+
pub_key,
193+
sequence,
194+
account_number,
195+
chain_id,
196+
):
197+
body = create_body_with_multiple_messages(messages, memo)
198+
fee_message = create_fee(fee, denom, gas_limit)
199+
pub_key_decoded = base64.b64decode(pub_key.encode("ascii"))
200+
# AMINO
201+
sign_info_amino = create_signer_info(
202+
algo,
203+
pub_key_decoded,
204+
sequence,
205+
LEGACY_AMINO,
206+
)
207+
auth_info_amino = create_auth_info(sign_info_amino, fee_message)
208+
sig_doc_amino = create_sig_doc(
209+
body.SerializeToString(),
210+
auth_info_amino.SerializeToString(),
211+
chain_id,
212+
account_number,
213+
)
214+
215+
hash_amino = sha3.keccak_256()
216+
hash_amino.update(sig_doc_amino.SerializeToString())
217+
to_sign_amino = hash_amino.hexdigest()
218+
219+
# SignDirect
220+
sig_info_direct = create_signer_info(
221+
algo,
222+
pub_key_decoded,
223+
sequence,
224+
SIGN_DIRECT,
225+
)
226+
auth_info_direct = create_auth_info(sig_info_direct, fee_message)
227+
sign_doc_direct = create_sig_doc(
228+
body.SerializeToString(),
229+
auth_info_direct.SerializeToString(),
230+
chain_id,
231+
account_number,
232+
)
233+
hash_direct = sha3.keccak_256()
234+
hash_direct.update(sign_doc_direct.SerializeToString())
235+
to_sign_direct = hash_direct.hexdigest()
236+
return {
237+
"legacyAmino": {
238+
"body": body,
239+
"authInfo": auth_info_amino,
240+
"signBytes": base64.b64decode(to_sign_amino),
241+
},
242+
"signDirect": {
243+
"body": body,
244+
"authInfo": auth_info_direct,
245+
"signBytes": base64.b64decode(to_sign_direct),
246+
},
247+
}
248+
249+
250+
def create_body_with_multiple_messages(messages, memo):
251+
content = []
252+
for message in messages:
253+
content.append(create_any_message(message))
254+
body = TxBody(memo=memo, messages=content)
255+
return body
256+
257+
258+
def create_any_message(msg):
259+
any = Any()
260+
any.Pack(msg["message"], "/")
261+
return any
262+
263+
264+
def create_signer_info(algo, public_key, sequence, mode):
265+
message = None
266+
path = None
267+
if algo == "secp256k1":
268+
message = PubKey(key=public_key)
269+
path = "cosmos.crypto.secp256k1.PubKey"
270+
else:
271+
message = EPubKey(key=public_key)
272+
path = "ethermint.crypto.v1.ethsecp256k1.PubKey"
273+
274+
pubkey = {
275+
"message": message,
276+
"path": path,
277+
}
278+
single = ModeInfo.Single(mode=mode)
279+
mode_info = ModeInfo(single=single)
280+
signer_info = SignerInfo(
281+
mode_info=mode_info,
282+
sequence=sequence,
283+
public_key=create_any_message(pubkey),
284+
)
285+
return signer_info
286+
287+
288+
def create_auth_info(signer_info, fee):
289+
auth_info = AuthInfo(
290+
signer_infos=[signer_info],
291+
fee=fee,
292+
)
293+
return auth_info
294+
295+
296+
def create_sig_doc(body_bytes, auth_info_bytes, chain_id, account_number):
297+
sign_doc = SignDoc(
298+
body_bytes=body_bytes,
299+
auth_info_bytes=auth_info_bytes,
300+
chain_id=chain_id,
301+
account_number=account_number,
302+
)
303+
return sign_doc
304+
305+
306+
def create_fee(fee, denom, gas_limit):
307+
value = Coin(denom=denom, amount=fee)
308+
fee = Fee(gas_limit=int(gas_limit), amount=[value])
309+
return fee
310+
311+
312+
def proto_msg_send(from_address, to_address, amount, denom):
313+
value = Coin(denom=denom, amount=amount)
314+
message = MsgSend(
315+
from_address=from_address,
316+
to_address=to_address,
317+
amount=[value],
318+
)
319+
return {
320+
"message": message,
321+
"path": "cosmos.bank.v1beta1.MsgSend",
322+
}
323+
324+
325+
def signature_to_web3_extension(chain, sender, signature):
326+
message = ExtensionOptionsWeb3Tx(
327+
typed_data_chain_id=chain["chainId"],
328+
fee_payer=sender["accountAddress"],
329+
fee_payer_sig=signature,
330+
)
331+
return {
332+
"message": message,
333+
"path": "ethermint.types.v1.ExtensionOptionsWeb3Tx",
334+
}
335+
336+
337+
def create_tx_raw(body_bytes, auth_info_bytes, signatures):
338+
message = TxRaw(
339+
body_bytes=body_bytes,
340+
auth_info_bytes=auth_info_bytes,
341+
signatures=signatures,
342+
)
343+
return {
344+
"message": message,
345+
"path": "cosmos.tx.v1beta1.TxRaw",
346+
}
347+
348+
349+
def create_tx_raw_eip712(body, auth_info, extension):
350+
any = create_any_message(extension)
351+
body.extension_options.append(any)
352+
return create_tx_raw(
353+
body.SerializeToString(),
354+
auth_info.SerializeToString(),
355+
[bytes()],
356+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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"}]}

0 commit comments

Comments
 (0)