@@ -18,6 +18,7 @@ use std::storageMap::storageMap_new;
1818use std::storageMap::storageMap_size;
1919use std::storageMap::storageMap_get;
2020use std::storageMap::storageMap_set;
21+ use std::storageMap::storageMap_forall;
2122
2223use core::kvs::Kvs;
2324use core::kvs::builtin_kvsSize;
@@ -70,7 +71,9 @@ type Account = struct {
7071};
7172
7273type AccountContractInfo = struct {
73- code: ByteArray, // deployed EVM code for the contract
74+ contractStateVersion: uint, // this changes when the rules of contract interpretation change,
75+ // for example due to Ethereum hardfork
76+ code: ByteArray, // deployed EVM code for the contract
7477 evmJumpTable: map<uint, view write func()>, // maps each valid EVM jump destination to the corresponding AVM codepoint
7578 startCodePoint: view write func(), // entry point for the translated AVM code
7679 storage: StorageMap,
@@ -140,7 +143,7 @@ public write func setGlobalAccountStore(acctStore: AccountStore) {
140143}
141144
142145public func accountStore_get(acctStore: AccountStore, addr: address) -> Account {
143- return xif let Some(acct) = acctStore.accounts[addr] {
146+ return if let Some(acct) = acctStore.accounts[addr] {
144147 acct
145148 } else {
146149 // There is no recorded account at this address, so make a pristine account.
@@ -194,8 +197,11 @@ public func accountStore_destroyAccount(acctStore: AccountStore, addrToDestroy:
194197 if let Some(contractInfo) = accountStore_get(acctStore, addrToDestroy).contractInfo {
195198 acctStore = accountStore_dropCodeRef(
196199 acctStore,
197- keccak256 (contractInfo.code, 0, bytearray_size( contractInfo.code))
200+ hashForCodeRef (contractInfo.code, contractInfo.contractStateVersion),
198201 );
202+ if (acctStore.numContracts > 0) { // avoid underflow, if the count is somehow incorrect
203+ set acctStore.numContracts = acctStore.numContracts-1;
204+ }
199205 }
200206 return accountStore_set(acctStore, addrToDestroy, pristineAccount(addrToDestroy));
201207}
@@ -215,7 +221,7 @@ public func account_checkAndIncrSeqNum(
215221 // if seqNumRecvd has the expected value, increment seqNum in account and return updated account.
216222 // else return None
217223 let nextSeq = account.nextSeqNum;
218- return xif (nextSeq == seqNumRecvd) {
224+ return if (nextSeq == seqNumRecvd) {
219225 Some(account with { nextSeqNum: nextSeq+1 })
220226 } else {
221227 None<Account>
@@ -239,7 +245,7 @@ public func account_getAggregatorToPayAsOption(account: Account) -> option<addre
239245}
240246
241247public view func account_getAggregatorToPay(account: Account) -> address {
242- return xif let Some(atp) = account.aggregatorToPay {
248+ return if let Some(atp) = account.aggregatorToPay {
243249 atp
244250 } else {
245251 chainParams_getDefaultAggregator()
@@ -293,7 +299,7 @@ public view func account_setAggregatorDecompressionState(
293299) -> Account {
294300 return account with {
295301 aggregatorInfo: Some(
296- xif let Some(aggInfo) = account.aggregatorInfo {
302+ if let Some(aggInfo) = account.aggregatorInfo {
297303 aggInfo with {
298304 decompressionState: maybeState
299305 }
@@ -313,7 +319,7 @@ public func _accountStore_feeCollectorForAggregator(acctStore: AccountStore, agg
313319}
314320
315321public func account_getFeeCollector(account: Account) -> address {
316- return xif let Some(aggInfo) = account.aggregatorInfo {
322+ return if let Some(aggInfo) = account.aggregatorInfo {
317323 aggInfo.feeCollector
318324 } else {
319325 account.addr // if fee collector isn't explicitly set, fees go to the account's own address
@@ -323,7 +329,7 @@ public func account_getFeeCollector(account: Account) -> address {
323329public view func account_setFeeCollector(account: Account, newCollector: address) -> Account {
324330 return account with {
325331 aggregatorInfo: Some(
326- xif let Some(aggInfo) = account.aggregatorInfo {
332+ if let Some(aggInfo) = account.aggregatorInfo {
327333 aggInfo with {
328334 feeCollector: newCollector
329335 }
@@ -341,7 +347,7 @@ public view func account_setFeeCollector(account: Account, newCollector: address
341347public func account_setBaseTxFeeL1Gas(account: Account, fee: uint) -> Account {
342348 return account with {
343349 aggregatorInfo: Some(
344- xif let Some(aggInfo) = account.aggregatorInfo {
350+ if let Some(aggInfo) = account.aggregatorInfo {
345351 aggInfo with {
346352 baseTxFeeL1Gas: fee
347353 }
@@ -367,14 +373,15 @@ public view write func accountStore_createAccountFromEvmCode(
367373 // Create a new account for a compiled EVM contract.
368374 // Returns Some(updatedAccountStore), or None if something went wrong.
369375 let oldAcct = accountStore_get(store, newAddr);
370- return xif ( ! account_hasContract(oldAcct)) {
376+ return if ( ! account_hasContract(oldAcct)) {
371377 evmCallStack_addStorageCharge( (bytearray_size(code) + 31) / 32 );
372378 set oldAcct.nextSeqNum = 1;
373379 set oldAcct.contractInfo = Some(struct {
374- code: code,
375- evmJumpTable: evmJumpTable,
376- startCodePoint: initCodePt,
377- storage: storageMap,
380+ contractStateVersion: const::ContractStateVersion_current,
381+ code: code,
382+ evmJumpTable: evmJumpTable,
383+ startCodePoint: initCodePt,
384+ storage: storageMap,
378385 });
379386 Some(accountStore_set(store, newAddr, oldAcct))
380387 } else {
@@ -394,18 +401,19 @@ public func accountStore_upgradeContractFromEvmCode(
394401 let oldAcct = accountStore_get(store, addr);
395402 let contractAdded = false;
396403 set oldAcct.contractInfo = Some(struct {
404+ contractStateVersion: const::ContractStateVersion_current,
397405 code: code,
398406 evmJumpTable: evmJumpTable,
399407 startCodePoint: initCodePt,
400- storage: xif let Some(cinfo) = oldAcct.contractInfo {
408+ storage: if let Some(cinfo) = oldAcct.contractInfo {
401409 cinfo.storage
402410 } else {
403411 contractAdded = true;
404412 storageMap_new()
405413 },
406414 });
407415 store = accountStore_set(store, addr, oldAcct);
408- return xif (contractAdded) {
416+ return if (contractAdded) {
409417 accountStore_changeNumContracts(store, int(1))
410418 } else {
411419 store
@@ -430,12 +438,13 @@ public func accountStore_createBuiltinContract(
430438 fakeCode = bytearray_setByte(fakeCode, 0, 0xfe);
431439 }
432440
433- return xif (account_isEmpty(oldAcct) || !isUpgrade) {
441+ return if (account_isEmpty(oldAcct) || !isUpgrade) {
434442 set oldAcct.contractInfo = Some(struct {
443+ contractStateVersion: const::ContractStateVersion_current,
435444 code: fakeCode,
436445 evmJumpTable: newmap<uint, view write func()>,
437446 startCodePoint: entryPoint,
438- storage: xif (isUpgrade && (oldAcct.contractInfo != None<AccountContractInfo>)) {
447+ storage: if (isUpgrade && (oldAcct.contractInfo != None<AccountContractInfo>)) {
439448 (oldAcct.contractInfo?).storage
440449 } else {
441450 storageMap_new()
@@ -453,7 +462,7 @@ public func accountStore_createBuiltinContract(
453462 numContracts: acctStore.numContracts + 1
454463 }
455464 )
456- } elseif (isUpgrade) {
465+ } else if (isUpgrade) {
457466 Some(accountStore_upgradeContractFromEvmCode(
458467 acctStore,
459468 addr,
@@ -476,7 +485,7 @@ public func account_addToEthBalance(account: Account, addition: uint) -> Account
476485}
477486
478487public func account_deductFromEthBalance(account: Account, deduction: uint) -> option<Account> {
479- return xif (account.ethBalance >= deduction) {
488+ return if (account.ethBalance >= deduction) {
480489 Some(
481490 account with { ethBalance: account.ethBalance-deduction }
482491 )
@@ -508,35 +517,55 @@ public func accountStore_transferEthBalance(
508517// Allocate a new escrow box, and transfer amount into it from the account of payer
509518// Return None if payer has insufficient funds
510519public func accountStore_newEscrow(acctStore: AccountStore, payer: address, amount: uint) -> option<(AccountStore, uint)> {
511- return xif (amount == 0) {
512- Some((acctStore, const::EscrowStoreID_txGas))
520+ let key = acctStore.escrowStore.nextKey;
521+ let updatedEscrowedValues = if (amount == 0) {
522+ // in this case we're allocating a new escrow box, which holds zero funds
523+ // this is not a waste of space in escrowedValues because a zero value doesn't consume space in a StorageMap
524+ acctStore.escrowStore.escrowedValues
513525 } else {
514526 acctStore = accountStore_set(
515527 acctStore,
516528 payer,
517529 account_deductFromEthBalance(accountStore_get(acctStore, payer), amount)?,
518530 );
519- let key = acctStore.escrowStore.nextKey;
520- Some((
521- acctStore with {
522- escrowStore: struct {
523- nextKey: key + 1,
524- escrowedValues: storageMap_set(acctStore.escrowStore.escrowedValues, key, amount),
525- }
526- },
527- key
528- ))
531+ storageMap_set(acctStore.escrowStore.escrowedValues, key, amount)
529532 };
533+ return Some((
534+ acctStore with {
535+ escrowStore: struct {
536+ nextKey: key + 1,
537+ escrowedValues: updatedEscrowedValues,
538+ }
539+ },
540+ key
541+ ));
530542}
531543
532- public func _accountStore_getEscrowBalance (acctStore: AccountStore, key: uint) -> uint {
544+ public func accountStore_getEscrowBalance (acctStore: AccountStore, key: uint) -> uint {
533545 return storageMap_get(acctStore.escrowStore.escrowedValues, key);
534546}
535547
548+ public func accountStore_sumOfAllEscrowBalances(acctStore: AccountStore) -> uint {
549+ unsafecast<uint>(
550+ storageMap_forall(
551+ acctStore.escrowStore.escrowedValues,
552+ /*closure(_k: uint, v: uint, state: any) -> any {
553+ return unsafecast<uint>(state) + v;
554+ },*/
555+ test_func,
556+ 0,
557+ )
558+ )
559+ }
560+
561+ public func test_func(k: uint, v: uint, state: any) -> any {
562+ unsafecast<uint>(state) + v
563+ }
564+
536565// Transfer funds into an existing escrow box
537566public func _accountStore_addToEscrow(acctStore: AccountStore, key: uint, payer: address, amount: uint) -> option<AccountStore> {
538567 let acct = accountStore_get(acctStore, payer);
539- return xif (acct.ethBalance < amount) {
568+ return if (acct.ethBalance < amount) {
540569 None<AccountStore>
541570 } else {
542571 let oldValue = storageMap_get(acctStore.escrowStore.escrowedValues, key);
@@ -554,7 +583,7 @@ public func _accountStore_addToEscrow(acctStore: AccountStore, key: uint, payer:
554583public func accountStore_addToEscrowUpToMax(acctStore: AccountStore, key: uint, payer: address, maxAmount: uint) -> (AccountStore, uint) {
555584 let acct = accountStore_get(acctStore, payer);
556585 let balance = account_getEthBalance(acct);
557- let amount = xif (balance < maxAmount) { balance } else { maxAmount };
586+ let amount = if (balance < maxAmount) { balance } else { maxAmount };
558587 let oldValue = storageMap_get(acctStore.escrowStore.escrowedValues, key);
559588 return (
560589 accountStore_set(acctStore, payer, acct with { ethBalance: acct.ethBalance-amount }) with {
@@ -569,7 +598,7 @@ public func accountStore_addToEscrowUpToMax(acctStore: AccountStore, key: uint,
569598// Transfer funds out of an escrow box into an account, or return None if box has insufficient funds.
570599public func accountStore_payFromEscrow(acctStore: AccountStore, key: uint, recipient: address, amount: uint) -> option<AccountStore> {
571600 let oldEscrow = storageMap_get(acctStore.escrowStore.escrowedValues, key);
572- return xif (oldEscrow < amount) {
601+ return if (oldEscrow < amount) {
573602 None<AccountStore>
574603 } else {
575604 let oldAcct = accountStore_get(acctStore, recipient);
@@ -648,6 +677,7 @@ public func account_setContractInfo(
648677
649678 return acct with { contractInfo: Some(
650679 struct {
680+ contractStateVersion: const::ContractStateVersion_current,
651681 code: code,
652682 evmJumpTable: evmJumpTable,
653683 startCodePoint: startCodePoint,
@@ -657,7 +687,7 @@ public func account_setContractInfo(
657687}
658688
659689public func account_getCodeSize(acct: Account) -> uint {
660- return xif let Some(contractInfo) = acct.contractInfo {
690+ return if let Some(contractInfo) = acct.contractInfo {
661691 bytearray_size(contractInfo.code)
662692 } else {
663693 0 // Account has no code, so its codesize is deemed to be zero.
@@ -668,6 +698,14 @@ public func account_getCode(acct: Account) -> option<ByteArray> {
668698 return Some((acct.contractInfo?).code);
669699}
670700
701+ public func account_usesOriginalStorageGasAlloc(acct: Account) -> bool {
702+ return if let Some(contractInfo) = acct.contractInfo {
703+ contractInfo.contractStateVersion == const::ContractStateVersion_original
704+ } else {
705+ false
706+ };
707+ }
708+
671709type asfa_wrappedState = struct {
672710 innerClosure: func(Account, any) -> any,
673711 innerState: any,
@@ -696,20 +734,28 @@ func asfaClosure(_: any, acct: Account, wrappedState: asfa_wrappedState) -> asfa
696734 };
697735}
698736
737+ func hashForCodeRef(code: ByteArray, contractStateVersion: uint) -> bytes32 {
738+ // This hash, meant as the key in the coderef table, is a collision-free function of this function's args.
739+ // For the original contractStateVersion (= 0) this is just the keccak256 of the code, so it is
740+ // backward-compatible with the original hashing scheme.
741+ return bytes32(uint(keccak256(code, 0, bytearray_size(code))) + contractStateVersion); // overflow OK
742+ }
743+
699744// If a CodeRef exists for code, increment its reference count.
700745// If one doesn't exist, create and initialize one.
701746// Return the attributes of the resulting CodeRef
702747public view write func accountStore_createOrAddCodeRef(
703748 acctStore: AccountStore,
749+ contractStateVersion: uint,
704750 code: ByteArray
705751) -> option<(
706752 AccountStore,
707753 view write func(),
708754 map<uint, view write func()>,
709755 bool,
710756)> {
711- let codeHash = keccak256 (code, 0, bytearray_size(code));
712- return xif let Some(codeRef) = acctStore.codeRefTable[codeHash] {
757+ let codeHash = hashForCodeRef (code, contractStateVersion); // overflow OK
758+ return if let Some(codeRef) = acctStore.codeRefTable[codeHash] {
713759 Some((
714760 accountStore_addCodeRef(acctStore, codeHash)?,
715761 codeRef.startCodePoint,
@@ -719,7 +765,7 @@ public view write func accountStore_createOrAddCodeRef(
719765 } else {
720766 let (startCodePoint, evmJumpTable, _) = translateEvmCodeSegment(bytestream_new(code), false);
721767 Some((
722- accountStore_createCodeRef(acctStore, code, evmJumpTable, startCodePoint),
768+ accountStore_createCodeRef(acctStore, contractStateVersion, code, evmJumpTable, startCodePoint),
723769 unsafecast<view write func()>(startCodePoint),
724770 unsafecast<map<uint, view write func()> >(evmJumpTable),
725771 true,
@@ -738,11 +784,12 @@ public func accountStore_addCodeRef(acctStore: AccountStore, codeHash: bytes32)
738784
739785public func accountStore_createCodeRef(
740786 acctStore: AccountStore,
787+ contractStateVersion: uint,
741788 code: ByteArray,
742789 evmJumpTable: map<uint, view write func()>,
743790 startCodePoint: view write func(),
744791) -> AccountStore {
745- let codeHash = keccak256 (code, 0, bytearray_size(code));
792+ let codeHash = hashForCodeRef (code, contractStateVersion); // overflow OK
746793 set acctStore.codeRefTable[codeHash] = struct {
747794 refCount: 1,
748795 code: code,
0 commit comments