This is an enhanced version of Ethereum Virtual Machine(EVM), it provides the ability to:
- Track the state changes
- Track the call stacks
With the above 2 abilities, it will be easier for Artela Aspect developers to know the states of their dApps, which could reduce the security issues in some ways.
NewEVM generates a Artela VM from the provided Message fields and the chain parameters
import "github.com/artela-network/artela-evm/vm"
// NewEVM generates a Artela VM from the provided Message fields and the chain parameters
// (ChainConfig and module Params). It additionally sets the validator operator address as the
// coinbase address to make it available for the COINBASE opcode, even though there is no
// beneficiary of the coinbase txs (since we're not mining).
func (k *Keeper) NewEVM(
ctx cosmos.Context,
msg *core.Message,
cfg *states.EVMConfig,
tracer vm.EVMLogger,
stateDB vm.StateDB,
) *vm.EVM {
blockCtx := vm.BlockContext{
CanTransfer: artcore.CanTransfer,
Transfer: artcore.Transfer,
GetHash: k.GetHashFn(ctx),
Coinbase: cfg.CoinBase,
GasLimit: artela.BlockGasLimit(ctx),
BlockNumber: big.NewInt(ctx.BlockHeight()),
Time: uint64(ctx.BlockHeader().Time.Unix()),
Difficulty: big.NewInt(0), // unused. Only required in PoW context
BaseFee: cfg.BaseFee,
Random: nil, // not supported
}
txCtx := artcore.NewEVMTxContext(msg)
if tracer == nil {
tracer = k.Tracer(ctx, msg, cfg.ChainConfig)
}
vmConfig := k.VMConfig(ctx, msg, cfg, tracer)
return vm.NewEVM(blockCtx, txCtx, stateDB, cfg.ChainConfig, vmConfig)
}
- Require your smart contract compile with ASOLC,
- Record SSTORE operations.
- Unimplemented features
-
Not able to handle array.push and delete for now, since this is not an assignment, it’s a function call
string[] dummy function push() { dummy.push("haha"); delete dummy[0]; }
-
Extra Op Codes for entire array change:
VAJOURNAL
: stateVar, key, slot, isDelete, memSizeRAJOURNAL
: stateVar, key, slot, isDelete, memSize
-
aave-v3-core/protocol/configuration/PoolAddressesProviderRegistry.sol
/** * @notice Adds the addresses provider address to the list. * @param provider The address of the PoolAddressesProvider */ function _addToAddressesProvidersList(address provider) internal { _addressesProvidersIndexes[provider] = _addressesProvidersList.length; _addressesProvidersList.push(provider); }
-
aave-v3-core/protocol/libraries/logic/PoolLogic.sol
/** * @notice Drop a reserve * @param reservesData The state of all the reserves * @param reservesList The addresses of all the active reserves * @param asset The address of the underlying asset of the reserve */ function executeDropReserve( mapping(address => DataTypes.ReserveData) storage reservesData, mapping(uint256 => address) storage reservesList, address asset ) external { DataTypes.ReserveData storage reserve = reservesData[asset]; ValidationLogic.validateDropReserve(reservesList, reserve, asset); reservesList[reservesData[asset].id] = address(0); delete reservesData[asset]; }
-
-
Not able to handle local storage pointer assignment for now, e.g.:
contract { mapping(string => mapping(uint256 => string)) dummy; mapping(uint256 => mapping(uint256 => string)) dummy2; function xxx() { mapping(uint256 => string) storage local = dummy["test"]; // in the AST we are missing the previous local // storage variable declaration, need to cache it local[1] = "test2"; // passing storage pointer to another function xxx2(local); // passing storage pointer to another function with scope escaping xxx2(dummy2[1]); } function xxx2(mapping(uint256 => string) storage param) { param[0] = "test3"; } }
-
Extra Op Codes for record scope escaping:
-
RPARENT
: stateVar, prefixKey, slot, memSize → parent : bytes32// Monitor --> intermediate state assignment { "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" : { // storage slot "stateVar": "dummy", "prefixKey": "0xbbbb...." }, "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" : { // ... } }
-
VPJOURNAL
: parent, key, slot, memSize -
RPJOURNAL
: parent, key, slot, memSize -
VAPJOURNAL
: parent, key, slot, isDelete, memSize -
RAPJOURNAL
: parent, key, slot, isDelete, memSize
-
-
aave-v3-core/protocol/libraries/BorrowLogic.sol
/** * @notice Implements the borrow feature. Borrowing allows users that provided collateral to draw liquidity from the * Aave protocol proportionally to their collateralization power. For isolated positions, it also increases the * isolated debt. * @dev Emits the `Borrow()` event * @param reservesData The state of all the reserves * @param reservesList The addresses of all the active reserves * @param eModeCategories The configuration of all the efficiency mode categories * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets * @param params The additional parameters needed to execute the borrow function */ function executeBorrow( mapping(address => DataTypes.ReserveData) storage reservesData, mapping(uint256 => address) storage reservesList, mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, DataTypes.UserConfigurationMap storage userConfig, DataTypes.ExecuteBorrowParams memory params ) public { DataTypes.ReserveData storage reserve = reservesData[params.asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); reserve.updateState(reserveCache); ( bool isolationModeActive, address isolationModeCollateralAddress, uint256 isolationModeDebtCeiling ) = userConfig.getIsolationModeState(reservesData, reservesList); ValidationLogic.validateBorrow( reservesData, reservesList, eModeCategories, DataTypes.ValidateBorrowParams({ reserveCache: reserveCache, userConfig: userConfig, asset: params.asset, userAddress: params.onBehalfOf, amount: params.amount, interestRateMode: params.interestRateMode, maxStableLoanPercent: params.maxStableRateBorrowSizePercent, reservesCount: params.reservesCount, oracle: params.oracle, userEModeCategory: params.userEModeCategory, priceOracleSentinel: params.priceOracleSentinel, isolationModeActive: isolationModeActive, isolationModeCollateralAddress: isolationModeCollateralAddress, isolationModeDebtCeiling: isolationModeDebtCeiling }) ); uint256 currentStableRate = 0; bool isFirstBorrowing = false; if (params.interestRateMode == DataTypes.InterestRateMode.STABLE) { currentStableRate = reserve.currentStableBorrowRate; ( isFirstBorrowing, reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( params.user, params.onBehalfOf, params.amount, currentStableRate ); } else { (isFirstBorrowing, reserveCache.nextScaledVariableDebt) = IVariableDebtToken( reserveCache.variableDebtTokenAddress ).mint(params.user, params.onBehalfOf, params.amount, reserveCache.nextVariableBorrowIndex); } if (isFirstBorrowing) { userConfig.setBorrowing(reserve.id, true); } if (isolationModeActive) { uint256 nextIsolationModeTotalDebt = reservesData[isolationModeCollateralAddress] .isolationModeTotalDebt += (params.amount / 10 ** (reserveCache.reserveConfiguration.getDecimals() - ReserveConfiguration.DEBT_CEILING_DECIMALS)).toUint128(); emit IsolationModeTotalDebtUpdated( isolationModeCollateralAddress, nextIsolationModeTotalDebt ); } reserve.updateInterestRates( reserveCache, params.asset, 0, params.releaseUnderlying ? params.amount : 0 ); if (params.releaseUnderlying) { IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(params.user, params.amount); } emit Borrow( params.asset, params.user, params.onBehalfOf, params.amount, params.interestRateMode, params.interestRateMode == DataTypes.InterestRateMode.STABLE ? currentStableRate : reserve.currentVariableBorrowRate, params.referralCode ); }
-
-
Not able to handle expression index, e.g.:
contract { mapping(bytes8 => string) dummy; mapping(bytes => string) a; function xxx(bytes calldata good) { // uint8(1) will be recognized as a type conversion function call dummy[bytes8(1)] = "haha"; // need to generate ir code for evaluating expression first a[d == 0 ? good[2:] : good[0:2]] = "haha"; } }
-
aave-v3-core/misc/AaveOracle.sol
mapping(address => AggregatorInterface) private assetsSources; function _setAssetsSources(address[] memory assets, address[] memory sources) internal { require(assets.length == sources.length, Errors.INCONSISTENT_PARAMS_LENGTH); for (uint256 i = 0; i < assets.length; i++) { assetsSources[assets[i]] = AggregatorInterface(sources[i]); emit AssetSourceUpdated(assets[i], sources[i]); } }
-
aave-v3-core/protocol/pool/Pool.sol
/// @inheritdoc IPool function getReservesList() external view virtual override returns (address[] memory) { uint256 reservesListCount = _reservesCount; uint256 droppedReservesCount = 0; address[] memory reservesList = new address[](reservesListCount); for (uint256 i = 0; i < reservesListCount; i++) { if (_reservesList[i] != address(0)) { reservesList[i - droppedReservesCount] = _reservesList[i]; } else { droppedReservesCount++; } } // Reduces the length of the reserves array by `droppedReservesCount` assembly { mstore(reservesList, sub(reservesListCount, droppedReservesCount)) } return reservesList; }
-
aave-v3-core/protocol/libraries/logic/PoolLogic.sol
/** * @notice Drop a reserve * @param reservesData The state of all the reserves * @param reservesList The addresses of all the active reserves * @param asset The address of the underlying asset of the reserve */ function executeDropReserve( mapping(address => DataTypes.ReserveData) storage reservesData, mapping(uint256 => address) storage reservesList, address asset ) external { DataTypes.ReserveData storage reserve = reservesData[asset]; ValidationLogic.validateDropReserve(reservesList, reserve, asset); reservesList[reservesData[asset].id] = address(0); delete reservesData[asset]; }
-
-
Not able to handle non-equal assignment, e.g.:
contract { uint256 dummy; function xxx() { dummy++; dummy<<1; } }
-
Not able to handle tuple assignment, e.g.:
contract { uint256 dummy; string dummy2; function xxx() { (dummy, dummy2) = (1, "haha"); } }
-
EVM memory size calculation is not correct, need fix.
-
- Gas rule implementation for
JOURNAL
commands. - Fix optimizer occasionally delete journal variable issue.
CallStack Data
type InnerTransaction struct {
From common.Address
To common.Address
Data []byte
Value *uint256.Int
Gas *uint256.Int
index uint64
parent *InnerTransaction
children []*InnerTransaction
}
{
"from": "0xaaaaa...", // <-- caller address
"to": "0xbbbbb...", // <-- contract address
"data": "0xaaaaabbbbbcccc..." // <-- abi.encoded
"value": 100000000, // <-- value in wei
"gas": 10000000, // <-- amount
"index": 1, // <-- inner tx index
"parent": { ... } // <-- pointer to parent call
"children": [
{ ... },
{ ... }
]
}
-
Currently call input is still in binary format, need to decode it in Aspect
// add util function let params : ethereum.Value[] = ethereum.decode('0xaaaa....', '{method}({type1},{type2}...)'); // decode with abi let decoder = new ethereum.AbiDecoder(abiJson); let method, params = decoder.decode('0xaaaaa');
Copyright © Artela Network, Inc. All rights reserved.
Licensed under the Apache v2 License.