Skip to content

Commit 9e29926

Browse files
authored
chore: bump genvm version to v0.0.11, add contract deployment from IC (#753)
* bump genvm version to v0.0.11, add contract deployment from IC
1 parent 839d6ec commit 9e29926

23 files changed

+371
-88
lines changed

backend/consensus/base.py

+39-4
Original file line numberDiff line numberDiff line change
@@ -1163,14 +1163,49 @@ async def handle(self, context):
11631163
nonce = context.transactions_processor.get_transaction_count(
11641164
context.transaction.to_address
11651165
)
1166+
data: dict
1167+
transaction_type: TransactionType
1168+
if pending_transaction.is_deploy():
1169+
transaction_type = TransactionType.DEPLOY_CONTRACT
1170+
new_contract_address: str
1171+
if pending_transaction.salt_nonce == 0:
1172+
# NOTE: this address is random, which doesn't 100% align with consensus spec
1173+
new_contract_address = (
1174+
context.accounts_manager.create_new_account().address
1175+
)
1176+
else:
1177+
from eth_utils.crypto import keccak
1178+
from backend.node.types import Address
1179+
from backend.node.base import SIMULATOR_CHAIN_ID
1180+
1181+
arr = bytearray()
1182+
arr.append(1)
1183+
arr.extend(Address(context.transaction.to_address).as_bytes)
1184+
arr.extend(
1185+
pending_transaction.salt_nonce.to_bytes(32, "big", signed=False)
1186+
)
1187+
arr.extend(SIMULATOR_CHAIN_ID.to_bytes(32, "big", signed=False))
1188+
new_contract_address = Address(keccak(arr)[:20]).as_hex
1189+
context.accounts_manager.create_new_account_with_address(
1190+
new_contract_address
1191+
)
1192+
pending_transaction.address = new_contract_address
1193+
data = {
1194+
"contract_address": new_contract_address,
1195+
"contract_code": pending_transaction.code,
1196+
"calldata": pending_transaction.calldata,
1197+
}
1198+
else:
1199+
transaction_type = TransactionType.RUN_CONTRACT
1200+
data = {
1201+
"calldata": pending_transaction.calldata,
1202+
}
11661203
context.transactions_processor.insert_transaction(
11671204
context.transaction.to_address, # new calls are done by the contract
11681205
pending_transaction.address,
1169-
{
1170-
"calldata": pending_transaction.calldata,
1171-
},
1206+
data,
11721207
value=0, # we only handle EOA transfers at the moment, so no value gets transferred
1173-
type=TransactionType.RUN_CONTRACT.value,
1208+
type=transaction_type.value,
11741209
nonce=nonce,
11751210
leader_only=context.transaction.leader_only, # Cascade
11761211
triggered_by_hash=context.transaction.hash,

backend/database_handler/contract_snapshot.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class ContractSnapshot:
1414

1515
contract_address: str
1616
contract_code: str
17-
encoded_state: dict[str, dict[str, str]]
17+
encoded_state: dict[str, str]
1818

1919
def __init__(self, contract_address: str | None, session: Session):
2020
self.session = session

backend/node/base.py

+17-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
from .types import Address
2020

21+
SIMULATOR_CHAIN_ID: typing.Final[int] = 61999
22+
2123

2224
class _SnapshotView(genvmbase.StateProxy):
2325
def __init__(
@@ -43,14 +45,14 @@ def _get_snapshot(self, addr: Address) -> ContractSnapshot:
4345
return res
4446

4547
def get_code(self, addr: Address) -> bytes:
46-
return self._get_snapshot(addr).contract_code.encode("utf-8")
48+
return base64.b64decode(self._get_snapshot(addr).contract_code)
4749

4850
def storage_read(
4951
self, account: Address, slot: bytes, index: int, le: int, /
50-
) -> tuple[bytes, int]:
52+
) -> bytes:
5153
snap = self._get_snapshot(account)
52-
for_acc = snap.encoded_state.setdefault(account.as_b64, {})
53-
for_slot = for_acc.setdefault(base64.b64encode(slot).decode("ascii"), "")
54+
slot_id = base64.b64encode(slot).decode("ascii")
55+
for_slot = snap.encoded_state.setdefault(slot_id, "")
5456
data = bytearray(base64.b64decode(for_slot))
5557
data.extend(b"\x00" * (index + le - len(data)))
5658
return data[index : index + le]
@@ -66,14 +68,13 @@ def storage_write(
6668
assert account == self.contract_address
6769
assert not self.readonly
6870
snap = self._get_snapshot(account)
69-
for_acc = snap.encoded_state.setdefault(account.as_b64, {})
7071
slot_id = base64.b64encode(slot).decode("ascii")
71-
for_slot = for_acc.setdefault(slot_id, "")
72+
for_slot = snap.encoded_state.setdefault(slot_id, "")
7273
data = bytearray(base64.b64decode(for_slot))
7374
mem = memoryview(got)
7475
data.extend(b"\x00" * (index + len(mem) - len(data)))
7576
data[index : index + len(mem)] = mem
76-
for_acc[slot_id] = base64.b64encode(data).decode("utf-8")
77+
snap.encoded_state[slot_id] = base64.b64encode(data).decode("utf-8")
7778

7879

7980
class Node:
@@ -102,10 +103,11 @@ async def exec_transaction(self, transaction: Transaction) -> Receipt:
102103
transaction_data = transaction.data
103104
assert transaction.from_address is not None
104105
if transaction.type == TransactionType.DEPLOY_CONTRACT:
106+
code = base64.b64decode(transaction_data["contract_code"])
105107
calldata = base64.b64decode(transaction_data["calldata"])
106108
receipt = await self.deploy_contract(
107109
transaction.from_address,
108-
transaction_data["contract_code"],
110+
code,
109111
calldata,
110112
transaction.hash,
111113
transaction.created_at,
@@ -149,13 +151,15 @@ def _date_from_str(self, date: str | None) -> datetime.datetime | None:
149151
async def deploy_contract(
150152
self,
151153
from_address: str,
152-
code_to_deploy: str,
154+
code_to_deploy: bytes,
153155
calldata: bytes,
154156
transaction_hash: str,
155157
transaction_created_at: str | None = None,
156158
) -> Receipt:
157159
assert self.contract_snapshot is not None
158-
self.contract_snapshot.contract_code = code_to_deploy
160+
self.contract_snapshot.contract_code = base64.b64encode(code_to_deploy).decode(
161+
"ascii"
162+
)
159163
return await self._run_genvm(
160164
from_address,
161165
calldata,
@@ -220,9 +224,9 @@ async def _execution_finished(
220224
)
221225
)
222226

223-
async def get_contract_schema(self, code: str) -> str:
227+
async def get_contract_schema(self, code: bytes) -> str:
224228
genvm = self._create_genvm()
225-
res = await genvm.get_contract_schema(code.encode("utf-8"))
229+
res = await genvm.get_contract_schema(code)
226230
await self._execution_finished(res, None)
227231
err_data = {
228232
"stdout": res.stdout,
@@ -298,6 +302,7 @@ async def _run_genvm(
298302
leader_results=leader_res,
299303
config=json.dumps(config),
300304
date=transaction_datetime,
305+
chain_id=SIMULATOR_CHAIN_ID,
301306
)
302307
await self._execution_finished(res, transaction_hash)
303308

backend/node/genvm/base.py

+42-7
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ async def run_contract(
9292
leader_results: None | dict[int, bytes],
9393
config: str,
9494
date: datetime.datetime | None,
95+
chain_id: int,
9596
) -> ExecutionResult: ...
9697

9798
async def get_contract_schema(self, contract_code: bytes) -> ExecutionResult: ...
@@ -137,15 +138,20 @@ async def run_contract(
137138
leader_results: None | dict[int, bytes],
138139
config: str,
139140
date: datetime.datetime | None,
141+
chain_id: int,
140142
) -> ExecutionResult:
141-
assert date.tzinfo is not None
142143
message = {
143144
"is_init": is_init,
144145
"contract_account": contract_address.as_b64,
145146
"sender_account": from_address.as_b64,
147+
"origin_account": from_address.as_b64, # FIXME: no origin in simulator #751
146148
"value": None,
149+
"chain_id": str(
150+
chain_id
151+
), # NOTE: it can overflow u64 so better to wrap it into a string
147152
}
148153
if date is not None:
154+
assert date.tzinfo is not None
149155
message["datetime"] = date.isoformat()
150156
return await _run_genvm_host(
151157
functools.partial(
@@ -164,8 +170,9 @@ async def get_contract_schema(self, contract_code: bytes) -> ExecutionResult:
164170
"is_init": False,
165171
"contract_account": NO_ADDR,
166172
"sender_account": NO_ADDR,
173+
"origin_account": NO_ADDR,
167174
"value": None,
168-
"gas": 2**64 - 1,
175+
"chain_id": "0",
169176
}
170177
return await _run_genvm_host(
171178
functools.partial(
@@ -227,9 +234,13 @@ async def get_calldata(self, /) -> bytes:
227234
async def get_code(self, addr: bytes, /) -> bytes:
228235
return self._state_proxy.get_code(Address(addr))
229236

237+
def has_result(self) -> bool:
238+
return self._result is not None
239+
230240
async def storage_read(
231-
self, account: bytes, slot: bytes, index: int, le: int, /
241+
self, type: StorageType, account: bytes, slot: bytes, index: int, le: int, /
232242
) -> bytes:
243+
assert type != StorageType.LATEST_FINAL
233244
return self._state_proxy.storage_read(Address(account), slot, index, le)
234245

235246
async def storage_write(
@@ -273,16 +284,40 @@ async def post_nondet_result(
273284
encoded_result.extend(memoryview(data))
274285
self._eq_outputs[call_no] = bytes(encoded_result)
275286

276-
async def post_message(
277-
self, gas: int, account: bytes, calldata: bytes, code: bytes, /
278-
) -> None:
287+
async def post_message(self, account: bytes, calldata: bytes, _data, /) -> None:
279288
self._pending_transactions.append(
280-
PendingTransaction(Address(account).as_hex, calldata)
289+
PendingTransaction(
290+
Address(account).as_hex, calldata, code=None, salt_nonce=0
291+
)
281292
)
282293

283294
async def consume_gas(self, gas: int, /) -> None:
284295
pass
285296

297+
async def deploy_contract(
298+
self,
299+
calldata: bytes,
300+
code: bytes,
301+
data: genvmhost.DeployDefaultTransactionData,
302+
/,
303+
) -> None:
304+
self._pending_transactions.append(
305+
PendingTransaction(
306+
address="0x",
307+
calldata=calldata,
308+
code=code,
309+
salt_nonce=data.get("salt_nonce", 0),
310+
)
311+
)
312+
313+
async def eth_send(self, account: bytes, calldata: bytes, /) -> None:
314+
# FIXME(core-team): #748
315+
assert False
316+
317+
async def eth_call(self, account: bytes, calldata: bytes, /) -> bytes:
318+
# FIXME(core-team): #748
319+
assert False
320+
286321

287322
async def _run_genvm_host(
288323
host_supplier: typing.Callable[[socket.socket], _Host],

0 commit comments

Comments
 (0)