├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── arb_os ├── accounts.mini ├── arbaddresstable.mini ├── arbaggregator.mini ├── arbbls.mini ├── arbfunctiontable.mini ├── arbgasinfo.mini ├── arbos-upgrade.mexe ├── arbos.mexe ├── arbosTest.mini ├── arbos_before.mexe ├── arbowner.mini ├── arbretryable.mini ├── arbstatistics.mini ├── arbsys.mini ├── blockhash.mini ├── chainParameters.mini ├── constants.json ├── customize_arbos_bridge_versions.mini ├── decompression.mini ├── dummy_version_bridge.mini ├── errorHandler.mini ├── evmCallStack.mini ├── evmOps.mini ├── evmlogs.mini ├── gasAccounting.mini ├── gasrefunds.mini ├── globals.txt ├── inbox.mini ├── main.mini ├── messageBatch.mini ├── messages.mini ├── output.mini ├── precompiles.mini ├── retrybuffer.mini ├── signedTx.mini ├── tracing.mini ├── upgrade.json └── upgrade.toml ├── benchmarks ├── boot.aoslog ├── erc20_100.aoslog ├── erc20_1000.aoslog ├── nulltx_100.aoslog ├── nulltx_1000.aoslog ├── nulltx_batch_100.aoslog ├── nulltx_batch_1000.aoslog └── nulltx_batch_500.aoslog ├── build.rs ├── builtin ├── array.mini ├── arraytest.mini ├── kvs.mini ├── kvstest.mini └── maptest.mini ├── contracts ├── arbos │ ├── builtin │ │ ├── ArbAddressTable.sol │ │ ├── ArbAggregator.sol │ │ ├── ArbBLS.sol │ │ ├── ArbFunctionTable.sol │ │ ├── ArbGasInfo.sol │ │ ├── ArbInfo.sol │ │ ├── ArbOwner.sol │ │ ├── ArbRetryableTx.sol │ │ ├── ArbStatistics.sol │ │ ├── ArbSys.sol │ │ └── ArbosTest.sol │ └── test │ │ ├── Add.sol │ │ ├── BlockNum.sol │ │ ├── Callback.sol │ │ ├── ConstructorSD.sol │ │ ├── Delegator.sol │ │ ├── Destroyer.sol │ │ ├── EvmTests.sol │ │ ├── ExtCodeSizeTest.sol │ │ ├── Fibonacci.sol │ │ ├── MemoryUsage.sol │ │ ├── Migrations.sol │ │ ├── PRConstructor.sol │ │ ├── PaymentChannel.sol │ │ ├── ReverterFactory.sol │ │ ├── SelfDestructor.sol │ │ ├── SingletonFactory.sol │ │ └── Underfunded.sol ├── hardhat.config.js ├── package.json ├── scripts │ └── deploy-test.js └── yarn.lock ├── coverage └── mini-coverage.sh ├── deny.toml ├── doc ├── DataFormats.md ├── MiniLanguageTutorial.md └── TracingData.md ├── evm-tests └── .gitignore ├── iterator.mini ├── logs └── .gitignore ├── looptest ├── bridge2.mini ├── impl2.mini ├── upgrade2.toml ├── upgrade2_base.mexe ├── upgrade2_new.mexe ├── upgrade2_new.mini ├── upgrade2_old.mexe └── upgrade2_old.mini ├── minitests ├── arithmetic.mini ├── basic.mini ├── builtin ├── callgraph │ ├── main.mini │ └── other.mini ├── closure.mini ├── codeblocks.mini ├── codeloadtest.mini ├── constants.json ├── default.mini ├── error-system-test.mini ├── generics │ ├── basic.mini │ ├── closure.mini │ ├── colorful.mini │ ├── func.mini │ ├── nested.mini │ ├── queue.mini │ └── simple.mini ├── globaltest.mini ├── if-else.mini ├── loadertest1.mini ├── loadertest2.mini ├── modexp.mini ├── quick.mini ├── replicas.mini ├── simple-closure.mini ├── stack-safety.mini └── wide-tuples.mini ├── parameters.json ├── replayTests ├── evm_direct_deploy_add.aoslog ├── evm_direct_deploy_and_call_add.aoslog ├── evm_direct_deploy_and_compressed_call_add.aoslog ├── evm_eval_sha256.aoslog ├── evm_test_arbsys.aoslog ├── evm_test_arbsys_direct.aoslog ├── evm_test_create.aoslog ├── evm_test_function_table_access.aoslog ├── evm_xcontract_call_using_batch.aoslog ├── evm_xcontract_call_with_constructors.aoslog └── payment_to_empty_address.aoslog ├── src ├── buffertests.rs ├── compile │ ├── ast.rs │ ├── codegen.rs │ ├── globals.rs │ ├── miniconstants.rs │ ├── mod.rs │ ├── source.rs │ ├── translate.rs │ └── typecheck.rs ├── console.rs ├── contracttemplates.rs ├── evm │ ├── abi.rs │ ├── benchmarks.rs │ ├── bls.rs │ ├── evmtest.rs │ ├── live_code.rs │ ├── mod.rs │ └── preinstalled_contracts.rs ├── gen_code │ └── mod.rs ├── link │ ├── mod.rs │ ├── striplabels.rs │ └── xformcode.rs ├── main.rs ├── mavm.rs ├── mini.lalrpop ├── minitests │ ├── integration.rs │ └── mod.rs ├── optimize │ ├── compute.rs │ ├── effects.rs │ ├── mod.rs │ ├── peephole.rs │ ├── reduce.rs │ └── reorder.rs ├── pos.rs ├── run │ ├── blake2b.rs │ ├── emulator.rs │ ├── mod.rs │ ├── ripemd160port.rs │ └── runtime_env.rs ├── stringtable.rs ├── uint256.rs └── upload.rs ├── stdlib ├── addressSet.mini ├── addressSetTest.mini ├── avmcodebuilder.mini ├── biguint.mini ├── biguinttest.mini ├── bls.mini ├── blstest.mini ├── bufferopcodetest.mini ├── bytearray.mini ├── bytearraytest.mini ├── bytestream.mini ├── codeSegment.mini ├── expandingIntArray.mini ├── expandingIntArrayTest.mini ├── fixedpoint.mini ├── fixedpointtest.mini ├── keccak.mini ├── keccaktest.mini ├── merkletree.mini ├── outputbuffer.mini ├── priorityq.mini ├── priorityqtest.mini ├── queue.mini ├── queuetest.mini ├── random.mini ├── rateEstimator.mini ├── ripemd160.mini ├── ripemd160test.mini ├── rlp.mini ├── rlptest.mini ├── safeMath.mini ├── sha256.mini ├── sha256test.mini ├── stack.mini ├── storageMap.mini └── storageMapTest.mini ├── stdlib2 ├── array.mini ├── arraytest.mini ├── priorityq.mini └── priorityqtest.mini ├── testmap.mini ├── upgradeConfig.toml ├── upgradetests ├── regcopy_new.mexe ├── regcopy_new.mini ├── regcopy_old.mexe ├── regcopy_old.mini ├── upgrade1_new.mexe ├── upgrade1_new.mini ├── upgrade1_old.mexe └── upgrade1_old.mini └── v40-before-to-v41.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | arb_os/*.mao 8 | arb_os/evmJumpTable.mini 9 | builtin/*.mao 10 | builtin/*.mexe 11 | upgradetests/*.mexe 12 | stdlib/*.mao 13 | stdlib/*.mexe 14 | stdlib2/*.mexe 15 | minitests/*.mao 16 | minitests/*.mexe 17 | minitests/*/*.mexe 18 | testlogs/* 19 | arb_os/contractTemplates.mini 20 | .idea 21 | evm-tests/.travis.yml 22 | evm-test-logs/* 23 | **.bkp 24 | **~ 25 | lcov.info 26 | lcov-mini.info 27 | 28 | # Files generated by Hardhat 29 | contracts/artifacts 30 | contracts/cache 31 | contracts/node_modules 32 | contracts/src/types 33 | arb_os/save_bridge_for_debugging.mini 34 | 35 | # Tooling 36 | flamegraph.svg 37 | perf.data 38 | **.dot 39 | 40 | # coverage files 41 | **.all 42 | **.cov 43 | **.partial 44 | 45 | # auto-generated files 46 | .make/ 47 | arb_os/bridge_arbos_versions.mini 48 | arb_os/arbos-upgrade-base.mexe 49 | **.avm 50 | 51 | # user files 52 | rachel/ 53 | emily/ 54 | ed/ 55 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "evm-tests/tests"] 2 | path = evm-tests/tests 3 | url = https://github.com/ethereum/tests.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mini" 3 | version = "0.1.0" 4 | authors = ["Ed Felten "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [build-dependencies] # <-- We added this and everything after! 10 | lalrpop = { version = "0.18.1", features = ["lexer"] } 11 | 12 | [dependencies] 13 | lalrpop-util = "0.18.1" 14 | regex = "1" 15 | rand = { version = "0.8.4", features = ["small_rng"] } 16 | num-bigint = { version = "0.4.2", features = ["rand"] } 17 | num-traits = "0.2.11" 18 | serde = { version = "1.0", features = ["derive", "rc"] } 19 | serde_repr = "0.1.6" 20 | serde_json = { version = "1.0", features = ["unbounded_depth"] } 21 | serde_stacker = "0.1.4" 22 | derivative = "2.2.0" 23 | clap = "3.0.0-beta.2" 24 | bincode = "1.2.1" 25 | crypto-hash = "0.3.4" 26 | hex = "0.4.2" 27 | ethabi = "12.0.0" 28 | keccak-hash = "0.5.1" 29 | ethereum-types = "0.9.2" 30 | bytes = "0.5.5" 31 | im = "15.0.0" 32 | rlp = "0.4.5" 33 | ethers-core = "0.1.3" 34 | ethers-signers = "0.1.3" 35 | keccak="0.1.0" 36 | parity-bn = "0.4.4" 37 | rust-crypto="^0.2" 38 | num-integer = "0.1" 39 | rustc-hex = "2.1.0" 40 | toml = "0.5.8" 41 | rayon = "1.5.1" 42 | petgraph = "0.6.0" 43 | unicode-width = "0.1.9" 44 | parking_lot = "0.11" 45 | lazy_static = "1.4.0" 46 | 47 | [features] 48 | sparse_buffers = [] 49 | 50 | [profile.release] 51 | debug = true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ArbOS and Mini compiler 2 | 3 | ArbOS is the "operating system" that runs at Layer 2 on an Arbitrum chain, to manage the chain's operation, maintain security, isolate contracts from each other, manage contract lifecycles, and account for and charge for resource usage. 4 | 5 | ArbOS is written in the Mini language. The Mini compiler is also in this repo. It compiles programs written in Mini, generating code to run on the Arbitrum Virtual Machine. The compiler is accompanied by an AVM emulator, with associated debugger and profiler. 6 | 7 | To build and test everything, do `make clean` then `make`. 8 | -------------------------------------------------------------------------------- /arb_os/arbbls.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use evmCallStack::EvmCallFrame; 6 | use std::bytearray::ByteArray; 7 | 8 | use chainParameters::chainParams_chainId; 9 | 10 | use evmCallStack::evmCallStack_stackDepth; 11 | use evmCallStack::evmCallStack_topFrame; 12 | use evmCallStack::evmCallStack_setAccount; 13 | use evmCallStack::evmCallStack_getTopFrameMemoryOrDie; 14 | use evmCallStack::evmCallStack_setTopFrameMemory; 15 | use evmCallStack::evmCallStack_callHitError; 16 | 17 | use evmCallStack::evmCallFrame_getCalldata; 18 | use evmCallStack::evmCallFrame_getAccount; 19 | use evmCallStack::evmCallFrame_getCaller; 20 | 21 | use evmOps::evmOp_return; 22 | use evmOps::evmOp_revert_knownCodePc; 23 | use evmOps::evmOp_revertIfStatic; 24 | 25 | use accounts::account_setBlsKey; 26 | use accounts::account_getBlsKey; 27 | 28 | use std::bytearray::bytearray_size; 29 | use std::bytearray::bytearray_get256; 30 | 31 | use std::bls::bls_makeKey; 32 | use std::bls::bls_marshalPublicKey; 33 | 34 | 35 | public view write throw func arbBLS_txcall() { 36 | if let Some(topFrame) = evmCallStack_topFrame() { 37 | let calldata = evmCallFrame_getCalldata(topFrame); 38 | if bytearray_size(calldata) < 4 { 39 | evmOp_revert_knownCodePc(address(const::Address_ArbBLS), 0, 0, 0); 40 | } 41 | let funcCode = getFuncCode(calldata); 42 | if funcCode == 0x375a7c7f { 43 | arbBLS_register(topFrame, calldata); 44 | } else if funcCode == 0x857cdbb8 { 45 | arbBLS_getPublicKey(topFrame, calldata); 46 | } else { 47 | // unrecognized function code 48 | evmOp_revert_knownCodePc(address(const::Address_ArbBLS), 1, 0, 0); 49 | } 50 | } else { 51 | // this shouldn't happen -- should always be called in an EVM tx 52 | evmCallStack_callHitError(18); 53 | } 54 | } 55 | 56 | func getFuncCode(ba: ByteArray) -> uint { 57 | (bytearray_get256(ba, 0) >> 224) 58 | } 59 | 60 | view write throw func arbBLS_register(topFrame: EvmCallFrame, calldata: ByteArray) { 61 | evmOp_revertIfStatic(); 62 | if bytearray_size(calldata) != 4+32*4 { 63 | evmOp_revert_knownCodePc(address(const::Address_ArbBLS), 170, 0, 0); 64 | } 65 | 66 | let x0 = bytearray_get256(calldata, 4); 67 | let x1 = bytearray_get256(calldata, 4+32); 68 | let y0 = bytearray_get256(calldata, 4+32*2); 69 | let y1 = bytearray_get256(calldata, 4+32*3); 70 | let maybeBlsKey = bls_makeKey(x0, x1, y0, y1); 71 | 72 | let caller = evmCallFrame_getCaller(topFrame); 73 | let acct = evmCallFrame_getAccount(topFrame, caller); 74 | if evmCallStack_setAccount(caller, account_setBlsKey(acct, maybeBlsKey)) { 75 | evmOp_return(0, 0); 76 | } else { 77 | evmOp_revert_knownCodePc(address(const::Address_ArbBLS), 171, 0, 0); 78 | } 79 | } 80 | 81 | view write throw func arbBLS_getPublicKey(topFrame: EvmCallFrame, calldata: ByteArray) { // (address) -> (uint, uint, uint, uint) 82 | if bytearray_size(calldata) != 36 { 83 | evmOp_revert_knownCodePc(address(const::Address_ArbBLS), 180, 0, 0); 84 | } 85 | let addr = address(bytearray_get256(calldata, 4)); 86 | let maybeBlsKey = account_getBlsKey(evmCallFrame_getAccount(topFrame, addr)); 87 | 88 | let mem = evmCallStack_getTopFrameMemoryOrDie(); 89 | if let Some(blsKey) = maybeBlsKey { 90 | mem = bls_marshalPublicKey(blsKey, mem, 0); 91 | } else { 92 | evmOp_revert_knownCodePc(address(const::Address_ArbBLS), 101, 0, 0); 93 | } 94 | 95 | if evmCallStack_setTopFrameMemory(mem) { 96 | evmOp_return(0, 4*32); 97 | } else { 98 | evmOp_revert_knownCodePc(address(const::Address_ArbBLS), 182, 0, 0); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /arb_os/arbstatistics.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use accounts::accountStore_getNumAccounts; 6 | use accounts::accountStore_getNumContracts; 7 | 8 | use evmCallStack::EvmCallFrame; 9 | use evmCallStack::evmCallStack_topFrame; 10 | use evmCallStack::evmCallStack_setTopFrameMemory; 11 | use evmCallStack::evmCallFrame_getCalldata; 12 | use evmCallStack::evmCallStack_callHitError; 13 | use evmCallStack::evmCallStack_getAccountStoreInCurrentContext; 14 | 15 | use evmOps::evmOp_return; 16 | use evmOps::evmOp_revert_knownPc; 17 | 18 | use inbox::inbox_currentArbBlockNumber; 19 | 20 | use std::bytearray::ByteArray; 21 | use std::bytearray::bytearray_new; 22 | use std::bytearray::bytearray_size; 23 | use std::bytearray::bytearray_get256; 24 | use std::bytearray::bytearray_set256; 25 | 26 | 27 | public view write throw func arbStatistics_txcall() { 28 | if let Some(topFrame) = evmCallStack_topFrame() { 29 | let calldata = evmCallFrame_getCalldata(topFrame); 30 | if bytearray_size(calldata) < 4 { 31 | evmOp_revert_knownPc(0, 0, 0); 32 | } 33 | let funcCode = (bytearray_get256(calldata, 0) >> 224); 34 | if funcCode == const::funcCode_ArbStatistics_getStats { 35 | arbStatistics_getStats(topFrame, calldata); 36 | } else { 37 | // unrecognized function code 38 | evmOp_revert_knownPc(1, 0, 0); 39 | } 40 | } else { 41 | // this shouldn't happen -- should always be called in an EVM tx 42 | evmCallStack_callHitError(28); 43 | } 44 | } 45 | 46 | // function getStats() external view returns(uint, uint, uint, uint, uint, uint); 47 | view write throw func arbStatistics_getStats(_topFrame: EvmCallFrame, calldata: ByteArray) { 48 | if bytearray_size(calldata) != 4 { 49 | evmOp_revert_knownPc(10, 0, 0); 50 | } 51 | 52 | let mem = bytearray_new(0); 53 | let mem = bytearray_set256(mem, 0, inbox_currentArbBlockNumber()); 54 | let mem = bytearray_set256(mem, 32, accountStore_getNumAccounts(evmCallStack_getAccountStoreInCurrentContext())); 55 | let mem = bytearray_set256(mem, 2*32, arbStats.storage); 56 | let mem = bytearray_set256(mem, 3*32, arbStats.arbGasUsed); 57 | let mem = bytearray_set256(mem, 4*32, arbStats.numTxReceipts); 58 | let mem = bytearray_set256(mem, 5*32, accountStore_getNumContracts(evmCallStack_getAccountStoreInCurrentContext())); 59 | 60 | if evmCallStack_setTopFrameMemory(mem) { 61 | evmOp_return(0, bytearray_size(mem)); 62 | } else { 63 | evmOp_revert_knownPc(12, 0, 0); 64 | } 65 | } 66 | 67 | type ArbStatistics = struct { 68 | storage: uint, 69 | arbGasUsed: uint, 70 | numTxReceipts: uint, 71 | }; 72 | 73 | var arbStats: ArbStatistics; 74 | 75 | public write func arbStatistics_init() { 76 | arbStats = struct { 77 | storage: 0, 78 | arbGasUsed: 0, 79 | numTxReceipts: 0, 80 | }; 81 | } 82 | 83 | public view write func arbStatistics_addToStorage(delta: uint) { 84 | arbStats = arbStats with { 85 | storage: arbStats.storage + delta 86 | }; 87 | } 88 | 89 | public view write func arbStatistics_addToGasUsed(gas: uint) { 90 | arbStats = arbStats with { 91 | arbGasUsed: arbStats.arbGasUsed + gas 92 | }; 93 | } 94 | 95 | public view write func arbStatistics_addTxReceipt() { 96 | arbStats = arbStats with { 97 | numTxReceipts: arbStats.numTxReceipts + 1 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /arb_os/blockhash.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | var blockhashes: struct { 6 | latestBlockNum: uint, 7 | oldHashes: [256]bytes32, 8 | }; 9 | 10 | public write func blockhash_init() { 11 | blockhashes = struct { 12 | latestBlockNum: 0, 13 | oldHashes: newfixedarray(256, bytes32(0)), 14 | }; 15 | } 16 | 17 | public view func blockhash_getForBlock(blockNum: uint) -> option { 18 | if (blockNum > blockhashes.latestBlockNum) || (blockNum < blockhashes.latestBlockNum-255) { 19 | None 20 | } else { 21 | Some(blockhashes.oldHashes[blockNum % 256]) 22 | } 23 | } 24 | 25 | public view write func blockhash_submitHash(ethBlockNum: uint, inboxAccumHash: bytes32) { 26 | let b = if ethBlockNum > blockhashes.latestBlockNum + 255 { 27 | ethBlockNum - 255 28 | } else { 29 | blockhashes.latestBlockNum + 1 30 | }; 31 | while b <= ethBlockNum { //FORLOOP 32 | set blockhashes.oldHashes[b%256] = hash(bytes32(b), inboxAccumHash); 33 | b = b + 1; 34 | } 35 | set blockhashes.latestBlockNum = ethBlockNum; 36 | } 37 | -------------------------------------------------------------------------------- /arb_os/customize_arbos_bridge_versions.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021-2022, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use bridge_arbos_versions::GlobalsBeforeUpgrade; 6 | 7 | public func set_globalChainParameters_onUpgrade(oldGlobals: GlobalsBeforeUpgrade) -> map { 8 | oldGlobals.globalChainParameters 9 | } 10 | -------------------------------------------------------------------------------- /arb_os/dummy_version_bridge.mini: -------------------------------------------------------------------------------- 1 | 2 | // This file is used for bootstrapping the upgrade build process. Don't edit it unless you know what you're doing. 3 | 4 | type GlobalsBeforeUpgrade = struct { 5 | unused: uint, 6 | }; 7 | 8 | type GlobalsAfterUpgrade = struct { 9 | unused: uint, 10 | }; 11 | 12 | public func remapGlobalsForUpgrade(_input_globals: GlobalsBeforeUpgrade) -> (GlobalsAfterUpgrade, uint) { 13 | (struct { 14 | unused: 0, 15 | }, 0) 16 | } 17 | -------------------------------------------------------------------------------- /arb_os/evmlogs.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | // This structure accumulates the EVM log items emitted by a transaction. 6 | // It uses the same representation as the Arbitrum protocol, so this structure can be emitted 7 | // directly without needing any more encoding. 8 | 9 | type EvmLogs = struct { 10 | last: any, 11 | rest: EvmLogs, 12 | }; 13 | 14 | public func evmlogs_empty() -> EvmLogs { // make an empty log-sequence 15 | unsafecast(()) 16 | } 17 | 18 | func evmlogs_isEmpty(logs: EvmLogs) -> bool { 19 | logs == unsafecast(()) 20 | } 21 | 22 | public func evmlogs_appendAny(logs: EvmLogs, item: any) -> EvmLogs { 23 | // Append an item to the end of a log-sequence, returning the updated log-sequence. 24 | struct { 25 | last: item, 26 | rest: logs, 27 | } 28 | } 29 | 30 | public func evmlogs_numLogs(logs: EvmLogs) -> uint { 31 | let ret = 0; 32 | loop { 33 | if evmlogs_isEmpty(logs) { 34 | return ret; 35 | } else { 36 | ret = ret + 1; 37 | logs = logs.rest; 38 | } 39 | } 40 | } 41 | 42 | type EvmLogMapFunc = view write func(any, any) -> (any, any); // (log, state) -> (updatedLog, updatedState) 43 | 44 | public view write func evmlogs_forall(logs: EvmLogs, mapFunc: EvmLogMapFunc, state: any) -> (EvmLogs, any) { 45 | // reverse the order of the logs, so we can apply the map to them in order 46 | let revLogs = evmlogs_empty(); 47 | while ! evmlogs_isEmpty(logs) { 48 | revLogs = evmlogs_appendAny(revLogs, logs.last); 49 | logs = logs.rest; 50 | } 51 | 52 | // now reverse the list again, applying the map to each item 53 | // list ends up in its original order, but with the map having been applied to each log 54 | while ! evmlogs_isEmpty(revLogs) { 55 | let (updatedLog, ustate) = mapFunc(revLogs.last, state); 56 | state = ustate; 57 | logs = evmlogs_appendAny(logs, updatedLog); 58 | revLogs = revLogs.rest; 59 | } 60 | 61 | (logs, state) 62 | } 63 | -------------------------------------------------------------------------------- /arb_os/gasrefunds.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use core::kvs::Kvs; 6 | use core::kvs::builtin_kvsForall; 7 | 8 | use accounts::AccountStore; 9 | use accounts::accountStore_get; 10 | use accounts::account_getStorageCell; 11 | 12 | 13 | type GasRefundTracker = struct { 14 | allocations: map<(address, uint), uint>, 15 | }; 16 | 17 | public func gasRefundTracker_new() -> GasRefundTracker { 18 | struct { 19 | allocations: newmap<(address, uint), uint>, 20 | } 21 | } 22 | 23 | public func gasRefundTracker_add( 24 | tracker: GasRefundTracker, 25 | addr: address, 26 | storageIndex: uint, 27 | ) -> GasRefundTracker { 28 | set tracker.allocations[(addr, storageIndex)] = if let Some(old) = tracker.allocations[(addr, storageIndex)] { 29 | old + 1 30 | } else { 31 | 1 32 | }; 33 | tracker 34 | } 35 | 36 | public throw func gasRefundTracker_allocationsToRefund( 37 | tracker: GasRefundTracker, 38 | acctStore: AccountStore, 39 | ) -> uint { 40 | unsafecast( 41 | builtin_kvsForall( 42 | unsafecast(tracker.allocations), 43 | closure(rawKey: any, rawValue: any, rawState: any) -> any { 44 | let (addr, storageIndex) = unsafecast<(address, uint)>(rawKey); 45 | let allocs = unsafecast(rawValue); 46 | let allocsSoFar = unsafecast(rawState); 47 | let newCredit = if let Some(val) = account_getStorageCell( 48 | accountStore_get(acctStore, addr), 49 | storageIndex, 50 | ) { 51 | if val == 0 { 52 | allocs 53 | } else if allocs > 1 { 54 | allocs - 1 55 | } else { 56 | 0 57 | } 58 | } else { 59 | 0 60 | }; 61 | return allocsSoFar + newCredit; 62 | }, 63 | 0, 64 | ) 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /arb_os/globals.txt: -------------------------------------------------------------------------------- 1 | __fixedLocationGlobal 2 | globalCallStack 3 | gasAccountingInfo 4 | globalCurrentTxRequest 5 | globalInbox 6 | globalOutbox 7 | globalBlockReceiptData 8 | globalInputQueue 9 | evmOpJumpTable 10 | globalIndexedAddressTable 11 | globalChainParameters 12 | blockhashes 13 | arbStats 14 | globalCurrentRetryableRequest 15 | globalAccountStore 16 | returnFromCallNestingDepth 17 | logsQueuedForEndOfBlock 18 | globalL1GasPriceEstimator 19 | addressRemapExceptions 20 | chainOwners 21 | codeSegGlobals 22 | inErrorHandler 23 | upgradeInProgress 24 | previousArbosUpgradeHash 25 | -------------------------------------------------------------------------------- /arb_os/signedTx.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::rlp::rlp_encodeAndHashMessageInfoForSignature; 6 | use std::rlp::SignedTx; 7 | 8 | use chainParameters::chainParams_chainId; 9 | 10 | 11 | public view throw func recoverSigner(tx: SignedTx) -> option
{ 12 | let rlpHashForSig = if (tx.v == 27) || (tx.v == 28) { 13 | rlp_encodeAndHashMessageInfoForSignature(tx, None) // non-EIP155 signature 14 | } else { 15 | rlp_encodeAndHashMessageInfoForSignature(tx, Some(chainParams_chainId())) // EIP155 signature 16 | }; 17 | 18 | let signer = asm( 19 | tx.r, 20 | tx.s, 21 | 1 - (tx.v % 2), 22 | rlpHashForSig, 23 | ) address { ecrecover }; 24 | 25 | if signer == address(0) { 26 | None 27 | } else { 28 | Some(signer) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /arb_os/tracing.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use output::outbox_getEvmTracer; 6 | use output::outbox_setEvmTracer; 7 | 8 | use std::bytearray::ByteArray; 9 | use std::bytearray::bytearray_new; 10 | 11 | use std::stack::Stack; 12 | use std::stack::stack_new; 13 | use std::stack::stack_push; 14 | use std::stack::stack_pop; 15 | 16 | 17 | type EvmTracer = struct { 18 | recordStack: Stack, 19 | }; 20 | 21 | public view write func evmTracer_clear() { 22 | outbox_setEvmTracer(evmTracer_new()); 23 | } 24 | 25 | public func evmTracer_new() -> EvmTracer { 26 | struct { 27 | recordStack: stack_new(), 28 | } 29 | } 30 | 31 | public view write func evmTracer_emit() { 32 | let val = any(()); 33 | let theStack = outbox_getEvmTracer().recordStack; 34 | loop { 35 | if let Some(res) = stack_pop(theStack) { 36 | let (*theStack, item) = res; 37 | val = (item, val); 38 | } else { 39 | debug((20000, val)); 40 | return; 41 | } 42 | }; 43 | } 44 | 45 | public view write func evmTracer_emitAndClear() { 46 | evmTracer_emit(); 47 | evmTracer_clear(); 48 | } 49 | 50 | public view write func evmTracer_push(typecode: uint, item: any) { 51 | let oldTracer = outbox_getEvmTracer(); 52 | outbox_setEvmTracer(oldTracer with { 53 | recordStack: stack_push(oldTracer.recordStack, (typecode, item)) 54 | }); 55 | } 56 | 57 | public view write func evmTracer_pushCall( 58 | callType: uint, 59 | calldata: ByteArray, 60 | callvalue: uint, 61 | from: address, 62 | to: address, 63 | gas: uint, 64 | gasPrice: uint, 65 | ) { 66 | evmTracer_push( 67 | const::TraceEvent_call, 68 | (callType, calldata, callvalue, from, to, gas, gasPrice, ~0), 69 | ); 70 | } 71 | 72 | public view write func evmTracer_pushReturnRevert( 73 | resultCode: uint, 74 | returndata: ByteArray, 75 | gasUsed: uint, 76 | ) { 77 | evmTracer_push( 78 | const::TraceEvent_returnOrRevert, 79 | (resultCode, returndata, gasUsed, ~0), 80 | ); 81 | } 82 | 83 | public view write func evmTracer_pushCallAndResultNoExecution( 84 | callType: uint, 85 | calldata: ByteArray, 86 | callvalue: uint, 87 | from: address, 88 | to: address, 89 | gas: uint, 90 | gasPrice: uint, 91 | resultCode: uint, 92 | ) { 93 | evmTracer_pushCall(callType, calldata, callvalue, from, to, gas, gasPrice); 94 | evmTracer_pushReturnRevert(resultCode, bytearray_new(0), 0); 95 | } 96 | 97 | public view write func evmTracer_pushCreate( 98 | code: ByteArray, 99 | addr: address, 100 | ) { 101 | evmTracer_push( 102 | const::TraceEvent_create, 103 | (code, addr, ~0), 104 | ); 105 | } 106 | 107 | public view write func evmTracer_pushCreate2( 108 | code: ByteArray, 109 | currentAddr: address, 110 | salt: uint, 111 | createdAddr: address, 112 | ) { 113 | evmTracer_push( 114 | const::TraceEvent_create2, 115 | (code, currentAddr, salt, createdAddr, ~0), 116 | ); 117 | } 118 | -------------------------------------------------------------------------------- /arb_os/upgrade.toml: -------------------------------------------------------------------------------- 1 | data = ["globalChainParameters"] -------------------------------------------------------------------------------- /benchmarks/boot.aoslog: -------------------------------------------------------------------------------- 1 | {"format_version":1,"inbox":[{"Tuple":[{"Int":"4"},{"Int":"186a0"},{"Int":"989680"},{"Int":"0"},{"Int":"0"},{"Int":"77359400"},{"Int":"40"},{"Buffer":"625a539a292ffb7be735d7db3f6b9b9c88b9ced262ae58163dfb75ba308551f2000000000000000000000000000000000000000000000000000000000000a4b1"}]}],"logs":[{"Tuple":[{"Int":"1"},{"Int":"0"},{"Int":"0"},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"1"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"112a8800"}]},{"Int":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},{"Int":"0"}]}],"sends":[],"total_gas":837688} 2 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | let mut config = lalrpop::Configuration::new(); 5 | config.emit_rerun_directives(true); 6 | config.process_current_dir().unwrap(); 7 | } 8 | -------------------------------------------------------------------------------- /builtin/kvstest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use core::kvs::Kvs; 6 | use core::kvs::builtin_kvsNew; 7 | use core::kvs::builtin_kvsGet; 8 | use core::kvs::builtin_kvsSet; 9 | use core::kvs::builtin_kvsHasKey; 10 | use core::kvs::builtin_kvsDelete; 11 | use core::kvs::builtin_kvsForall; 12 | use core::kvs::builtin_kvsSize; 13 | 14 | write throw func main() { 15 | asm(tests().1) { log }; 16 | } 17 | 18 | throw func tests() -> (uint, buffer) { 19 | let s = builtin_kvsNew(); 20 | if builtin_kvsGet(s, 17) != None { 21 | return "new map isn't empty"; 22 | } 23 | s = builtin_kvsDelete(s, 17); 24 | if builtin_kvsSize(s) != 0 { 25 | return "map delete doesn't preserve size"; 26 | } 27 | 28 | s = builtin_kvsNew(); 29 | s = builtin_kvsSet(s, 42, 42); 30 | if builtin_kvsGet(s, 42) != Some(unsafecast(42)) { 31 | return "map set-get doesn't work"; 32 | } 33 | if builtin_kvsSize(s) != 1 { 34 | return "map set doesn't increase size"; 35 | } 36 | s = builtin_kvsNew(); 37 | s = builtin_kvsSet(s, 42, 43); 38 | s = builtin_kvsSet(s, 55, 56); 39 | s = builtin_kvsSet(s, 42, 99); 40 | if builtin_kvsGet(s, 42) != Some(unsafecast(99)) { 41 | return "map overwrite doesn't work"; 42 | } 43 | if builtin_kvsSize(s) != 2 { 44 | return "map overwrite doesn't preserve size"; 45 | } 46 | 47 | s = builtin_kvsNew(); 48 | s = builtin_kvsSet(s, 42, 43); 49 | s = builtin_kvsSet(s, 55, 56); 50 | s = builtin_kvsSet(s, 42, 99); 51 | if builtin_kvsGet(s, 55) != Some(unsafecast(56)) { 52 | return "map overwrite affects other keys"; 53 | } 54 | if builtin_kvsSize(s) != 2 { 55 | return "map overwrite doesn't preserve size"; 56 | } 57 | 58 | s = builtin_kvsNew(); 59 | let i = 0; 60 | while i < 41 { 61 | s = builtin_kvsSet(s, i, 1000+i); 62 | i = i+1; 63 | } 64 | if builtin_kvsGet(s, 17) != Some(unsafecast(1017)) { 65 | return "writing many times corrupted map"; 66 | } 67 | if builtin_kvsSize(s) != 41 { 68 | return "writing many times corrupted size"; 69 | } 70 | 71 | s = builtin_kvsNew(); 72 | let i = 0; 73 | while i < 27 { 74 | s = builtin_kvsSet(s, i, 1000+i); 75 | i = i+1; 76 | if !builtin_kvsHasKey(s, i-1) { 77 | return "key existence check is wrong"; 78 | } 79 | } 80 | s = builtin_kvsDelete(s, 17); 81 | if builtin_kvsGet(s, 17) != None { 82 | return "map deletion didn't remove value"; 83 | } 84 | if builtin_kvsHasKey(s, 17) { 85 | return "map deletion looks like it didn't remove value"; 86 | } 87 | s = builtin_kvsDelete(s, 17); 88 | s = builtin_kvsDelete(s, 1000); 89 | if builtin_kvsSize(s) != 26 { 90 | return "map delete corrupts size"; 91 | } 92 | 93 | s = builtin_kvsNew(); 94 | s = builtin_kvsSet(s, 42, 43); 95 | s = builtin_kvsSet(s, 55, 56); 96 | s = builtin_kvsSet(s, 42, 99); 97 | if builtin_kvsGet(s, 42) != Some(unsafecast(99)) { 98 | return "map overwrite doesn't work #2"; 99 | } 100 | if builtin_kvsSize(s) != 2 { 101 | return "map overwrite doesn't preserve size #2"; 102 | } 103 | 104 | s = builtin_kvsNew(); 105 | s = builtin_kvsSet(s, 42, 43); 106 | s = builtin_kvsSet(s, 55, 56); 107 | s = builtin_kvsSet(s, 42, 99); 108 | if builtin_kvsGet(s, 3) != None { 109 | return "map overwrite affects other entries"; 110 | } 111 | if builtin_kvsSize(s) != 2 { 112 | return "getting nonexistent value changed size after overwrite"; 113 | } 114 | 115 | s = builtin_kvsNew(); 116 | s = builtin_kvsSet(s, 42, 43); 117 | s = builtin_kvsSet(s, 55, 56); 118 | s = builtin_kvsSet(s, 42, 99); 119 | let rawResult = builtin_kvsForall(s, sumForKvsIterator, 0); 120 | if unsafecast(rawResult) != 155 { 121 | return "map forall did not compute correctly"; 122 | } 123 | if builtin_kvsSize(s) != 2 { 124 | return "map forall did not preserve size"; 125 | } 126 | 127 | // regression test for bug #73 128 | s = builtin_kvsNew(); 129 | s = builtin_kvsSet(s, 42, 43); 130 | s = builtin_kvsSet(s, 55, 56); 131 | s = builtin_kvsDelete(s, 42); 132 | s = builtin_kvsSet(s, 55, 100); 133 | s = builtin_kvsDelete(s, 55); 134 | if builtin_kvsGet(s, 55) != None { 135 | return "bug #73 is back (data)"; 136 | } 137 | if builtin_kvsSize(s) != 0 { 138 | return "bug #73 is back (size)"; 139 | } 140 | 141 | "" 142 | } 143 | 144 | func sumForKvsIterator(_key: any, value: any, state: any) -> any { 145 | unsafecast(value) + unsafecast(state) 146 | } 147 | -------------------------------------------------------------------------------- /builtin/maptest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | write func main() { 6 | 7 | if let Some(issue) = test_1() { 8 | asm(issue.1) { log }; 9 | } else if let Some(issue) = test_2() { 10 | asm(issue.1) { log }; 11 | } else { 12 | asm("".1) { log }; 13 | } 14 | } 15 | 16 | func test_1() -> option { 17 | 18 | let m = newmap<(uint, uint), uint>; 19 | if m[(17, 18)] != None { 20 | return Some("map<(uint, uint), uint> isn't empty"); 21 | } 22 | 23 | let m = newmap; 24 | if m[17] != None { 25 | return Some("map isn't empty"); 26 | } 27 | 28 | let m = newmap<(uint, uint), uint>; 29 | m = m with { [(999, 321)] = 42 }; 30 | if m[(999, 321)] != Some(42) { 31 | return Some("map assignment is wrong"); 32 | } 33 | 34 | m = newmap<(uint, uint), uint>; 35 | m = m with { [(999, 321)] = 42 }; 36 | m = m with { [( 0, 0)] = 73 }; 37 | if m[(999, 321)] != Some(42) { 38 | return Some("map assignment is inconsistent"); 39 | } 40 | 41 | m = newmap<(uint, uint), uint>; 42 | m = m with { [(999, 321)] = 42 }; 43 | m = m with { [( 0, 0)] = 73 }; 44 | m = m with { [(999, 321)] = 13 }; 45 | m = m with { [( 0, 0)] = 173 }; 46 | if m[(999, 321)] != Some(13) { 47 | return Some("map update is incorrect"); 48 | } 49 | 50 | None 51 | } 52 | 53 | 54 | func test_2() -> option { 55 | 56 | let m = newmap<(uint, uint), uint>; 57 | set m[(999, 321)] = 42; 58 | if m[(999, 321)] != Some(42) { 59 | return Some("map assignment is wrong"); 60 | } 61 | 62 | m = newmap<(uint, uint), uint>; 63 | set m[(999, 321)] = 42; 64 | set m[( 0, 0)] = 73; 65 | if m[(999, 321)] != Some(42) { 66 | return Some("map assignment is inconsistent"); 67 | } 68 | 69 | m = newmap<(uint, uint), uint>; 70 | set m[(999, 321)] = 42; 71 | set m[( 0, 0)] = 73; 72 | set m[(999, 321)] = 13; 73 | set m[( 0, 0)] = 173; 74 | if m[(999, 321)] != Some(13) { 75 | return Some("map update is incorrect"); 76 | } 77 | 78 | None 79 | } 80 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbAddressTable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | /** @title Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000066. 4 | * Allows registering / retrieving addresses at uint indices, saving calldata. 5 | */ 6 | interface ArbAddressTable { 7 | /** 8 | * @notice Register an address in the address table 9 | * @param addr address to register 10 | * @return index of the address (existing index, or newly created index if not already registered) 11 | */ 12 | function register(address addr) external returns(uint); 13 | 14 | /** 15 | * @param addr address to lookup 16 | * @return index of an address in the address table (revert if address isn't in the table) 17 | */ 18 | function lookup(address addr) external view returns(uint); 19 | 20 | /** 21 | * @notice Check whether an address exists in the address table 22 | * @param addr address to check for presence in table 23 | * @return true if address is in table 24 | */ 25 | function addressExists(address addr) external view returns(bool); 26 | 27 | /** 28 | * @return size of address table (= first unused index) 29 | */ 30 | function size() external view returns(uint); 31 | 32 | /** 33 | * @param index index to lookup address 34 | * @return address at a given index in address table (revert if index is beyond end of table) 35 | */ 36 | function lookupIndex(uint index) external view returns(address); 37 | 38 | /** 39 | * @notice read a compressed address from a bytes buffer 40 | * @param buf bytes buffer containing an address 41 | * @param offset offset of target address 42 | * @return resulting address and updated offset into the buffer (revert if buffer is too short) 43 | */ 44 | function decompress(bytes calldata buf, uint offset) external pure returns(address, uint); 45 | 46 | /** 47 | * @notice compress an address and return the result, possibly modifying the compression/decompression state 48 | * @param addr address to compress 49 | * @return compressed address bytes 50 | */ 51 | function compress(address addr) external returns(bytes memory); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbAggregator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | interface ArbAggregator { 4 | // Get the preferred aggregator for an address. 5 | // Returns (preferredAggregatorAddress, isDefault) 6 | // isDefault is true if addr is set to prefer the default aggregator 7 | function getPreferredAggregator(address addr) external view returns (address, bool); 8 | 9 | // Set the caller's preferred aggregator. 10 | // If prefAgg is zero, this sets the caller to prefer the default aggregator 11 | function setPreferredAggregator(address prefAgg) external; 12 | 13 | // Get default aggregator. 14 | function getDefaultAggregator() external view returns (address); 15 | 16 | // Set the preferred aggregator. 17 | // Reverts unless called by the chain owner or the current default aggregator. 18 | function setDefaultAggregator(address newDefault) external; 19 | 20 | // Get the address where fees to aggregator are sent. 21 | // This will often but not always be the same as the aggregator's address. 22 | function getFeeCollector(address aggregator) external view returns (address); 23 | 24 | // Set the address where fees to aggregator are sent. 25 | // This reverts unless called by the address that would be returned by getFeeCollector(aggregator), 26 | // or by the chain owner. 27 | function setFeeCollector(address aggregator, address newFeeCollector) external; 28 | 29 | // Get the tx base fee (in approximate L1 gas) for aggregator 30 | function getTxBaseFee(address aggregator) external view returns (uint); 31 | 32 | // Set the tx base fee (in approximate L1 gas) for aggregator 33 | // Revert unless called by aggregator or the chain owner 34 | // Revert if feeInL1Gas is outside the chain's allowed bounds 35 | function setTxBaseFee(address aggregator, uint feeInL1Gas) external; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbBLS.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | //This functionality has been disabled for now. Calls to these methods will revert. 4 | interface ArbBLS { 5 | // Associate a BLS public key with the caller's address 6 | function register(uint x0, uint x1, uint y0, uint y1) external; 7 | 8 | // Get the BLS public key associated with an address (revert if there isn't one) 9 | function getPublicKey(address addr) external view returns (uint, uint, uint, uint); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbFunctionTable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | interface ArbFunctionTable { 4 | // Upload a serialized function table and associate it with the caller's address 5 | // If caller already had a function table, this will overwrite the old one 6 | // Revert if buf is mal-formatted 7 | // (Caller will typically be an aggregator) 8 | function upload(bytes calldata buf) external; 9 | 10 | // Get the size of addr's function table; revert if addr doesn't have a function table 11 | function size(address addr) external view returns(uint); 12 | 13 | // Get the entry from addr's function table, at index; revert if addr has no table or index out of bounds 14 | // Returns (functionCode, isPayable, gasLimit) 15 | function get(address addr, uint index) external view returns(uint, bool, uint); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbGasInfo.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | interface ArbGasInfo { 4 | // return gas prices in wei, assuming the specified aggregator is used 5 | // ( 6 | // per L2 tx, 7 | // per L1 calldata unit, (zero byte = 4 units, nonzero byte = 16 units) 8 | // per storage allocation, 9 | // per ArbGas base, 10 | // per ArbGas congestion, 11 | // per ArbGas total 12 | // ) 13 | function getPricesInWeiWithAggregator(address aggregator) external view returns (uint, uint, uint, uint, uint, uint); 14 | 15 | // return gas prices in wei, as described above, assuming the caller's preferred aggregator is used 16 | // if the caller hasn't specified a preferred aggregator, the default aggregator is assumed 17 | function getPricesInWei() external view returns (uint, uint, uint, uint, uint, uint); 18 | 19 | // return prices in ArbGas (per L2 tx, per L1 calldata unit, per storage allocation), 20 | // assuming the specified aggregator is used 21 | function getPricesInArbGasWithAggregator(address aggregator) external view returns (uint, uint, uint); 22 | 23 | // return gas prices in ArbGas, as described above, assuming the caller's preferred aggregator is used 24 | // if the caller hasn't specified a preferred aggregator, the default aggregator is assumed 25 | function getPricesInArbGas() external view returns (uint, uint, uint); 26 | 27 | // return gas accounting parameters (speedLimitPerSecond, gasPoolMax, maxTxGasLimit) 28 | function getGasAccountingParams() external view returns (uint, uint, uint); 29 | 30 | // get ArbOS's estimate of the L1 gas price in wei 31 | function getL1GasPriceEstimate() external view returns(uint); 32 | 33 | // set ArbOS's estimate of the L1 gas price in wei 34 | // reverts unless called by chain owner or designated gas oracle (if any) 35 | function setL1GasPriceEstimate(uint priceInWei) external; 36 | 37 | // get L1 gas fees paid by the current transaction (txBaseFeeWei, calldataFeeWei) 38 | function getCurrentTxL1GasFees() external view returns(uint); 39 | } 40 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbInfo.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | pragma solidity >=0.4.21 <0.9.0; 18 | 19 | contract ArbInfo { 20 | function getBalance(address account) external view returns (uint256) { 21 | return account.balance; 22 | } 23 | 24 | function getCode(address account) external view returns (bytes memory) { 25 | uint256 size; 26 | assembly { 27 | size := extcodesize(account) 28 | } 29 | bytes memory code = new bytes(size); 30 | assembly { 31 | extcodecopy(account, add(code, 0x20), 0, size) 32 | } 33 | return code; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbOwner.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | interface ArbOwner { 4 | // Support actions that can be taken by the chain's owner. 5 | // All methods will revert, unless the caller is the chain's owner. 6 | 7 | function addToReserveFunds() external payable; 8 | 9 | function setFairGasPriceSender(address addr, bool isFairGasPriceSender) external; 10 | function isFairGasPriceSender(address addr) external view returns(bool); 11 | function getAllFairGasPriceSenders() external view returns(bytes memory); 12 | 13 | // DEPRECATED: use ArbGasInfo.setL1GasPriceEstimate(priceInGwei * 1000000000) instead 14 | function setL1GasPriceEstimate(uint priceInGwei) external; 15 | 16 | // Deploy a contract on the chain 17 | // The contract is deployed as if it was submitted by deemedSender with deemedNonce 18 | // Reverts if there is already a contract at that address 19 | // Returns the address of the deployed contract 20 | function deployContract(bytes calldata constructorData, address deemedSender, uint deemedNonce) external payable returns(address); 21 | 22 | // To upgrade ArbOS, the owner calls startArbosUpgrade or startArbosUpgradeWithCheck, 23 | // then calls continueArbosUpgrade one or more times to upload 24 | // the code to be installed as the upgrade, then calls finishArbosUpgrade to complete the upgrade and start executing the new code. 25 | // startCodeUploadWithCheck will revert unless oldCodeHash equals either zero or the hash of the last ArbOS upgrade 26 | function startCodeUpload() external; 27 | function startCodeUploadWithCheck(bytes32 oldCodeHash) external; 28 | function continueCodeUpload(bytes calldata marshalledCode) external; 29 | function getUploadedCodeHash() external view returns(bytes32); 30 | 31 | // Install the currently uploaded code as an ArbOS upgrade. 32 | // Revert if the hash of the uploaded code bytes does not equal newCodeHash 33 | // Revert if (oldCodeHash != 0) && (oldCodeHash != [hash of code bytes from the previous ArbOS upgrade] 34 | function finishCodeUploadAsArbosUpgrade(bytes32 newCodeHash, bytes32 oldCodeHash) external; 35 | 36 | // Get the code hash of the last upgrade that was installed, or zero if there hasn't been an upgrade on this chain 37 | function getLastUpgradeHash() external view returns(bytes32); 38 | 39 | // Get and set chain parameters 40 | function getChainParameter(bytes32 which) external view returns(uint); 41 | function setChainParameter(bytes32 which, uint value) external; // reverts if param doesn't already exist 42 | function createChainParameter(bytes32 which, uint value) external; // sets param, even if it didn't already exist 43 | function serializeAllParameters() external view returns(bytes memory); 44 | 45 | // Manage the set of allowed senders 46 | // address 0 and the chain owner are always allowed to send, even if not on the list 47 | function allowAllSenders() external; 48 | function allowOnlyOwnerToSend() external; 49 | function isAllowedSender(address addr) external view returns(bool); 50 | function addAllowedSender(address addr) external; 51 | function removeAllowedSender(address addr) external; 52 | function getAllAllowedSenders() external view returns(bytes memory); // reverts if all or nearly all senders are allowed 53 | 54 | // Manage the set of chain owners 55 | function addChainOwner(address newOwner) external; 56 | function removeChainOwner(address ownerToRemove) external; // revert if ownerToRemove is not an owner 57 | function isChainOwner(address addr) external view returns(bool); 58 | function getAllChainOwners() external view returns(bytes memory); 59 | 60 | // Manage exceptions to L1->L2 address remapping 61 | function addMappingException(uint from, uint to) external; 62 | function removeMappingException(uint from, uint to) external; 63 | function isMappingException(uint from, uint to) external view returns(bool); 64 | function getAllMappingExceptions() external view returns (bytes memory); 65 | 66 | function getTotalOfEthBalances() external view returns(uint); 67 | } 68 | 69 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbRetryableTx.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity >=0.4.21 <0.9.0; 3 | 4 | /** 5 | * @title precompiled contract in every Arbitrum chain for retryable transaction related data retrieval and interactions. Exists at 0x000000000000000000000000000000000000006E 6 | */ 7 | interface ArbRetryableTx { 8 | 9 | /** 10 | * @notice Redeem a redeemable tx. 11 | * Revert if called by an L2 contract, or if userTxHash does not exist, or if userTxHash reverts. 12 | * If this returns, userTxHash has been completed and is no longer available for redemption. 13 | * If this reverts, userTxHash is still available for redemption (until it times out or is canceled). 14 | * @param userTxHash unique identifier of retryable message: keccak256(keccak256(ArbchainId, inbox-sequence-number), uint(0) ) 15 | */ 16 | function redeem(bytes32 userTxHash) external; 17 | 18 | /** 19 | * @notice Return the minimum lifetime of redeemable txn. 20 | * @return lifetime in seconds 21 | */ 22 | function getLifetime() external view returns(uint); 23 | 24 | /** 25 | * @notice Return the timestamp when userTxHash will age out, or zero if userTxHash does not exist. 26 | * The timestamp could be in the past, because aged-out tickets might not be discarded immediately. 27 | * @param userTxHash unique ticket identifier 28 | * @return timestamp for ticket's deadline 29 | */ 30 | function getTimeout(bytes32 userTxHash) external view returns(uint); 31 | 32 | /** 33 | * @notice Return the price, in wei, of submitting a new retryable tx with a given calldata size. 34 | * @param calldataSize call data size to get price of (in wei) 35 | * @return (price, nextUpdateTimestamp). Price is guaranteed not to change until nextUpdateTimestamp. 36 | */ 37 | function getSubmissionPrice(uint calldataSize) external view returns (uint, uint); 38 | 39 | /** 40 | * @notice Return the price, in wei, of extending the lifetime of userTxHash by an additional lifetime period. Revert if userTxHash doesn't exist. 41 | * @param userTxHash unique ticket identifier 42 | * @return (price, nextUpdateTimestamp). Price is guaranteed not to change until nextUpdateTimestamp. 43 | */ 44 | function getKeepalivePrice(bytes32 userTxHash) external view returns(uint, uint); 45 | 46 | /** 47 | @notice Deposits callvalue into the sender's L2 account, then adds one lifetime period to the life of userTxHash. 48 | * If successful, emits LifetimeExtended event. 49 | * Revert if userTxHash does not exist, or if the timeout of userTxHash is already at least one lifetime period in the future, or if the sender has insufficient funds (after the deposit). 50 | * @param userTxHash unique ticket identifier 51 | * @return New timeout of userTxHash. 52 | */ 53 | function keepalive(bytes32 userTxHash) external payable returns(uint); 54 | 55 | /** 56 | * @notice Return the beneficiary of userTxHash. 57 | * Revert if userTxHash doesn't exist. 58 | * @param userTxHash unique ticket identifier 59 | * @return address of beneficiary for ticket 60 | */ 61 | function getBeneficiary(bytes32 userTxHash) external view returns (address); 62 | 63 | /** 64 | * @notice Cancel userTxHash and refund its callvalue to its beneficiary. 65 | * Revert if userTxHash doesn't exist, or if called by anyone other than userTxHash's beneficiary. 66 | * @param userTxHash unique ticket identifier 67 | */ 68 | function cancel(bytes32 userTxHash) external; 69 | 70 | event TicketCreated(bytes32 indexed userTxHash); 71 | event LifetimeExtended(bytes32 indexed userTxHash, uint newTimeout); 72 | event Redeemed(bytes32 indexed userTxHash); 73 | event Canceled(bytes32 indexed userTxHash); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbStatistics.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity >=0.4.21 <0.9.0; 3 | 4 | interface ArbStatistics { 5 | // Get the following statistics for this chain: 6 | // Number of Arbitrum blocks 7 | // Number of accounts 8 | // Total storage allocated (includes storage that was later deallocated) 9 | // Total ArbGas used 10 | // Number of transaction receipt issued 11 | // Number of contracts created 12 | function getStats() external view returns(uint, uint, uint, uint, uint, uint); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbSys.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | /** 4 | * @title Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. Exposes a variety of system-level functionality. 5 | */ 6 | interface ArbSys { 7 | /** 8 | * @notice Get internal version number identifying an ArbOS build 9 | * @return version number as int 10 | */ 11 | function arbOSVersion() external pure returns (uint); 12 | 13 | function arbChainID() external view returns(uint); 14 | 15 | /** 16 | * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) 17 | * @return block number as int 18 | */ 19 | function arbBlockNumber() external view returns (uint); 20 | 21 | /** 22 | * @notice Send given amount of Eth to dest from sender. 23 | * This is a convenience function, which is equivalent to calling sendTxToL1 with empty calldataForL1. 24 | * @param destination recipient address on L1 25 | * @return unique identifier for this L2-to-L1 transaction. 26 | */ 27 | function withdrawEth(address destination) external payable returns(uint); 28 | 29 | /** 30 | * @notice Send a transaction to L1 31 | * @param destination recipient address on L1 32 | * @param calldataForL1 (optional) calldata for L1 contract call 33 | * @return a unique identifier for this L2-to-L1 transaction. 34 | */ 35 | function sendTxToL1(address destination, bytes calldata calldataForL1) external payable returns(uint); 36 | 37 | /** 38 | * @notice get the number of transactions issued by the given external account or the account sequence number of the given contract 39 | * @param account target account 40 | * @return the number of transactions issued by the given external account or the account sequence number of the given contract 41 | */ 42 | function getTransactionCount(address account) external view returns(uint256); 43 | 44 | /** 45 | * @notice get the value of target L2 storage slot 46 | * This function is only callable from address 0 to prevent contracts from being able to call it 47 | * @param account target account 48 | * @param index target index of storage slot 49 | * @return stotage value for the given account at the given index 50 | */ 51 | function getStorageAt(address account, uint256 index) external view returns (uint256); 52 | 53 | /** 54 | * @notice check if current call is coming from l1 55 | * @return true if the caller of this was called directly from L1 56 | */ 57 | function isTopLevelCall() external view returns (bool); 58 | 59 | /** 60 | * @notice check if the caller (of this caller of this) is an aliased L1 contract address 61 | * @return true iff the caller's address is an alias for an L1 contract address 62 | */ 63 | function wasMyCallersAddressAliased() external view returns (bool); 64 | 65 | /** 66 | * @notice return the address of the caller (of this caller of this), without applying L1 contract address aliasing 67 | * @return address of the caller's caller, without applying L1 contract address aliasing 68 | */ 69 | function myCallersAddressWithoutAliasing() external view returns (address); 70 | 71 | /** 72 | * @notice map L1 sender contract address to its L2 alias 73 | * @param sender sender address 74 | * @param dest destination address 75 | * @return aliased sender address 76 | */ 77 | function mapL1SenderContractAddressToL2Alias(address sender, address dest) external pure returns(address); 78 | 79 | /** 80 | * @notice get the caller's amount of available storage gas 81 | * @return amount of storage gas available to the caller 82 | */ 83 | function getStorageGasAvailable() external view returns(uint); 84 | 85 | event L2ToL1Transaction(address caller, address indexed destination, uint indexed uniqueId, 86 | uint indexed batchNumber, uint indexInBatch, 87 | uint arbBlockNum, uint ethBlockNum, uint timestamp, 88 | uint callvalue, bytes data); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /contracts/arbos/builtin/ArbosTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | interface ArbosTest { 4 | function installAccount(address addr, bool isEOA, uint balance, uint nonce, bytes calldata code, bytes calldata initStorage) external; 5 | 6 | function getMarshalledStorage(address addr) external view; // returns raw returndata 7 | 8 | function getAccountInfo(address addr) external view; // returns raw returndata 9 | 10 | function burnArbGas(uint gasAmount) external view; 11 | 12 | function setNonce(address addr, uint nonce) external; 13 | 14 | function setBalance(address addr, uint balance) external; 15 | 16 | function setCode(address addr, bytes calldata code) external; 17 | 18 | function setState(address addr, bytes calldata state) external; 19 | 20 | function store(address addr, uint256 key, uint256 value) external; 21 | } 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /contracts/arbos/test/Add.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | import "./Fibonacci.sol"; 4 | import "../builtin/ArbSys.sol"; 5 | import "./BlockNum.sol"; 6 | 7 | 8 | contract Add { 9 | constructor() public payable {} 10 | 11 | function add(uint x, uint y) public pure returns (uint) { 12 | return x+y; 13 | } 14 | 15 | function mult(uint x, uint y) public pure returns (uint) { 16 | return x*y; 17 | } 18 | 19 | function pythag(uint x, uint y) public pure returns (uint) { 20 | return add(mult(x,x), mult(y,y)); 21 | } 22 | 23 | function getSeqNum() public view returns (uint) { 24 | uint256 txCount = ArbSys(address(100)).getTransactionCount(msg.sender); 25 | return txCount; 26 | } 27 | 28 | function withdrawMyEth() public payable { 29 | ArbSys(address(100)).withdrawEth{ value: msg.value }(address(1025)); 30 | } 31 | 32 | function withdraw5000() public { 33 | ArbSys(address(100)).withdrawEth{ value: 5000 }(address(1025)); 34 | } 35 | 36 | function isTopLevel() public returns (bool) { 37 | return ArbSys(address(100)).isTopLevelCall(); 38 | } 39 | 40 | function isNotTopLevel() public returns (bool) { 41 | Fibonacci fib = new Fibonacci(); 42 | return fib.isTopLevel(); 43 | } 44 | 45 | function payTo(address addr) public payable { 46 | (bool success,) = addr.call{ value: msg.value }(""); 47 | require(success); 48 | } 49 | 50 | function requireMyCallerIsOrigin(address blockNumAddr) public { 51 | require(msg.sender == BlockNum(blockNumAddr).getOrigin()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/arbos/test/BlockNum.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | import "../builtin/ArbSys.sol"; 4 | import "../builtin/ArbosTest.sol"; 5 | 6 | 7 | contract BlockNum { 8 | uint public currBlock; 9 | mapping(uint => uint) aMap; 10 | 11 | function setBlock() public { 12 | currBlock = getBlock(); 13 | } 14 | 15 | function getBlock() public view returns (uint) { 16 | return block.number; 17 | } 18 | 19 | function getOrigin() public returns (address) { 20 | return tx.origin; 21 | } 22 | 23 | function getBlockNumTimestamp() public returns (uint, uint) { 24 | return (block.number, block.timestamp); 25 | } 26 | 27 | function getSender() public view returns (address) { 28 | return msg.sender; 29 | } 30 | 31 | function getL1CallerInfo() public view returns (bool, address) { 32 | return ( 33 | ArbSys(address(100)).wasMyCallersAddressAliased(), 34 | ArbSys(address(100)).myCallersAddressWithoutAliasing() 35 | ); 36 | } 37 | 38 | function recursiveCall(uint depth, bool shouldRevert) public { 39 | if (depth > 0) { 40 | this.recursiveCall(depth-1, shouldRevert); 41 | } 42 | require(!shouldRevert); 43 | } 44 | 45 | function useGasDownTo(uint targetGas) public { 46 | uint gas = gasleft(); 47 | require(gas > targetGas); 48 | ArbosTest(address(105)).burnArbGas(gas-targetGas); 49 | } 50 | 51 | function setMap(uint index, uint value) public { 52 | aMap[index] = value; 53 | } 54 | 55 | function rewriteStorage(uint index1, uint val1, uint index2, uint val2, uint index3, uint val3) public { 56 | this.setMap(index1, val1); 57 | this.setMap(index2, val2); 58 | this.setMap(index3, val3); 59 | } 60 | 61 | function rewriteThenRevert(uint index1, uint val1, uint index2, uint val2) public { 62 | this.setMap(index1, val1); 63 | this.setMap(index2, val2); 64 | require(false); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /contracts/arbos/test/Callback.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicensed 2 | 3 | pragma solidity >=0.4.21 <0.9.0; 4 | 5 | import "../builtin/ArbSys.sol"; 6 | 7 | contract Callback { 8 | function sendDummies() external { 9 | emit DummyEvent(1, 11, 21); 10 | emit DummyEvent(2, 12, 22); 11 | emit DummyEvent(3, 13, 23); 12 | } 13 | 14 | function doCallback() external returns(uint, uint) { 15 | emit DummyEvent(1, 2, 3); 16 | emit DummyEvent(4, 5, 6); 17 | uint r1 = ArbSys(address(100)).sendTxToL1(address(42), "hello world"); 18 | emit DummyEvent(7, 8, 9); 19 | emit DummyEvent(10, 11, 12); 20 | emit DummyEvent(13, 14, 15); 21 | uint r2 = ArbSys(address(100)).sendTxToL1(address(43), "hello there world"); 22 | emit DummyEvent(16, 17, 18); 23 | return(r1, r2); 24 | } 25 | 26 | event DummyEvent(uint indexed a, uint b, uint c); 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /contracts/arbos/test/ConstructorSD.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | import "./SelfDestructor.sol"; 4 | 5 | 6 | contract ConstructorSD { 7 | constructor(address victim, address payable beneficiary) public { 8 | SelfDestructor(victim).die(beneficiary); 9 | } 10 | } -------------------------------------------------------------------------------- /contracts/arbos/test/Delegator.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity >=0.4.21 <0.9.0; 3 | 4 | contract Worker { 5 | mapping(uint256 => uint256) public values; 6 | 7 | function work() public { 8 | for (uint256 i = 0; i < 100; i++) values[i] += 1; 9 | } 10 | } 11 | 12 | contract Base is Worker { 13 | event GasPre(uint256 gas); 14 | event GasPost(uint256 gas); 15 | 16 | Worker worker; 17 | 18 | constructor() public { 19 | worker = new Worker(); 20 | } 21 | } 22 | 23 | contract Greeter is Base { 24 | function test() external { 25 | emit GasPre(gasleft()); 26 | 27 | work(); 28 | // worker.work(); 29 | 30 | emit GasPost(gasleft()); 31 | } 32 | } 33 | 34 | contract Delegator is Base { 35 | function testDelegate(address implementation) external { 36 | (bool success,) = implementation.delegatecall(abi.encodeWithSelector(Greeter.test.selector)); 37 | require(success, "FAIL_DELEGATE"); 38 | } 39 | } -------------------------------------------------------------------------------- /contracts/arbos/test/Destroyer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | import "./SelfDestructor.sol"; 4 | 5 | 6 | contract Destroyer { 7 | function destroy(address victim, address payable beneficiary) public { 8 | SelfDestructor(victim).die(beneficiary); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/arbos/test/EvmTests.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | import "./SelfDestructor.sol"; 4 | 5 | 6 | contract EvmTests { 7 | function test(address otherAddr) public { 8 | uint a = 1; 9 | uint b = 1; 10 | if ((a+b) != 2) { 11 | emit TestFail(0, 0); 12 | } 13 | 14 | bytes memory otherCode = getCode(otherAddr); 15 | bytes32 otherHash = keccak256(abi.encodePacked(otherCode)); 16 | if (otherHash != getCodeHash(otherAddr)) { 17 | emit TestFail(1, 0); 18 | } 19 | 20 | uint timestamp = block.timestamp; 21 | if ((timestamp < 10000000) || (timestamp > 10001000)) { 22 | emit TestFail(2, 0); 23 | } 24 | 25 | if (address(this).balance > 0) { 26 | emit TestFail(3, 0); 27 | } 28 | 29 | if (tx.origin != address(0)) { 30 | emit TestFail(4, 0); 31 | } 32 | 33 | uint reread; 34 | assembly { 35 | mstore8(50000, 73) 36 | reread := mload(49969) 37 | } 38 | if (reread != 73) { 39 | emit TestFail(5, reread); 40 | } 41 | } 42 | 43 | function makeLog0() public { 44 | assembly { 45 | mstore8(50000, 73) 46 | log0(49969, 32) 47 | } 48 | } 49 | 50 | function destructTest(address victim) public { 51 | uint bal = address(this).balance; 52 | SelfDestructor(victim).die(payable(address(this))); 53 | if ((address(this).balance - bal) != 777) { 54 | emit TestFail(7, address(this).balance-bal); 55 | } 56 | } 57 | 58 | function getCode(address addr) internal returns(bytes memory o_code) { 59 | assembly { 60 | // retrieve the size of the code, this needs assembly 61 | let size := extcodesize(addr) 62 | // allocate output byte array - this could also be done without assembly 63 | // by using o_code = new bytes(size) 64 | o_code := mload(0x40) 65 | // new "memory end" including padding 66 | mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) 67 | // store length in memory 68 | mstore(o_code, size) 69 | // actually retrieve the code, this needs assembly 70 | extcodecopy(addr, add(o_code, 0x20), 0, size) 71 | } 72 | } 73 | 74 | function getCodeHash(address addr) internal returns(bytes32 o_hash) { 75 | assembly { 76 | o_hash := extcodehash(addr) 77 | } 78 | } 79 | 80 | event TestFail(uint code1, uint code2); 81 | } 82 | -------------------------------------------------------------------------------- /contracts/arbos/test/ExtCodeSizeTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | // test code provided by github user "noiach" 4 | 5 | contract ExtCodeSizeTest { 6 | constructor() public { 7 | require(isConstructor()); 8 | } 9 | 10 | // From an old version of OpenZeppelin's Initializable.sol (the latest 11 | // version no longer uses `extcodesize`). 12 | // 13 | // The MIT License (MIT) 14 | // 15 | // Copyright (c) 2016-2020 zOS Global Limited 16 | // 17 | // Permission is hereby granted, free of charge, to any person obtaining 18 | // a copy of this software and associated documentation files (the 19 | // "Software"), to deal in the Software without restriction, including 20 | // without limitation the rights to use, copy, modify, merge, publish, 21 | // distribute, sublicense, and/or sell copies of the Software, and to 22 | // permit persons to whom the Software is furnished to do so, subject to 23 | // the following conditions: 24 | // 25 | // The above copyright notice and this permission notice shall be included 26 | // in all copies or substantial portions of the Software. 27 | // 28 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 29 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 30 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 31 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 32 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 33 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 34 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 | function isConstructor() private view returns (bool) { 36 | // extcodesize checks the size of the code stored in an address, and 37 | // address returns the current address. Since the code is still not 38 | // deployed when running a constructor, any checks on its code size will 39 | // yield zero, making it an effective way to detect if a contract is 40 | // under construction or not. 41 | address self = address(this); 42 | uint256 cs; 43 | assembly { 44 | cs := extcodesize(self) 45 | } 46 | return cs == 0; 47 | } 48 | } -------------------------------------------------------------------------------- /contracts/arbos/test/Fibonacci.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | import "../builtin/ArbSys.sol"; 4 | 5 | contract Fibonacci { 6 | 7 | uint[] fibseries; 8 | 9 | event TestEvent(uint number); 10 | 11 | // n = how many in the series to return 12 | function generateFib(uint n) public payable { 13 | 14 | // set 1st and 2nd entries 15 | fibseries.push(1); 16 | fibseries.push(1); 17 | 18 | // generate subsequent entries 19 | for (uint i=2; i < n ; i++) { 20 | fibseries.push(fibseries[i-1] + fibseries[i-2]); 21 | } 22 | 23 | emit TestEvent(n); 24 | 25 | } 26 | 27 | function doFib(uint n) public returns (uint) { 28 | generateFib(n+1); 29 | return getFib(n); 30 | } 31 | 32 | function getFib(uint n) public view returns (uint) { 33 | return fibseries[n]; 34 | } 35 | 36 | // function getFib(uint n) public view returns (uint, uint) { 37 | // return (fibseries[n], fibseries[n + 1]); 38 | // } 39 | 40 | function isTopLevel() public returns (bool) { 41 | return ArbSys(address(100)).isTopLevelCall(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/arbos/test/MemoryUsage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | contract MemoryUsage { 4 | function test(uint offset) public { 5 | address addr = address(uint160(offset)); 6 | assembly { 7 | mstore(addr, offset) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/arbos/test/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/arbos/test/PRConstructor.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | contract PRConstructor { 4 | 5 | constructor(uint val) payable public { 6 | 7 | require(val == 0, "revert message"); 8 | 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /contracts/arbos/test/PaymentChannel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | import "./Fibonacci.sol"; 4 | 5 | contract PaymentChannel { 6 | event Deposited(address indexed payee, uint256 weiAmount); 7 | event Withdrawn(address indexed payee, uint256 weiAmount); 8 | event Transfer(address indexed from, address indexed to, uint256 value); 9 | 10 | mapping (address => uint) balances; 11 | Fibonacci fib; 12 | 13 | constructor(address fibAddress) public { 14 | fib = Fibonacci(fibAddress); 15 | } 16 | 17 | function deposit() public payable { 18 | balances[msg.sender] += msg.value; 19 | 20 | emit Deposited(msg.sender, msg.value); 21 | } 22 | 23 | function getBalance(address addr) public view returns (uint) { 24 | return balances[addr]; 25 | } 26 | 27 | function withdraw(uint amount) public { 28 | require(amount <= balances[msg.sender]); 29 | balances[msg.sender] -= amount; 30 | payable(msg.sender).transfer(amount); 31 | 32 | emit Withdrawn(msg.sender, amount); 33 | } 34 | 35 | function transfer(address dest, uint amount) public { 36 | require(amount <= balances[msg.sender]); 37 | balances[msg.sender] -= amount; 38 | balances[dest] += amount; 39 | 40 | emit Transfer(msg.sender, dest, amount); 41 | } 42 | 43 | function transferFib(address dest, uint count) public { 44 | //Fibonacci newFib = new Fibonacci(); 45 | 46 | fib.generateFib(count + 1); 47 | uint amount = fib.getFib(count) + balances[address(5)]; 48 | 49 | require(amount <= balances[msg.sender]); 50 | balances[msg.sender] -= amount; 51 | balances[dest] += amount; 52 | 53 | emit Transfer(msg.sender, dest, amount); 54 | } 55 | 56 | function testCreate() public returns (uint) { 57 | Fibonacci newFib = new Fibonacci(); 58 | newFib.generateFib(3); 59 | return newFib.getFib(2); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /contracts/arbos/test/ReverterFactory.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | 3 | contract Reverter { 4 | constructor(uint mode) public { 5 | require(mode == 0, "Mode not 0"); 6 | } 7 | } 8 | 9 | contract ReverterFactory { 10 | constructor(uint mode) public { 11 | new Reverter(mode); 12 | } 13 | } 14 | 15 | contract ConstructorCallback { 16 | event TestEvent(uint256 dataLength); 17 | event TestEvent2(address dataLength); 18 | 19 | constructor() public payable { 20 | emit TestEvent(msg.data.length); 21 | ConstructorCallback2(msg.sender).test2(); 22 | } 23 | 24 | function test(address data) external { 25 | emit TestEvent2(data); 26 | } 27 | } 28 | 29 | contract ConstructorCallback2 { 30 | event TestEvent3(bool indexed success, bytes returnData); 31 | 32 | function test() external payable { 33 | new ConstructorCallback(); 34 | } 35 | 36 | function test2() external payable { 37 | (bool success, bytes memory returnData) = 38 | address(msg.sender).call( 39 | abi.encodeWithSelector(ConstructorCallback.test.selector, msg.sender) 40 | ); 41 | emit TestEvent3(success, returnData); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/arbos/test/SelfDestructor.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | 4 | contract SelfDestructor { 5 | function die(address payable beneficiary) public { 6 | selfdestruct(beneficiary); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /contracts/arbos/test/SingletonFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | 4 | /** 5 | * @title Singleton Factory (EIP-2470) 6 | * @notice Exposes CREATE2 (EIP-1014) to deploy bytecode on deterministic addresses based on initialization code and salt. 7 | * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) 8 | */ 9 | contract SingletonFactory { 10 | /** 11 | * @notice Deploys `_initCode` using `_salt` for defining the deterministic address. 12 | * @param _initCode Initialization code. 13 | * @param _salt Arbitrary value to modify resulting address. 14 | * @return createdContract Created contract address. 15 | */ 16 | function deploy(bytes memory _initCode, bytes32 _salt) 17 | public 18 | returns (address payable createdContract) 19 | { 20 | assembly { 21 | createdContract := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) 22 | } 23 | } 24 | } 25 | // IV is a value changed to generate the vanity address. 26 | // IV: 6583047 -------------------------------------------------------------------------------- /contracts/arbos/test/Underfunded.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | contract Underfunded { 4 | fallback() external payable { 5 | require(false, "no deposits"); 6 | } 7 | function nestedCall(uint256 value) external { 8 | address(this).call{ value: value }(""); 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /contracts/hardhat.config.js: -------------------------------------------------------------------------------- 1 | const solcVersion = process.env.SOLC_VERSION || "0.8.10" 2 | 3 | if(solcVersion !== "0.6.11" && solcVersion !== "0.8.10" && solcVersion !== "0.7.5") 4 | throw new Error("Select a supported solidity version.") 5 | 6 | /** 7 | * @type import('hardhat/config').HardhatUserConfig 8 | */ 9 | module.exports = { 10 | solidity: { 11 | compilers: [ 12 | { 13 | version: solcVersion, 14 | settings: { 15 | optimizer: { 16 | enabled: true, 17 | runs: 100, 18 | }, 19 | }, 20 | } 21 | ], 22 | }, 23 | paths: { 24 | sources: "./arbos", 25 | artifacts: process.env["HARDHAT_ARTIFACT_PATH"] || "./artifacts" 26 | }, 27 | defaultNetwork: "localhost", 28 | networks: { 29 | localhost: { 30 | url: "http://127.0.0.1:7545", 31 | accounts: { 32 | mnemonic: 'jar deny prosper gasp flush glass core corn alarm treat leg smart', 33 | path: "m/44'/60'/0'/0", 34 | initialIndex: 0, 35 | count: 10, 36 | }, 37 | }, 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arbos-precompiles", 3 | "version": "1.0.2", 4 | "author": "Offchain Labs, Inc.", 5 | "license": "Apache-2.0", 6 | "dependencies": { 7 | "hardhat": "^2.6.4" 8 | }, 9 | "scripts": { 10 | "hardhat:prod": "hardhat --config hardhat.config.js", 11 | "build": "hardhat compile", 12 | "build:0.6": "SOLC_VERSION=0.6.11 hardhat compile", 13 | "build:0.7": "SOLC_VERSION=0.7.5 hardhat compile", 14 | "build:0.8": "SOLC_VERSION=0.8.10 hardhat compile", 15 | "full:build": "$npm_execpath run build:0.6 && $npm_execpath run build:0.7 && $npm_execpath run build:0.8", 16 | "deploy:test": "hardhat run --network localhost scripts/deploy-test.js" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/scripts/deploy-test.js: -------------------------------------------------------------------------------- 1 | async function main() { 2 | const Fibonacci = await ethers.getContractFactory("Fibonacci"); 3 | const fibonacci = await Fibonacci.deploy(); 4 | console.log("Fibonacci deployed to:", fibonacci.address); 5 | 6 | const PaymentChannel = await ethers.getContractFactory("Fibonacci"); 7 | const paymentChannel = await PaymentChannel.deploy(fibonacci.address); 8 | console.log("PaymentChannel deployed to:", paymentChannel.address); 9 | } 10 | 11 | main() 12 | .then(() => process.exit(0)) 13 | .catch((error) => { 14 | console.error(error); 15 | process.exit(1); 16 | }); 17 | -------------------------------------------------------------------------------- /coverage/mini-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | LINES=$(cat $1 | awk '{$3="DA:"$3","$1; $1=""; sub(/\+/, 1); sub(/\-/, 0); sub(/^ /, ""); sub(/^std::/, "stdlib/"); sub(/^core::/, "builtin/"); print $0}') 4 | 5 | CURR_FILE="" 6 | CURR_COUNT=0 7 | 8 | while read -r line; do 9 | 10 | FILE=${line%% *} 11 | 12 | if [[ "$FILE" != "$CURR_FILE" ]]; then 13 | if [[ "$CURR_FILE" != "" ]]; then 14 | echo "LF:$CURR_COUNT" 15 | echo "LH:0" 16 | echo "end_of_record" 17 | fi 18 | CURR_FILE=$FILE 19 | CURR_COUNT=0 20 | echo "SF:$FILE.mini" 21 | fi 22 | 23 | ((CURR_COUNT += 1)) 24 | 25 | echo $line | cut -d" " -f2- 26 | 27 | done <<< "$LINES" 28 | 29 | echo "LF:$CURR_COUNT" 30 | echo "LH:0" 31 | echo "end_of_record" 32 | -------------------------------------------------------------------------------- /doc/TracingData.md: -------------------------------------------------------------------------------- 1 | # Tracing Data Emitted by ArbOS 2 | 3 | ArbOS emits tracing data for any nodes that want to get more information about the execution of EVM transactions. This document describes how that data is emitted, and its format. 4 | 5 | ## Emitting trace info as debugprints 6 | 7 | Soon after emitting the log item that is a transaction receipt, ArbOS does a debugprint to give tracing information about that transaction. The debugprint contents are a 2-tuple `(20000, traceData)` where `traceData` is a tuple structure described below. 8 | 9 | ArbOS will emit a trace debugprint for almost every transaction that it runs. It typically will not emit a trace debugprint for submitted transactions that cannot run, such as ones with bad sequence numbers, or inadequate funds for callvalue, or other failure cases that would make a transaction not runnable on Ethereum. For these failing transactions, ArbOS will emit a transaction receipt giving the failure reason but will not emit a tracing debugprint. 10 | 11 | There is one case where ArbOS does run a transaction and emit a receipt for it, but does not emit trace information: a successful redemption of a retryable transaction ticket. At present, ArbOS emits trace information for the "outer" transaction that makes the call to redeem the ticket, but it does not emit trace information for the "inner" redemption transaction, even though there will be a receipt for the outer transaction (always) and a separate receipt for the inner transaction (if it succeeds). [The treatment of this use case is likely to change in the future.] 12 | 13 | ## Format of the traceData 14 | 15 | The traceData is a tuple-structured linked list, representing a sequence of events. The list is either an empty tuple denoting an empty list, or a 2-tuple `(firstEvent, restOfList)`. 16 | 17 | An event is a 2-tuple `(eventType, eventData)`, where `eventType` is an integer and the format of `eventData` depends on the event type. 18 | 19 | #### Event type 0: call 20 | 21 | Type-specific data is a 7-tuple: 22 | 23 | * call type (uint) [0: call, 1: callcode, 2: delegatecall, 3: staticcall] 24 | * calldata (3-tuple: nbytes, offset, buffer) 25 | * callvalue (uint) 26 | * from (address formatted as uint) 27 | * to (address formatted as uint; 0 means this is a contract deploy) 28 | * gas (uint) 29 | * gas price paid (uint) 30 | 31 | #### Event type 1: return/revert 32 | 33 | Type-specific data is a 3-tuple: 34 | 35 | * result code (uint) 36 | * returndata (3-tuple: nbytes, offset, buffer) 37 | * gas used (uint) 38 | 39 | #### Event type 2: create 40 | 41 | Type-specific data is a 2-tuple: 42 | 43 | * code (3-tuple: nbytes, offset, buffer) 44 | * deploy address (address encoded as uint) 45 | 46 | #### Event type 3: create2 47 | 48 | Type-specific data is a 4-tuple: 49 | 50 | * code (3-tuple: nbytes, offset, buffer) 51 | * address that invoked create2 (address encoded as uint) 52 | * salt (uint) 53 | * deploy address (address encoded as uint) 54 | 55 | -------------------------------------------------------------------------------- /evm-tests/.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | docs/_build/ 3 | *~ 4 | *.sw? 5 | .vagrant/ 6 | *.pem 7 | src/GenStateTestAsBcTemp/ 8 | .env3/ 9 | .idea/ 10 | /BlockchainTests/InvalidBlocks/bcExpectSection* 11 | -------------------------------------------------------------------------------- /iterator.mini: -------------------------------------------------------------------------------- 1 | 2 | type Iterator = func(state: anytype) (bool, anytype, anytype) 3 | // return type: (false, _, _) if no more values 4 | // (true, value, nextState) otherwise 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /looptest/bridge2.mini: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // This file is machine-generated. Don't edit it unless you know what you're doing. 4 | // 5 | // Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 6 | // 7 | 8 | use impl2::set_newGlobal1_onUpgrade; 9 | use impl2::set_newGlobal2_onUpgrade; 10 | use impl2::set_newGlobal3_onUpgrade; 11 | 12 | 13 | type GlobalsBeforeUpgrade = struct { 14 | global1: uint, 15 | global2: option , 16 | _jump_table: any, 17 | }; 18 | 19 | type GlobalsAfterUpgrade = struct { 20 | newGlobal1: option , 21 | newGlobal2: uint, 22 | newGlobal3: (uint, uint, ), 23 | _jump_table: any, 24 | }; 25 | 26 | public view func remapGlobalsForUpgrade(input_globals: GlobalsBeforeUpgrade) -> (GlobalsAfterUpgrade, uint) { 27 | let newGlobal1 = set_newGlobal1_onUpgrade(input_globals); 28 | let newGlobal2 = set_newGlobal2_onUpgrade(input_globals); 29 | let newGlobal3 = set_newGlobal3_onUpgrade(input_globals); 30 | let _jump_table = (asm() GlobalsAfterUpgrade { rget })._jump_table; 31 | return (struct { 32 | newGlobal1: newGlobal1, 33 | newGlobal2: newGlobal2, 34 | newGlobal3: newGlobal3, 35 | _jump_table: _jump_table, 36 | }, 58); 37 | } 38 | 39 | 40 | sensitive func __dummy__() { return; } 41 | 42 | 43 | -------------------------------------------------------------------------------- /looptest/impl2.mini: -------------------------------------------------------------------------------- 1 | 2 | use bridge2::GlobalsBeforeUpgrade; 3 | 4 | public func set_newGlobal1_onUpgrade(input: GlobalsBeforeUpgrade) -> option { 5 | return input.global2; 6 | } 7 | 8 | public func set_newGlobal2_onUpgrade(input: GlobalsBeforeUpgrade) -> uint { 9 | return input.global1; 10 | } 11 | 12 | public func set_newGlobal3_onUpgrade(_input: GlobalsBeforeUpgrade) -> (uint, uint) { 13 | return (17, 39); 14 | } 15 | -------------------------------------------------------------------------------- /looptest/upgrade2.toml: -------------------------------------------------------------------------------- 1 | data = [] 2 | -------------------------------------------------------------------------------- /looptest/upgrade2_new.mini: -------------------------------------------------------------------------------- 1 | 2 | use bridge2::remapGlobalsForUpgrade; 3 | use bridge2::GlobalsBeforeUpgrade; 4 | 5 | var newGlobal1: option; 6 | var newGlobal2: uint; 7 | var newGlobal3: (uint, uint); 8 | 9 | 10 | view write throw func main(oldGlobals: GlobalsBeforeUpgrade) -> uint { 11 | asm(remapGlobalsForUpgrade(oldGlobals).0,) { rset }; 12 | 13 | if (newGlobal1 != Some(-3s)) { 14 | asm((1, newGlobal1)) { debugprint }; 15 | error; 16 | } 17 | 18 | if (newGlobal2 != 5) { 19 | asm((2, newGlobal2)) { debugprint }; 20 | error; 21 | } 22 | 23 | asm(42) { debugprint }; 24 | return 2*myExpr(3); 25 | } 26 | 27 | func plusFour(x: uint) -> uint { 28 | return x+4; 29 | } 30 | 31 | func myExpr(x: uint) -> uint { 32 | return 3*(plusFour(x)); 33 | } 34 | -------------------------------------------------------------------------------- /looptest/upgrade2_old.mini: -------------------------------------------------------------------------------- 1 | 2 | use std::bytearray::bytearray_fromSizeAndBuffer; 3 | 4 | use std::bytestream::bytestream_new; 5 | 6 | use std::avmcodebuilder::avmCodeBuilder_new; 7 | use std::avmcodebuilder::avmCodeBuilder_append; 8 | use std::avmcodebuilder::avmCodeBuilder_finish; 9 | 10 | 11 | var global1: uint; 12 | var global2: option; 13 | 14 | view write throw func main() { 15 | global1 = 5; 16 | global2 = Some(-3s); 17 | 18 | let (sz, buf) = asm() (uint, buffer) { inbox }; 19 | let stream = bytestream_new(bytearray_fromSizeAndBuffer(sz, buf)); 20 | 21 | let builder = avmCodeBuilder_new(false); 22 | builder = if let Some(bu) = avmCodeBuilder_append(builder, stream) { 23 | bu 24 | } else { 25 | error 26 | }; 27 | let jumpTarget = avmCodeBuilder_finish(builder); 28 | 29 | asm(jumpTarget, 78) { jump }; 30 | 31 | // should never get here 32 | error; 33 | } 34 | -------------------------------------------------------------------------------- /minitests/arithmetic.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | 6 | write func main() { 7 | asm(tests().1) { log }; 8 | } 9 | 10 | func tests() -> string { 11 | 12 | let sum = -int(1) + -int(1); 13 | 14 | if sum + int(2) != int(0) || -sum != int(2) { 15 | return "Uh oh, signed arithmetic is broken"; 16 | } 17 | 18 | let flip = ----int(7); 19 | let flop = ---int(7); 20 | 21 | if flip != -flop { 22 | return "Uh oh, unary minus is broken"; 23 | } 24 | 25 | "" 26 | } 27 | -------------------------------------------------------------------------------- /minitests/basic.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | 6 | func main() { 7 | let x = 4; 8 | } 9 | -------------------------------------------------------------------------------- /minitests/builtin: -------------------------------------------------------------------------------- 1 | ../builtin/ -------------------------------------------------------------------------------- /minitests/callgraph/main.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use other::start; 6 | 7 | func main() -> uint { 8 | return used() + used(); 9 | } 10 | 11 | func used() -> uint { 12 | return 4 + used() + start(); 13 | } 14 | 15 | func unused() -> uint { 16 | return 8 + unused(); 17 | } 18 | 19 | sensitive func disconnected_cycle1() { disconnected_cycle2(); } 20 | sensitive func disconnected_cycle2() { disconnected_cycle3(); } 21 | sensitive func disconnected_cycle3() { disconnected_cycle1(); } 22 | 23 | public func externally_used() -> uint { 24 | return 4; 25 | } 26 | public func externally_unused() -> uint { 27 | return 8; 28 | } 29 | -------------------------------------------------------------------------------- /minitests/callgraph/other.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use main::externally_used; 6 | use main::externally_unused; 7 | 8 | public func start() -> uint { 9 | return back_edge(); 10 | } 11 | 12 | func back_edge() -> uint { 13 | let a = externally_used; 14 | if a == externally_used { 4 } else { 4 } // trick the optimizer 15 | } 16 | 17 | func unused() -> uint { 18 | return externally_unused(); 19 | } 20 | 21 | func _purposefully_unused() -> uint { 22 | return 4; 23 | } 24 | -------------------------------------------------------------------------------- /minitests/closure.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | 6 | var global: uint; 7 | 8 | type Produce = closure(uint, uint) -> string; 9 | 10 | view write throw func main() { 11 | asm(tests().1) { log }; 12 | } 13 | 14 | view write throw func tests() -> string { 15 | 16 | let lambda = capture_2(); 17 | 18 | if lambda(48, 96) != "Closures are so cool!" { 19 | return "Uh oh, closures don't work when passed around"; 20 | } 21 | 22 | if lambda(48, 96) != lambda(48, 96) { 23 | return "Uh oh, closures are stateful"; 24 | } 25 | 26 | lambda = capture_1(3); 27 | 28 | if lambda(1, 2) != "Capture count doesn't matter!" { 29 | return lambda(1, 2); 30 | } 31 | 32 | test_global_capture() 33 | } 34 | 35 | throw func capture_2() -> Produce { 36 | 37 | // Checks that captures work as expected 38 | 39 | let a = 32; 40 | let b = (64, 128, "Closures are so cool!"); 41 | 42 | let lambda = closure(arg1: uint, arg2: uint) -> string { 43 | 44 | let sum = arg1 + a; 45 | let mul = arg2 * b.0 + b.1; 46 | 47 | if sum != arg1 + 32 { 48 | return "a wasn't captured correctly"; 49 | } 50 | if mul != arg2 * 64 + 128 { 51 | return "b wasn't captured correctly"; 52 | } 53 | 54 | let (_, _, message) = b; 55 | 56 | message 57 | }; 58 | 59 | if lambda(1024, 512) != "Closures are so cool!" { 60 | return error; 61 | } 62 | lambda 63 | } 64 | 65 | func capture_1(sum: uint) -> Produce { 66 | 67 | closure(arg1: uint, arg2: uint) -> string { 68 | if arg1 + arg2 == sum { 69 | "Capture count doesn't matter!" 70 | } else { 71 | "Uh oh, a wasn't 0" 72 | } 73 | } 74 | } 75 | 76 | view write func test_global_capture() -> string { 77 | 78 | global = 2; 79 | 80 | let saved_global = 2; 81 | let true_capture = closure() -> uint { 82 | saved_global 83 | }; 84 | saved_global = 1024; // no longer the same 85 | 86 | let viewable = view closure() -> uint { 87 | global 88 | }; 89 | 90 | if viewable() != 2 { 91 | return "Could not read a 2"; 92 | } 93 | 94 | global = 4; 95 | 96 | if viewable() != 4 { 97 | return "Could not read a 4"; 98 | } 99 | 100 | let writeable = write closure(new: uint) { 101 | global = new; 102 | }; 103 | 104 | writeable(16); 105 | 106 | if viewable() != 16 { 107 | return "Could not read-write a 16"; 108 | } 109 | 110 | let view_and_write = view write closure() { 111 | global = global + 1; 112 | }; 113 | 114 | let loops = 0; 115 | writeable(0); 116 | 117 | while loops < 32 { 118 | view_and_write(); 119 | loops = loops + 1; 120 | } 121 | 122 | if viewable() != 32 { 123 | return "Could not update global"; 124 | } 125 | 126 | if true_capture() != saved_global / 512 { 127 | return "True global capture didn't work"; 128 | } 129 | 130 | "" 131 | } 132 | -------------------------------------------------------------------------------- /minitests/codeblocks.mini: -------------------------------------------------------------------------------- 1 | 2 | func main() -> (uint, uint) { 3 | let x = 0; 4 | let y = { 5 | let x = 4; 6 | x = x + 1; 7 | x * x 8 | }; 9 | (x, y) 10 | } 11 | -------------------------------------------------------------------------------- /minitests/codeloadtest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | 6 | view write func main() { 7 | asm(tests()) { log }; 8 | } 9 | 10 | view write func tests() -> uint { 11 | let codept = loadFunc1(); 12 | let res = unsafecast uint>(codept)(); 13 | if res != 3 { 14 | 1 15 | } else { 16 | 0 17 | } 18 | } 19 | 20 | view write func loadFunc1() -> func() { 21 | let ret = asm() func() { errcodept }; 22 | 23 | ret = addInsn(0x34, ret); // jump 24 | ret = addInsnImm(0x43, 3, ret); // [3] swap1 25 | 26 | ret 27 | } 28 | 29 | view write func addInsn(op: uint, codept: func()) -> func() { 30 | asm(op, codept) func() { pushinsn } 31 | } 32 | 33 | view write func addInsnImm(op: uint, imm: any, codept: func()) -> func() { 34 | asm(op, imm, codept) func() { pushinsnimm } 35 | } 36 | -------------------------------------------------------------------------------- /minitests/constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "arbos_version": 28, 3 | "integer": { 4 | "_ShouldNotBeUsed": 8 5 | }, 6 | "hex": { 7 | "ShouldBeUsed": "8" 8 | }, 9 | "contract_folder": "contracts/artifacts/arbos/builtin", 10 | "contract": [ 11 | 12 | ], 13 | "parameters_int": { }, 14 | "parameters_hex": { } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /minitests/default.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | type Nestable = struct { 6 | name: string, 7 | array: []string, 8 | }; 9 | 10 | var sized_array: [12]uint; 11 | var array: []string; 12 | var nested: [][]Nestable; 13 | 14 | view write throw func main() { 15 | 16 | if let Some(issue) = test() { 17 | asm(issue.1) { log }; 18 | } else { 19 | asm("".1) { log }; 20 | } 21 | } 22 | 23 | view write throw func test() -> option { 24 | 25 | let sized_local = newfixedarray(12, 0); 26 | 27 | if sized_local != sized_array { 28 | debug(("array".1, sized_array)); 29 | debug(("local".1, sized_local)); 30 | return Some("default not same for sized array"); 31 | } 32 | 33 | let local = newarray(0); 34 | let direct = unsafecast<[]string>(builtin_arrayNew(0, "")); 35 | 36 | if local != array || direct != array { 37 | debug(("array".1, array)); 38 | debug(("local".1, local)); 39 | debug(("direct".1, direct)); 40 | return Some("default not same for unsized array"); 41 | } 42 | 43 | let nested_local = newarray<[]Nestable>(0); 44 | 45 | if nested != nested_local { 46 | debug(("array".1, nested)); 47 | debug(("local".1, nested_local)); 48 | return Some("default not same for nested array"); 49 | } 50 | 51 | None 52 | } 53 | -------------------------------------------------------------------------------- /minitests/generics/basic.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | type basic = (T, W, string, W); // (p0, p0, p1, string, p1) 6 | type concrete = (uint, uint); 7 | 8 | write func main() { 9 | asm(tests().1) { log }; 10 | } 11 | 12 | func tests() -> string { 13 | 14 | let nongeneric = load_concrete(4); 15 | if (nongeneric.0 != nongeneric.1) { 16 | return "Tuples don't work"; 17 | } 18 | 19 | let generic = load(4); 20 | 21 | if (generic.0 != 4 || generic.1 != !generic.3) { 22 | return "Generics don't work"; 23 | } 24 | 25 | if (generic.2 != "I <3 Generics") { 26 | return "Basic types don't work along-side generics"; 27 | } 28 | 29 | return ""; 30 | } 31 | 32 | func load(arg: uint) -> basic { 33 | return (arg, true, "I <3 Generics", false); 34 | } 35 | 36 | func load_concrete(arg: uint) -> concrete { 37 | return (arg, arg); 38 | } 39 | -------------------------------------------------------------------------------- /minitests/generics/closure.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | type tupfunc = func(I) -> (I, I); 6 | 7 | write func main() { 8 | asm(tests().1) { log }; 9 | } 10 | 11 | func tests() -> string { 12 | 13 | let lambda = make_lambda::(); 14 | 15 | if (lambda("duplicate") != ("duplicate", "duplicate")) { 16 | return "Generics don't work with closures"; 17 | } 18 | 19 | let store = struct { 20 | value: "wow", 21 | }; 22 | 23 | if (duplicate::(store, lambda) != ("wow", "wow")) { 24 | return "Generics don't work with passed closures"; 25 | } 26 | 27 | return ""; 28 | } 29 | 30 | func make_lambda() -> tupfunc { 31 | let lambda = closure(input: T) -> (T, T) { 32 | return (input, input); 33 | }; 34 | return lambda; 35 | } 36 | 37 | type storage = struct { 38 | value: S, 39 | }; 40 | 41 | func duplicate(store: storage, lambda: tupfunc) -> (U, U) { 42 | return lambda(store.value); 43 | } 44 | -------------------------------------------------------------------------------- /minitests/generics/colorful.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | write func main() { 6 | asm(tests().1) { log }; 7 | } 8 | 9 | func tests() -> string { 10 | 11 | let generic = color::("wow"); 12 | 13 | if (generic != ("wow", "wow", "wow")) { 14 | return "Generics don't work when passed around"; 15 | } 16 | 17 | return ""; 18 | } 19 | 20 | func color(arg: T) -> (T, T, T) { 21 | 22 | let pair = (arg, arg); 23 | 24 | let same = unwrap::<(T, T)>(struct { 25 | entry: pair, 26 | other: (pair, pair), 27 | }); 28 | 29 | return (arg, pair.0, pass::(same.1)); 30 | } 31 | 32 | func pass(arg: R) -> R { 33 | return arg; 34 | } 35 | 36 | type wrapper = struct { 37 | entry: T, 38 | other: (T, T), 39 | }; 40 | 41 | func unwrap(wrap: wrapper) -> X { 42 | return wrap.other.1; 43 | } 44 | -------------------------------------------------------------------------------- /minitests/generics/func.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | write func main() { 6 | asm(tests().1) { log }; 7 | } 8 | 9 | func tests() -> string { 10 | 11 | let generic = wrap::(8); 12 | 13 | if (generic.0 + generic.1 != 16) { 14 | return "Generic funcs don't work"; 15 | } 16 | 17 | return ""; 18 | } 19 | 20 | func wrap(arg: T) -> (T, T) { 21 | return (arg, arg); 22 | } 23 | -------------------------------------------------------------------------------- /minitests/generics/nested.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | type base = A; 6 | type tupe = (S, U); 7 | type flip = (tupe, tupe); 8 | 9 | write func main() { 10 | asm(tests().1) { log }; 11 | } 12 | 13 | sensitive func diff(arg: flip) -> flip { return arg; } 14 | sensitive func same(arg: flip) -> flip { return arg; } 15 | sensitive func noop(arg: A ) -> A { return arg; } 16 | 17 | func tests() -> string { 18 | let flip_flop = ((true, 1024), (1024, true)); 19 | let _diff_test = diff::(flip_flop); 20 | 21 | let same_same = ((true, true), (true, true)); 22 | let _diff_test = diff::(same_same); 23 | let _same_test = same::(same_same); 24 | 25 | let _a = 32; 26 | let _b = noop::(32); 27 | let _c = noop:: >(32); 28 | let _d = noop:: > >(32); 29 | _a = _b; _a = _c; _a = _d; 30 | _b = _a; _b = _c; _b = _d; 31 | _c = _a; _c = _b; _c = _d; 32 | _d = _a; _d = _b; _d = _c; 33 | 34 | let _complex_1 = explicit::(flip_flop); 35 | let _complex_2 = explicit::, base >(flip_flop); 36 | _complex_2 = _complex_1; 37 | _complex_1 = _complex_2; 38 | 39 | let a = test(); 40 | let _ = a.0 + a.1; 41 | 42 | let source = (((true, true), 1024), (1024, (true, true))); 43 | let _complex_1 = explicit::<(bool, bool), uint>(source); 44 | let _complex_2 = explicit::<(base, base), base >(source); 45 | _complex_2 = _complex_1; 46 | _complex_1 = _complex_2; 47 | 48 | return ""; 49 | } 50 | 51 | func explicit(arg: flip) -> tupe, tupe > { 52 | return arg; 53 | } 54 | 55 | func test() -> (base >, base) { 56 | return (1, 2); 57 | } 58 | -------------------------------------------------------------------------------- /minitests/generics/queue.mini: -------------------------------------------------------------------------------- 1 | 2 | type queue = (T, option >); 3 | 4 | write func main() { 5 | asm(tests().1) { log }; 6 | } 7 | 8 | func tests() -> string { 9 | return ""; 10 | } 11 | 12 | func _example() -> queue { 13 | return unsafecast >(0); 14 | } 15 | -------------------------------------------------------------------------------- /minitests/generics/simple.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | type simple = (T, T, W, string, W); // (p0, p0, p1, string, p1) 6 | 7 | write func main() { 8 | asm(tests().1) { log }; 9 | } 10 | 11 | func tests() -> string { 12 | 13 | let generic = load(5); 14 | 15 | if (count::(generic) != first::(generic)) { 16 | return "Generics don't work"; 17 | } 18 | 19 | return ""; 20 | } 21 | 22 | func load(arg: uint) -> simple { 23 | return (arg, 0, true, "I <3 Generics", false); 24 | } 25 | 26 | func count(input: simple) -> uint { 27 | let _ = input; 28 | return len(input); 29 | } 30 | 31 | func first(input2: simple) -> A { 32 | return input2.0; 33 | } 34 | -------------------------------------------------------------------------------- /minitests/globaltest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | var global0: uint; 6 | var global1: uint; 7 | var global2: any; 8 | var global3: (uint, int, bool); 9 | var global4: int; 10 | var global5: bool; 11 | var global6: uint; 12 | var global7: []uint; 13 | var global8: [13]uint; 14 | var global9: uint; 15 | 16 | type MyType = uint; 17 | var global10: MyType; 18 | 19 | 20 | view write func main() { 21 | asm(tests()) { log }; 22 | } 23 | 24 | view write func tests() -> uint { 25 | global0 = 3; 26 | if global0 != 3 { 27 | return 1; 28 | } 29 | 30 | global9 = 4; 31 | if global9 != 4 { 32 | return 2; 33 | } 34 | 35 | global0 = 5; 36 | global9 = 4; 37 | if global0 != 5 { 38 | return 3; 39 | } 40 | 41 | global9 = 6; 42 | global0 = 3; 43 | if global9 != 6 { 44 | return 4; 45 | } 46 | 47 | let sum = asm (4, 0, 3) uint { 48 | add 49 | add 50 | }; 51 | if sum != 7 { 52 | return 5; 53 | } 54 | 55 | global10 = 3; 56 | if global10 != 3 { 57 | return 6; 58 | } 59 | 60 | let farr = newfixedarray(25, 0); 61 | farr = farr with { [0] = 33 }; 62 | if farr[0] != 33 { 63 | return 7; 64 | } 65 | 66 | let farr2 = newfixedarray(42, 0); 67 | farr2 = farr2 with { [21] = 44 }; 68 | farr2 = farr2 with { [0] = 33 }; 69 | if farr2[21] != 44 { 70 | return 8; 71 | } 72 | 73 | if asm(16, 0) uint { byte } != 0 { 74 | return 10; 75 | } 76 | if asm(30, 65534) uint { byte } != 255 { 77 | return 11; 78 | } 79 | 80 | if asm(31, 0xfe985018496551) uint { byte } != 0x51 { 81 | return 12; 82 | } 83 | 84 | if asm(29, 0xfe985018496551) uint { byte } != 0x49 { 85 | return 13; 86 | } 87 | 88 | if asm(0, ~0) uint { byte } != 0xff { 89 | return 14; 90 | } 91 | 92 | if (0xbe << 12) != 0xbe000 { 93 | return 15; 94 | } 95 | 96 | if ((32*581249+17) >> 5) != 581249 { 97 | return 16; 98 | } 99 | 100 | if ((~0) >> 244) != 0xfff { 101 | return 17; 102 | } 103 | 104 | let testInt = int(0); 105 | testInt = testInt - int(16395); 106 | let shiftedTestInt = testInt << int(159); 107 | let shiftedBack = asm(159, shiftedTestInt) int { sar }; 108 | if shiftedBack != testInt { 109 | return 18; 110 | } 111 | 112 | if asm(5, 32*581249+17) uint { sar } != 581249 { 113 | return 19; 114 | } 115 | 116 | if asm(1, 0xffff) uint { signextend } != ~0 { 117 | return 20; 118 | } 119 | 120 | if asm(2, 0xffff) uint { signextend } != 0xffff { 121 | return 21; 122 | } 123 | 124 | if asm(1, 0x63ff) uint { signextend } != 0x63ff { 125 | return 22; 126 | } 127 | 128 | let a = -int(1); 129 | let b = int(0); 130 | if b < a { 131 | return 23; 132 | } 133 | if a >= b { 134 | return 24; 135 | } 136 | 137 | struct9test() 138 | } 139 | 140 | func struct9test() -> uint { 141 | let _theStruct = struct { 142 | field0: 0, 143 | field1: 1, 144 | field2: 2, 145 | field3: 3, 146 | field4: 4, 147 | field5: 5, 148 | field6: 6, 149 | field7: 7, 150 | field8: 8, 151 | }; 152 | 153 | 0 154 | } 155 | -------------------------------------------------------------------------------- /minitests/if-else.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | 6 | func main() -> uint { 7 | let x = if true == false { 8 | 1 9 | } else if let Some(thing) = None { 10 | 2 11 | } else if true { 12 | 0 13 | } else { 14 | 3 15 | }; 16 | x 17 | } 18 | -------------------------------------------------------------------------------- /minitests/loadertest1.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | public func main() { 6 | asm(777,) { log }; 7 | } -------------------------------------------------------------------------------- /minitests/loadertest2.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | 6 | public func main() { 7 | let ret = fact(5); 8 | asm(ret) { log }; 9 | } 10 | 11 | throw func fact(n: uint) -> uint { 12 | let ret = 1; 13 | while n > 0 { 14 | ret = ret*n; 15 | n = n-1; 16 | } 17 | ret 18 | } 19 | -------------------------------------------------------------------------------- /minitests/quick.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | write func main() { 6 | asm("".1) { log }; 7 | } 8 | -------------------------------------------------------------------------------- /minitests/replicas.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | func main() -> uint { 6 | return 4; 7 | } 8 | 9 | view func main() -> uint { 10 | return 4; 11 | } 12 | -------------------------------------------------------------------------------- /minitests/simple-closure.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | 6 | var global: uint; 7 | 8 | type Produce = func(uint, uint) -> string; 9 | 10 | throw write func main() { 11 | asm(tests().1) { log }; 12 | } 13 | 14 | throw func tests() -> string { 15 | 16 | let lambda = make_lambda(); 17 | 18 | if lambda(48, 96) != "Closures are so cool!" { 19 | "Uh oh, closures don't work when passed around" 20 | } else { 21 | "" 22 | } 23 | } 24 | 25 | throw func make_lambda() -> Produce { 26 | 27 | let a = 1024; 28 | let b = 512; 29 | 30 | let lambda = closure(arg1: uint, arg2: uint) -> string { 31 | // this lambda has no captures 32 | 33 | let _ = arg1 + arg2; 34 | let b = "Closures are so cool!"; 35 | 36 | b 37 | }; 38 | 39 | if lambda(a, b) != "Closures are so cool!" { 40 | return error; 41 | } 42 | 43 | lambda 44 | } 45 | -------------------------------------------------------------------------------- /minitests/stack-safety.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | 6 | write func main() { 7 | asm(tests().1) { log }; 8 | } 9 | 10 | func tests() -> string { 11 | 12 | asm("on stack") { noop }; 13 | 14 | let _ = smash_stack(); 15 | smash_stack2(); 16 | smash_stack3(); 17 | smash_stack4(); 18 | 19 | let top = asm() string { noop }; 20 | 21 | if (top != "on stack") { 22 | return "broke the stack"; 23 | } 24 | 25 | return ""; 26 | } 27 | 28 | sensitive func load(_: uint, __: uint) {} 29 | 30 | sensitive func smash_stack() -> uint { 31 | asm({ return 4; 4 }, {4}) { noop }; 32 | return 8; 33 | } 34 | 35 | sensitive func smash_stack2() { 36 | asm({ return; 4 }, {4}) { noop }; 37 | } 38 | 39 | sensitive func smash_stack3() { 40 | load({ return; 8 }, { 8 }); 41 | } 42 | 43 | sensitive func smash_stack4() { 44 | load({ 8 }, { return; 8 }); 45 | } 46 | -------------------------------------------------------------------------------- /minitests/wide-tuples.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | write func main() { 6 | 7 | if let Some(issue) = test_1() { 8 | asm(issue.1) { log }; 9 | } else if let Some(issue) = test_2() { 10 | asm(issue.1) { log }; 11 | } else { 12 | asm("".1) { log }; 13 | } 14 | } 15 | 16 | func test_1() -> option { 17 | 18 | let a = Some("uh oh, not replaced"); 19 | 20 | a = (0, 1, 2, 3, 4, 5, 6, None, a).7; 21 | 22 | a 23 | } 24 | 25 | func test_2() -> option { 26 | 27 | let result = "should be overwritten"; 28 | let _ = result; 29 | 30 | let pair = (32, 64, "carry me"); 31 | 32 | let sum = pair.0 + pair.1; 33 | 34 | if sum != 96 { 35 | return Some("dot doesn't work"); 36 | } 37 | 38 | let (_, __, *result) = pair; 39 | 40 | if result != "carry me" { 41 | return Some("Did not let-assign correctly"); 42 | } 43 | 44 | let (result) = (32, 32); 45 | 46 | if result != (32, 32) { 47 | return Some("Did not single-shadow correctly"); 48 | } 49 | 50 | let super_wide = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "nest me :)"); 51 | 52 | let result = super_wide.10; 53 | 54 | if result != "nest me :)" { 55 | return Some("Wide dot doesn't work"); 56 | } 57 | 58 | let (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, result) = super_wide; 59 | 60 | let (*sum) = _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9; 61 | 62 | if sum != 45 || result != "nest me :)" { 63 | return Some("Wide let doesn't work"); 64 | } 65 | 66 | None 67 | } 68 | -------------------------------------------------------------------------------- /parameters.json: -------------------------------------------------------------------------------- 1 | {"ArbitrumNonZeroBalanceCallStipend":"9ddf92bfb34ea956bd2199dfcbcbc7ebb7ef22dac4dbd0064a22e9631c243f17","AvmGasPerArbGas":"37be7cf123383ad7fb95509cc9e3abecbb313969afc2457c39cf1f8621bf841c","ChainID":"625a539a292ffb7be735d7db3f6b9b9c88b9ced262ae58163dfb75ba308551f2","ChallengePeriodEthBlocks":"ece83efd40ff2c9c69c406a49cbd4e7019fc5807db782ea78e3ee698923a412c","CongestionFeeRecipient":"822944628d8665d34890f3c669fc162e9bf93068c279a5d6542e1d1b9b6d1f26","DefaultAggregator":"5178bebc4d6744e9916ccf5faa7c484f5892ce719af7ea41693cb261581e0a53","DefaultBaseTxFeeL1Gas":"0a73f9270d1447a00b972eacd6829bb9d92baa0f72da884cc731a606623ca831","EnableL1ContractAddressAliasing":"6f24e8f3152885c69e929c548f052c0c51ee31396555c21e6da82e27d0c397f7","FeesEnabled":"5aa84b26614b32a0bee45accd6c51befe94fd6208620f6cb514e955cf441c646","GasPoolMax":"3df39358f9f74e8897994c6f9649a2fa02f92e0afe8357bcdcfacb7369bce065","GasPriceOracle":"b805b4628c7eb76eb0fee0af85c84f1192e9325a3357e06de8523daaa6734de1","L1GasPerL1CalldataUnit":"91cf58d7af9d7e3eed19d6b7a6dc3c9eddedf3cf9d25ee022d5ee1a58b0f7c7c","L1GasPerStorage":"018bc34256aaf8406eb4a53c816f95a676597ea33af650ae5256924fe0d43260","L1GasPriceEstimateWeightDenominator":"219f6eab3948f4a940fbea8b9d7fd38b5e8d4b64067676a3906450292b55d2de","L1GasPriceEstimateWeightNumerator":"60035087797bd0a129ebfcec82f6b97df2994da9484de348a80af91e00d20201","MaxBaseTxFeeL1Gas":"e1eaf66f763118356c8c0b43381642f36faab87ee4e693e57a0d0a70c2fa244f","MinBaseTxFeeL1Gas":"0dd10fe2206914ebce467a72341083f65b3cceaa96f2bb59b24c443042075c37","NetworkFeeRecipient":"21ceee4cd18db4d762a492f0841a0eb4b312d6a14b32a97f66fc778b7767d830","NetworkFeeShareDenominator":"559e19b1d93cb33689d0f2606478477012d74e2601f7e987829fa1e6c16169ad","NetworkFeeShareNumerator":"6b3db84bd2c2e8bb1c6279c861bc6c92714ff58f879ad7d7fca792d0ff5cce1b","RetryablePriceBase":"8309860d22572982303b784e4bcd1672f5b7b8e9741b6004e934ce98a53e8ade","RetryablePricePerByteDenominator":"5d8498650e90b8e3e471383be6b36929d4fdba48a8cec72d573ecc5c8770f039","RetryablePricePerByteNumerator":"f9827eae29cdb89e1d2d7f6e49068122e371c05630d817e9a0a2c3b3f449ed42","RetryableTxLifetimeSeconds":"264c798351aaca449c7e999ab7b21dca7e4e2cd396131b634fbd88f47d141214","RetryableTxRepriceIntervalSeconds":"3d8ac6c8b7b8ef8fa530f17ee13bcca1db47cee428a85e784b4bd1c4c6e0de8f","SecondsPerBlockDenominator":"e3b3fad2ea885b78d3ef0d7edb5c0449c523402f4e07c091752402e41be184ce","SecondsPerBlockNumerator":"8aa625c3de620adef16b489aaee7d6dbd5545dc8adff0d941bc8488ff02458ed","SecondsPerSend":"a5d08c289ab8f7209643084b32418d4477c44faa700e2e184c3dd5911e4c692d","SpeedLimitPerSecond":"44edde536e3f4df60ef7ec43bc928f7438fa9804cb64ef42045ab4781bbc0bb3","TxGasLimit":"5bde5fa4722a26a37dced3593fb92e3f3cf748e22c4d4e4de2f6e72d81a4673d"} 2 | -------------------------------------------------------------------------------- /replayTests/evm_eval_sha256.aoslog: -------------------------------------------------------------------------------- 1 | {"format_version":1,"inbox":[{"Tuple":[{"Int":"4"},{"Int":"186a0"},{"Int":"989680"},{"Int":"0"},{"Int":"0"},{"Int":"77359400"},{"Int":"40"},{"Buffer":"625a539a292ffb7be735d7db3f6b9b9c88b9ced262ae58163dfb75ba308551f2000000000000000000000000000000000000000000000000000000000000a4b1"}]},{"Tuple":[{"Int":"3"},{"Int":"186a0"},{"Int":"989680"},{"Int":"401"},{"Int":"1"},{"Int":"77359400"},{"Int":"a2"},{"Buffer":"0000000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000000000000077359400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000cc"}]}],"logs":[{"Tuple":[{"Int":"1"},{"Int":"0"},{"Int":"0"},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"1"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"112a8800"}]},{"Int":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Tuple":[{"Int":"3"},{"Int":"1"},{"Int":"186a0"},{"Int":"989680"},{"Int":"1111000000000000000000000000000000001512"},{"Int":"7da3cdb0e6719e006a5ddcdc497343c054123d623187d251ac2652299e398e26"},{"Tuple":[{"Int":"a2"},{"Buffer":"0000000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000000000000077359400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000cc"}]},{"Tuple":[{"Tuple":[{"Int":"1"},{"Int":"0"},{"Int":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}]},{"Tuple":[{"Int":"1"},{"Tuple":[{"Tuple":[{"Int":"0"}]},{"Int":"2e8"}]}]},{"Int":"0"},{"Tuple":[{"Int":"0"}]}]}]},{"Tuple":[{"Int":"0"},{"Tuple":[{"Int":"20"},{"Buffer":"1dd8312636f6a0bf3d21fa2855e63072507453e93a5ced4301b364e91c9d87d6"}]},{"Tuple":[]}]},{"Tuple":[{"Int":"e9"},{"Int":"0"}]},{"Tuple":[{"Int":"e9"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"1"},{"Int":"2e8"},{"Int":"0"},{"Int":"e9"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Int":"0"},{"Int":"0"}]},{"Int":"401"}]}],"sends":[],"total_gas":936815} 2 | -------------------------------------------------------------------------------- /replayTests/payment_to_empty_address.aoslog: -------------------------------------------------------------------------------- 1 | {"format_version":1,"inbox":[{"Tuple":[{"Int":"4"},{"Int":"186a0"},{"Int":"989680"},{"Int":"0"},{"Int":"0"},{"Int":"77359400"},{"Int":"40"},{"Buffer":"625a539a292ffb7be735d7db3f6b9b9c88b9ced262ae58163dfb75ba308551f2000000000000000000000000000000000000000000000000000000000000a4b1"}]},{"Tuple":[{"Int":"7"},{"Int":"186a0"},{"Int":"989680"},{"Int":"401"},{"Int":"1"},{"Int":"77359400"},{"Int":"81"},{"Buffer":"010000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000007735940000000000000000000000000011110000000000000000000000000000000015120000000000000000000000000000000000000000000000000000000000004e20"}]},{"Tuple":[{"Int":"3"},{"Int":"186a0"},{"Int":"989680"},{"Int":"401"},{"Int":"2"},{"Int":"77359400"},{"Int":"a1"},{"Buffer":"00000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000077359400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010920000000000000000000000000000000000000000000000000000000000002710"}]}],"logs":[{"Tuple":[{"Int":"1"},{"Int":"0"},{"Int":"0"},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"1"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"112a8800"}]},{"Int":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Tuple":[{"Int":"7"},{"Int":"1"},{"Int":"186a0"},{"Int":"989680"},{"Int":"1111000000000000000000000000000000001512"},{"Int":"205bae445d323d41c2a70638ff83cb0c4359544903850978ded8943e052cf973"},{"Tuple":[{"Int":"81"},{"Buffer":"010000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000007735940000000000000000000000000011110000000000000000000000000000000015120000000000000000000000000000000000000000000000000000000000004e20"}]},{"Tuple":[{"Tuple":[{"Int":"1"},{"Int":"0"},{"Int":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}]},{"Tuple":[{"Int":"1"},{"Tuple":[{"Tuple":[{"Int":"0"}]},{"Int":"2a0"}]}]},{"Int":"0"},{"Tuple":[{"Int":"0"}]}]}]},{"Tuple":[{"Int":"0"},{"Tuple":[{"Int":"0"},{"Buffer":""}]},{"Tuple":[]}]},{"Tuple":[{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"1"},{"Int":"2a0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Int":"0"},{"Int":"0"}]},{"Int":"401"}]},{"Tuple":[{"Int":"0"},{"Tuple":[{"Int":"3"},{"Int":"1"},{"Int":"186a0"},{"Int":"989680"},{"Int":"1111000000000000000000000000000000001512"},{"Int":"f360449325ac831bf8154e114338748d4fdff18f4d0d0b0a7ba086c9fef31a72"},{"Tuple":[{"Int":"a1"},{"Buffer":"00000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000077359400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010920000000000000000000000000000000000000000000000000000000000002710"}]},{"Tuple":[{"Tuple":[{"Int":"2"},{"Int":"0"},{"Int":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}]},{"Tuple":[{"Int":"1"},{"Tuple":[{"Tuple":[{"Int":"0"}]},{"Int":"2fc"}]}]},{"Int":"0"},{"Tuple":[{"Int":"0"}]}]}]},{"Tuple":[{"Int":"0"},{"Tuple":[{"Int":"0"},{"Buffer":""}]},{"Tuple":[]}]},{"Tuple":[{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"1"},{"Int":"0"}]},{"Tuple":[{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"1"},{"Int":"2fc"},{"Int":"0"},{"Int":"0"}]},{"Tuple":[{"Int":"0"},{"Int":"0"},{"Int":"0"},{"Int":"0"}]},{"Int":"0"},{"Int":"0"}]},{"Int":"401"}]}],"sends":[],"total_gas":939596} 2 | -------------------------------------------------------------------------------- /src/buffertests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | /* 6 | use crate::mavm::{zero_hash, Buffer}; 7 | use crate::uint256::Uint256; 8 | 9 | use rand::Rng; 10 | use std::convert::TryFrom; 11 | 12 | #[test] 13 | fn test_hash_test() { 14 | let mut buf: Vec = Vec::new(); 15 | buf.resize(64, 0); 16 | for i in 0..64 { 17 | buf[i] = u8::try_from(i).unwrap(); 18 | } 19 | let u1 = Uint256::from_bytes(&buf[0..32]).avm_hash(); 20 | let u2 = Uint256::from_bytes(&buf[32..64]).avm_hash(); 21 | assert_eq!(Uint256::avm_hash2(&u1, &u2), hash_buffer2(buf.to_vec())); 22 | 23 | let mut rng = rand::thread_rng(); 24 | buf.resize(8 * 1024, 0); 25 | for i in 0..8 * 1024 { 26 | buf[i] = rng.gen(); 27 | } 28 | assert_eq!(hash_buffer(&buf, true), hash_buffer2(buf.to_vec())); 29 | } 30 | */ 31 | -------------------------------------------------------------------------------- /src/compile/globals.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved 3 | */ 4 | 5 | use crate::compile::{CompileError, TypeCheckedModule}; 6 | use crate::console::Color; 7 | use std::collections::HashMap; 8 | use std::fs::File; 9 | use std::io::{BufRead, BufReader}; 10 | use std::path::Path; 11 | 12 | /// Assign an ordering to the global variables 13 | pub fn order_globals( 14 | globals_path: Option<&Path>, 15 | modules: &mut Vec, 16 | ) -> Result<(), CompileError> { 17 | let mut global_count = 0; 18 | for module in modules.iter_mut() { 19 | // naively assign offsets one-by-one 20 | for global in &mut module.global_vars { 21 | global.offset = Some(global_count); 22 | global_count += 1; 23 | } 24 | } 25 | 26 | let mut globals: HashMap<_, _> = modules 27 | .iter_mut() 28 | .map(|module| module.global_vars.iter_mut()) 29 | .flatten() 30 | .map(|global| (global.name.to_owned(), global)) 31 | .collect(); 32 | 33 | macro_rules! error { 34 | (@$text:expr $(,$args:expr)* $(,)?) => { 35 | CompileError::new("globals file error", format!($text, $(Color::red($args),)*), vec![]) 36 | }; 37 | ($text:expr $(,$args:expr)* $(,)?) => { 38 | return Err(CompileError::new("globals file error", format!($text, $(Color::red($args),)*), vec![])) 39 | }; 40 | } 41 | 42 | if let Some(path) = globals_path { 43 | // Copy the order of the globals in the file provided 44 | 45 | let filename = path.display(); 46 | 47 | let file = File::open(path).map_err(|_| error!(@"failed to open {}", &filename))?; 48 | let lines: Vec<_> = BufReader::new(file).lines().enumerate().collect(); 49 | 50 | if lines.len() != global_count { 51 | error!( 52 | "{} has {} globals but {} exist", 53 | filename, 54 | lines.len(), 55 | global_count 56 | ); 57 | } 58 | 59 | for (index, line) in lines { 60 | let line = line.map_err(|err| error!(@"failed to read line {}", err))?; 61 | match globals.get_mut(&line) { 62 | Some(global) => global.offset = Some(index), 63 | None => error!("{}'s global {} doesn't exist", filename, line), 64 | } 65 | } 66 | } 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /src/compile/source.rs: -------------------------------------------------------------------------------- 1 | // Code in this file is derived from gluon (MIT license) 2 | // https://github.com/gluon-lang/gluon/blob/f8326d21a14b5f21d203e9c43fa5bb7f0688a74c/base/src/source.rs#L22-L35 3 | 4 | //! Module containing types and functions for mapping between byte indexes and line and column 5 | //! locations 6 | 7 | use crate::pos::{BytePos, Column, Line, Location}; 8 | 9 | /// Type which provides a bidirectional mapping between byte offsets and line and column locations 10 | /// for some source file 11 | #[derive(Clone, Debug)] 12 | pub struct Lines { 13 | starting_bytes: Vec, 14 | end: usize, 15 | } 16 | 17 | impl Lines { 18 | /// Creates a mapping for `src` 19 | pub fn new(src: I) -> Lines 20 | where 21 | I: IntoIterator, 22 | { 23 | use std::iter; 24 | 25 | let mut len = 0; 26 | let starting_bytes = { 27 | let input_indices = src 28 | .into_iter() 29 | .inspect(|_| len += 1) 30 | .enumerate() 31 | .filter(|&(_, b)| b == b'\n') 32 | .map(|(i, _)| BytePos::from(i + 1)); // index of first char in the line 33 | 34 | iter::once(BytePos::from(0)).chain(input_indices).collect() 35 | }; 36 | Lines { 37 | starting_bytes, 38 | end: len, 39 | } 40 | } 41 | 42 | /// Returns the byte offset of the start of `line_number` 43 | pub fn line(&self, line_number: Line) -> Option { 44 | let line_number = line_number.to_usize(); 45 | self.starting_bytes.get(line_number).cloned() 46 | } 47 | 48 | /// Returns the line and column location of `byte` 49 | pub fn location(&self, byte: BytePos, file_id: u64) -> Option { 50 | if byte.to_usize() <= self.end { 51 | let line_index = self.line_number_at_byte(byte); 52 | 53 | self.line(line_index).map(|line_byte| Location { 54 | line: line_index, 55 | column: Column::from((byte - line_byte).to_usize()), 56 | absolute: byte, 57 | file_id, 58 | }) 59 | } else { 60 | None 61 | } 62 | } 63 | 64 | /// Returns which line `byte` points to 65 | pub fn line_number_at_byte(&self, byte: BytePos) -> Line { 66 | let num_lines = self.starting_bytes.len(); 67 | 68 | Line::from( 69 | (0..num_lines) 70 | .filter(|&i| self.starting_bytes[i] > byte) 71 | .map(|i| i - 1) 72 | .next() 73 | .unwrap_or(num_lines - 1), 74 | ) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/contracttemplates.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | use crate::mavm::Value; 6 | use crate::run::_bytestack_from_bytes; 7 | use bytes::{BufMut, BytesMut}; 8 | use std::{fs::File, io::Write, path::Path}; 9 | 10 | pub fn generate_contract_template_file_or_die(path: &Path) { 11 | let display = path.display(); 12 | let mut file = match File::create(&path) { 13 | Err(why) => panic!("couldn't create {}: {}", display, why), 14 | Ok(file) => file, 15 | }; 16 | 17 | if let Err(why) = file.write_all(&mini_code_for_templates().freeze()[..]) { 18 | panic!("couldn't write to {}: {}", display, why) 19 | } 20 | } 21 | 22 | fn mini_code_for_avm_value(buf: &mut BytesMut, val: Value) { 23 | match val { 24 | Value::Int(ui) => { 25 | buf.extend(ui.to_string().as_bytes()); 26 | } 27 | Value::Tuple(tup) => { 28 | buf.put_u8(b'('); 29 | for t in &*tup { 30 | mini_code_for_avm_value(buf, t.clone()); 31 | buf.put_u8(b','); 32 | } 33 | buf.put_u8(b')'); 34 | } 35 | _ => { 36 | panic!("Can't generate code for this AVM value: {}", val); 37 | } 38 | } 39 | } 40 | 41 | fn mini_code_for_bytes(buf: &mut BytesMut, b: &[u8]) { 42 | mini_code_for_avm_value(buf, _bytestack_from_bytes(b)); 43 | } 44 | 45 | fn mini_code_getter_for_bytes(buf: &mut BytesMut, name: &str, b: &[u8]) { 46 | buf.put(&b"public func "[..]); 47 | buf.put(name.as_bytes()); 48 | buf.put(&b"() -> MarshalledBytes {\n return unsafecast("[..]); 49 | mini_code_for_bytes(buf, b); 50 | buf.put(&b");\n}\n\n"[..]); 51 | } 52 | 53 | fn mini_code_for_templates() -> BytesMut { 54 | let mut buf = BytesMut::with_capacity(1024); 55 | buf.put(&b"// DO NOT EDIT -- this is machine-generated code.\n\n"[..]); 56 | buf.put(&b"use std::bytearray::MarshalledBytes;\nuse std::storageMap::StorageMap;\n\n"[..]); 57 | buf.put(&b"use std::storageMap::storageMap_new;\n\n"[..]); 58 | 59 | mini_code_for_arbinfo(&mut buf); 60 | buf 61 | } 62 | 63 | fn mini_code_for_arbinfo(buf: &mut BytesMut) { 64 | mini_code_getter_for_bytes( 65 | buf, 66 | "getArbInfoCode", 67 | &get_deployed_bytecode(Path::new( 68 | "contracts/artifacts/arbos/builtin/ArbInfo.sol/ArbInfo.json", 69 | )), 70 | ); 71 | 72 | buf.put( 73 | &b"public func getArbInfoStorage() -> StorageMap {\n return storageMap_new();\n}\n\n"[..], 74 | ); 75 | 76 | buf.put(&b"public func getArbInfoAddress() -> address {\n return address(0x65);\n}\n\n"[..]) 77 | } 78 | 79 | fn get_deployed_bytecode(path: &Path) -> Vec { 80 | let contents = 81 | std::fs::read_to_string(path.clone()).expect("Something went wrong reading the file"); 82 | let json: serde_json::Value = 83 | serde_json::from_str(&contents).expect("JSON was not well-formatted"); 84 | let code_str = json["deployedBytecode"].to_string(); 85 | hex::decode(&code_str[3..(code_str.len() - 1)]).unwrap() 86 | } 87 | -------------------------------------------------------------------------------- /src/evm/benchmarks.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | use crate::evm::abi::deploy_add; 6 | use crate::run::load_from_file; 7 | use crate::uint256::Uint256; 8 | use std::path::Path; 9 | 10 | pub fn make_benchmarks() -> Result<(), ethabi::Error> { 11 | let benchmarks: Vec<( 12 | fn(u64, &Path) -> Result, 13 | u64, 14 | &str, 15 | &str, 16 | )> = vec![ 17 | (benchmark_boot, 1, "boot ArbOS", "boot"), 18 | (benchmark_add, 100, "100 null txs", "nulltx_100"), 19 | (benchmark_add, 1000, "1000 null txs", "nulltx_1000"), 20 | ( 21 | benchmark_add_batched, 22 | 100, 23 | "100 signed batched null txs", 24 | "nulltx_batch_100", 25 | ), 26 | ( 27 | benchmark_add_batched, 28 | 500, 29 | "500 signed batched null txs", 30 | "nulltx_batch_500", 31 | ), 32 | ]; 33 | 34 | for (bm_func, iterations, description, filename) in benchmarks { 35 | let filename = format!("benchmarks/{}.aoslog", filename); 36 | let gas_used = bm_func(iterations, Path::new(&filename))?; 37 | println!("ArbGas for {}: {}", description, gas_used); 38 | eprint!("{}\t", gas_used); 39 | } 40 | eprintln!(); 41 | Ok(()) 42 | } 43 | 44 | pub fn benchmark_boot(_iterations: u64, log_to: &Path) -> Result { 45 | let mut machine = load_from_file(Path::new("arb_os/arbos.mexe")); 46 | machine.start_at_zero(false); 47 | 48 | let gas_used = machine.run(None); 49 | machine 50 | .runtime_env 51 | .recorder 52 | .to_file(log_to, machine.get_total_gas_usage().to_u64().unwrap()) 53 | .unwrap(); 54 | Ok(gas_used) 55 | } 56 | 57 | pub fn benchmark_add(iterations: u64, log_to: &Path) -> Result { 58 | let mut machine = load_from_file(Path::new("arb_os/arbos.mexe")); 59 | machine.start_at_zero(false); 60 | 61 | let my_addr = Uint256::from_u64(1025); 62 | let contract = deploy_add(&mut machine)?; 63 | 64 | for _ in 0..iterations { 65 | let _result = contract 66 | .call_function( 67 | my_addr.clone(), 68 | "add", 69 | vec![ 70 | ethabi::Token::Uint(ethabi::Uint::one()), 71 | ethabi::Token::Uint(ethabi::Uint::one()), 72 | ] 73 | .as_ref(), 74 | &mut machine, 75 | Uint256::zero(), 76 | false, 77 | ) 78 | .unwrap(); 79 | } 80 | 81 | machine 82 | .runtime_env 83 | .recorder 84 | .to_file(log_to, machine.get_total_gas_usage().to_u64().unwrap()) 85 | .unwrap(); 86 | Ok(machine.get_total_gas_usage().to_u64().unwrap()) 87 | } 88 | 89 | pub fn benchmark_add_batched(iterations: u64, log_to: &Path) -> Result { 90 | let mut machine = load_from_file(Path::new("arb_os/arbos.mexe")); 91 | machine.start_at_zero(false); 92 | 93 | let wallet = machine.runtime_env.new_wallet(); 94 | 95 | let contract = deploy_add(&mut machine)?; 96 | 97 | let mut batch = machine.runtime_env.new_batch(); 98 | 99 | for _ in 0..iterations { 100 | let _tx_id = contract 101 | .add_function_call_to_batch( 102 | &mut batch, 103 | "add", 104 | vec![ 105 | ethabi::Token::Uint(ethabi::Uint::one()), 106 | ethabi::Token::Uint(ethabi::Uint::one()), 107 | ] 108 | .as_ref(), 109 | &mut machine, 110 | Uint256::zero(), 111 | &wallet, 112 | ) 113 | .unwrap(); 114 | } 115 | 116 | machine 117 | .runtime_env 118 | .insert_batch_message(Uint256::from_usize(1025), &batch); 119 | 120 | machine.run(None); 121 | 122 | machine 123 | .runtime_env 124 | .recorder 125 | .to_file(log_to, machine.get_total_gas_usage().to_u64().unwrap()) 126 | .unwrap(); 127 | Ok(machine.get_total_gas_usage().to_u64().unwrap()) 128 | } 129 | -------------------------------------------------------------------------------- /src/optimize/peephole.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | use crate::compile::DebugInfo; 6 | use crate::mavm::{AVMOpcode, Instruction, Opcode, Value}; 7 | use crate::opcode; 8 | 9 | /// Find pairs of instructions whose combined effects could be re-expressed more cheaply 10 | pub fn peephole(mut code: Vec) -> Vec { 11 | use AVMOpcode::*; 12 | 13 | code.push(opcode!(Noop, Value::from(0))); 14 | 15 | let mut code = code.into_iter().peekable(); 16 | let mut out = Vec::with_capacity(code.len()); 17 | 18 | macro_rules! drop { 19 | ($count:expr) => {{ 20 | for _ in 0..$count { 21 | out.pop(); 22 | } 23 | }}; 24 | } 25 | 26 | while code.peek().is_some() { 27 | macro_rules! retry { 28 | () => {{ 29 | out.push(code.next().unwrap()); 30 | continue; 31 | }}; 32 | } 33 | 34 | if out.len() < 2 { 35 | retry!(); 36 | } 37 | 38 | let curr = &out[out.len() - 2]; 39 | let next = &out[out.len() - 1]; 40 | 41 | let curr_op = match &curr.opcode { 42 | Opcode::AVMOpcode(op) => op, 43 | _ => retry!(), 44 | }; 45 | let next_op = match &next.opcode { 46 | Opcode::AVMOpcode(op) => op, 47 | _ => retry!(), 48 | }; 49 | 50 | match (curr_op, next_op, &curr.immediate, &next.immediate) { 51 | (IsZero, IsZero, None, None) => drop!(2), 52 | (Dup0, Pop, None, None) => drop!(2), 53 | (Dup0, Swap1, _, None) => drop!(1), 54 | (Swap1, Swap1, None, None) => drop!(2), 55 | (Swap2, Swap2, None, None) => drop!(2), 56 | (Xget, Pop, Some(_), None) => drop!(2), 57 | (AuxPop, AuxPush, None, None) => drop!(2), 58 | (AuxPush, AuxPop, None, None) => drop!(2), 59 | (AuxPush, AuxPop, Some(value), None) | (AuxPop, AuxPush, Some(value), None) => { 60 | let value = value.clone(); 61 | drop!(2); 62 | out.push(opcode!(Noop, value)); 63 | } 64 | (AuxPush, AuxPop, None, Some(value)) => { 65 | let value = value.clone(); 66 | drop!(2); 67 | out.push(opcode!(Swap1, value)); 68 | } 69 | (Xset, Xget, Some(a), Some(b)) if a == b => { 70 | let offset = a.clone(); 71 | drop!(2); 72 | out.push(opcode!(Dup0)); 73 | out.push(opcode!(Xset, offset)); 74 | } 75 | (Tget, Pop, Some(_), None) => { 76 | drop!(2); 77 | out.push(opcode!(Pop)); 78 | } 79 | (_, Noop, _, None) => drop!(1), 80 | (_, Pop, _, Some(_)) => drop!(1), 81 | (Noop, _, Some(value), None) => { 82 | let mut other = next.clone(); 83 | other.immediate = Some(value.clone()); 84 | drop!(2); 85 | out.push(other); 86 | } 87 | (Pop, _, Some(_), _) 88 | | (Swap1, Add, None, None) 89 | | (Swap1, Mul, None, None) 90 | | (Swap1, Equal, None, None) 91 | | (Swap1, BitwiseAnd, None, None) 92 | | (Swap1, BitwiseOr, None, None) 93 | | (Swap1, BitwiseXor, None, None) => { 94 | out.swap_remove(out.len() - 2); 95 | } 96 | _ => { 97 | out.push(code.next().unwrap()); 98 | } 99 | } 100 | } 101 | 102 | out.pop(); // pop the Noop 103 | out 104 | } 105 | -------------------------------------------------------------------------------- /src/stringtable.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | use serde::{Deserialize, Serialize}; 6 | use std::collections::HashMap; 7 | 8 | pub type StringId = usize; 9 | 10 | /// Maps `String`s to `usize` IDs. 11 | #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] 12 | pub struct StringTable { 13 | next_id: StringId, 14 | table: HashMap, 15 | by_id: Vec, 16 | } 17 | 18 | impl StringTable { 19 | pub fn new() -> Self { 20 | let table: HashMap = HashMap::new(); 21 | let by_id = Vec::new(); 22 | StringTable { 23 | next_id: 0, 24 | table, 25 | by_id, 26 | } 27 | } 28 | 29 | /// Returns the `StringID` associated with `name` if it exists, if not creates a new entry and 30 | /// returns the newly created ID. 31 | pub fn get(&mut self, name: String) -> StringId { 32 | match self.table.get(&name) { 33 | Some(id) => *id, 34 | None => { 35 | let new_id = self.next_id; 36 | self.next_id += 1; 37 | self.table.insert(name.clone(), new_id); 38 | self.by_id.push(name); 39 | new_id 40 | } 41 | } 42 | } 43 | 44 | /// If an ID exists, returns it, if not returns `None`. 45 | pub fn get_if_exists(&self, name: &str) -> Option { 46 | self.table.get(name).cloned() 47 | } 48 | 49 | /// Takes a `usize` ID and returns the associated `String` 50 | pub fn name_from_id(&self, name: StringId) -> &String { 51 | &self.by_id[name as usize] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stdlib/addressSetTest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::addressSet::setOfAddresses_emptySet; 6 | use std::addressSet::setOfAddresses_all; 7 | use std::addressSet::setOfAddresses_isEmpty; 8 | use std::addressSet::setOfAddresses_isAll; 9 | use std::addressSet::setOfAddresses_add; 10 | use std::addressSet::setOfAddresses_remove; 11 | use std::addressSet::setOfAddresses_contains; 12 | 13 | use std::addressSet::addressPairSet_new; 14 | use std::addressSet::addressPairSet_set; 15 | use std::addressSet::addressPairSet_lookup; 16 | 17 | 18 | write func main() { 19 | asm(tests()) { log }; 20 | } 21 | 22 | func tests() -> uint { 23 | let (addr1, addr2, addr3, addr4, addr5) = (address(1), address(2), address(3), address(4), address(5)); 24 | 25 | let empty = setOfAddresses_emptySet(); 26 | let all = setOfAddresses_all(); 27 | 28 | if setOfAddresses_contains(empty, addr1) { 29 | return 1; 30 | } 31 | 32 | if !setOfAddresses_isEmpty(empty) { 33 | return 2; 34 | } 35 | 36 | if setOfAddresses_isEmpty(all) { 37 | return 3; 38 | } 39 | 40 | if !setOfAddresses_contains(all, addr1) { 41 | return 4; 42 | } 43 | 44 | if setOfAddresses_isAll(empty) { 45 | return 5; 46 | } 47 | 48 | if !setOfAddresses_isAll(all) { 49 | return 6; 50 | } 51 | 52 | let set1 = setOfAddresses_add(empty, addr1); 53 | set1 = setOfAddresses_add(set1, addr2); 54 | if setOfAddresses_contains(set1, addr3) { 55 | return 7; 56 | } 57 | if ! setOfAddresses_contains(set1, addr1) { 58 | return 8; 59 | } 60 | if ! setOfAddresses_contains(set1, addr2) { 61 | return 9; 62 | } 63 | 64 | set1 = setOfAddresses_remove(set1, addr3); // remove address that isn't in the set 65 | if setOfAddresses_contains(set1, addr3) { 66 | return 10; 67 | } 68 | if !setOfAddresses_contains(set1, addr1) { 69 | return 11; 70 | } 71 | if !setOfAddresses_contains(set1, addr2) { 72 | return 12; 73 | } 74 | 75 | set1 = setOfAddresses_remove(set1, addr1); 76 | 77 | if setOfAddresses_contains(set1, addr3) { 78 | return 13; 79 | } 80 | if setOfAddresses_contains(set1, addr1) { 81 | return 14; 82 | } 83 | if ! setOfAddresses_contains(set1, addr2) { 84 | return 15; 85 | } 86 | 87 | let aps = addressPairSet_new(); 88 | if addressPairSet_lookup(aps, addr4, addr5) { 89 | return 100; 90 | } 91 | 92 | aps = addressPairSet_set(aps, Some(addr1), None
, true); 93 | aps = addressPairSet_set(aps, None
, Some(addr2), true); 94 | aps = addressPairSet_set(aps, Some(addr3), Some(addr4), true); 95 | if !addressPairSet_lookup(aps, addr1, addr5) { 96 | return 101; 97 | } 98 | if !addressPairSet_lookup(aps, addr5, addr2) { 99 | return 102; 100 | } 101 | if !addressPairSet_lookup(aps, addr3, addr4) { 102 | return 103; 103 | } 104 | if addressPairSet_lookup(aps, addr4, addr3) { 105 | return 104; 106 | } 107 | if addressPairSet_lookup(aps, addr2, addr5) { 108 | return 105; 109 | } 110 | if addressPairSet_lookup(aps, addr5, addr1) { 111 | return 106; 112 | } 113 | 114 | aps = addressPairSet_set(aps, Some(addr1), None
, false); 115 | if addressPairSet_lookup(aps, addr1, addr5) { 116 | return 111; 117 | } 118 | if !addressPairSet_lookup(aps, addr5, addr2) { 119 | return 112; 120 | } 121 | if !addressPairSet_lookup(aps, addr3, addr4) { 122 | return 113; 123 | } 124 | if addressPairSet_lookup(aps, addr4, addr3) { 125 | return 114; 126 | } 127 | if addressPairSet_lookup(aps, addr2, addr5) { 128 | return 115; 129 | } 130 | if addressPairSet_lookup(aps, addr5, addr1) { 131 | return 116; 132 | } 133 | 134 | 0 135 | } 136 | -------------------------------------------------------------------------------- /stdlib/blstest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::bytearray_new; 6 | use std::bytearray::bytearray_setByte; 7 | 8 | use std::bls::bls_hashToPoint; 9 | use std::bls::bls_verifySingleSig; 10 | 11 | 12 | throw write func main() { 13 | asm(tests().1) { log }; 14 | } 15 | 16 | throw func tests() -> string { 17 | let domain2 = bytes32(0x2d889d03243d367c56457383bb04bcdadff3a522dbf9c97145d0a58c1e88d6f9); 18 | 19 | let message2 = bytearray_new(12); 20 | message2 = bytearray_setByte(message2,0,0x46); 21 | message2 = bytearray_setByte(message2,1,0xf5); 22 | message2 = bytearray_setByte(message2,2,0xa4); 23 | message2 = bytearray_setByte(message2,3,0x33); 24 | message2 = bytearray_setByte(message2,4,0x9d); 25 | message2 = bytearray_setByte(message2,5,0x68); 26 | message2 = bytearray_setByte(message2,6,0x7a); 27 | message2 = bytearray_setByte(message2,7,0x8c); 28 | message2 = bytearray_setByte(message2,8,0x2f); 29 | message2 = bytearray_setByte(message2,9,0x7e); 30 | message2 = bytearray_setByte(message2,10,0x1d); 31 | message2 = bytearray_setByte(message2,11,0x7b); 32 | 33 | let signature = struct { 34 | x: 0x0d9c060fbeb416fc52d052764a0774cbe6f2a998034645bf5f4c1726b0da6289, 35 | y: 0x240e4d2d8958684713d37f2f7c20a67627bbecb60450e62aeb5b2eab6d4dbbff 36 | }; 37 | 38 | let pubkey = struct { 39 | ax: 0x16d85ac6b1495562dabf0e052b323cb27095143a8dd228dceeccd94ca23fce6e, 40 | ay: 0x252f8568427a600d5e7b2db105ca1bdf8b4f10c2c98a78da764f0cdb6fa9547d, 41 | bx: 0x2bb33da900a0cc9a13e56fd0d3226c562d1fc54106754f11dc992daa153407a0, 42 | by: 0x236e9f6e5631d4800ea7f50debef4d331ea50f50e6c1c03a3efdd292e30a52ff 43 | }; 44 | 45 | let bad_signature = struct { 46 | x: 0x0e9c060fbeb416fc52d052764a0774cbe6f2a998034645bf5f4c1726b0da6289, 47 | y: 0x240e4d2d8958684713d37f2f7c20a67627bbecb60450e62aeb5b2eab6d4dbbff 48 | }; 49 | 50 | if let Some(hashedmessage) = bls_hashToPoint(domain2, message2) { 51 | if let Some(_res) = bls_verifySingleSig(hashedmessage, pubkey, signature) { 52 | // This is the expected case. 53 | } else { 54 | return "single sig is wrong"; 55 | } 56 | if let Some(_res2) = bls_verifySingleSig(hashedmessage, pubkey, bad_signature) { 57 | return "bad signature somehow checked-out"; 58 | } 59 | } else { 60 | return "hash failed"; 61 | } 62 | 63 | "" 64 | } 65 | -------------------------------------------------------------------------------- /stdlib/bufferopcodetest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | 6 | func main() { 7 | let ba = newbuffer(); 8 | let ba = setbuffer8(ba, 0, getbuffer8(ba, 0)+1); 9 | let ba = setbuffer8(ba, 100, 123); 10 | getbuffer256(ba, 100); 11 | let ba = setbuffer256(ba, 1000, 1234567891011121315161718); 12 | let ba = setbuffer256(ba, 1000, 0); 13 | let bb = newbuffer(); 14 | let bb = setbuffer8(bb, 100, 123); 15 | let bb = setbuffer8(bb, 100, 0); 16 | let bc = newbuffer(); 17 | let bc = setbuffer64(bc, 100, 1234567891011121314); 18 | getbuffer64(bc, 100); 19 | getbuffer8(ba, 1234567891011121314); 20 | let ba = setbuffer8(ba, 1234567891011121314, 100); 21 | getbuffer8(ba, 1234567891011121314); 22 | } 23 | -------------------------------------------------------------------------------- /stdlib/bytestream.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::ByteArray; 6 | use std::bytearray::bytearray_new; 7 | use std::bytearray::bytearray_size; 8 | use std::bytearray::bytearray_buffer; 9 | use std::bytearray::bytearray_slice; 10 | use std::bytearray::bytearray_wrap_slice; 11 | use std::bytearray::buffer_getCalldataUnits; 12 | 13 | 14 | type ByteStream = struct { 15 | buf: buffer, 16 | capacity: uint, 17 | slice: uint, 18 | currentOffset: uint, 19 | }; 20 | 21 | public func bytestream_new(contents: ByteArray) -> ByteStream { 22 | struct { 23 | buf: bytearray_buffer(contents), 24 | capacity: bytearray_size(contents), 25 | slice: bytearray_slice(contents), 26 | currentOffset: 0, 27 | } 28 | } 29 | 30 | public func bytestream_atEof(bs: ByteStream) -> bool { 31 | bs.currentOffset >= bs.capacity 32 | } 33 | 34 | public func bytestream_bytesReadSoFar(bs: ByteStream) -> uint { 35 | bs.currentOffset 36 | } 37 | 38 | public func bytestream_getCalldataUnits(bs: ByteStream, nbytes: uint) -> uint { 39 | buffer_getCalldataUnits(bs.buf, bs.slice, nbytes) 40 | } 41 | 42 | public func bytestream_bytesRemaining(bs: ByteStream) -> uint { 43 | if bs.currentOffset >= bs.capacity { 44 | 0 45 | } else { 46 | bs.capacity - bs.currentOffset 47 | } 48 | } 49 | 50 | public func bytestream_skipBytes(bs: ByteStream, nbytes: uint) -> option { 51 | let newOffset = bs.currentOffset + nbytes; 52 | if newOffset <= bs.capacity { 53 | Some(bs with { currentOffset: newOffset }) 54 | } else { 55 | None 56 | } 57 | } 58 | 59 | public func bytestream_truncate(bs: ByteStream, size: uint) -> ByteStream { 60 | if size < bs.capacity { 61 | if size < bs.currentOffset { 62 | bs with { capacity: bs.currentOffset } 63 | } else { 64 | bs with { capacity: size } 65 | } 66 | } else { 67 | bs 68 | } 69 | } 70 | 71 | public func bytestream_getByte(bs: ByteStream) -> option<(ByteStream, uint)> { 72 | if bs.currentOffset >= bs.capacity { 73 | None 74 | } else { 75 | Some(( 76 | bs with { currentOffset: bs.currentOffset+1 }, 77 | getbuffer8(bs.buf, bs.currentOffset+bs.slice) 78 | )) 79 | } 80 | } 81 | 82 | public func bytestream_get64(bs: ByteStream) -> option<(ByteStream, uint)> { 83 | if bs.currentOffset+8 > bs.capacity { 84 | None 85 | } else { 86 | Some(( 87 | bs with { currentOffset: bs.currentOffset+8 }, 88 | getbuffer64(bs.buf, bs.currentOffset+bs.slice) 89 | )) 90 | } 91 | } 92 | 93 | public func bytestream_get256(bs: ByteStream) -> option<(ByteStream, uint)> { 94 | if bs.currentOffset+32 > bs.capacity { 95 | None<(ByteStream, uint)> 96 | } else { 97 | Some(( 98 | bs with { currentOffset: bs.currentOffset+32 }, 99 | getbuffer256(bs.buf, bs.currentOffset+bs.slice) 100 | )) 101 | } 102 | } 103 | 104 | public func bytestream_getPartialWord(bs: ByteStream, nbytes: uint) -> option<(ByteStream, uint)> { 105 | // read part of the next word -- just get the first nbytes bytes of it 106 | // nbytes must be <= 32 107 | if nbytes > 32 { 108 | None 109 | } else if bs.currentOffset+nbytes > bs.capacity { 110 | None 111 | } else { 112 | Some(( 113 | bs with { currentOffset: bs.currentOffset+nbytes }, 114 | (getbuffer256(bs.buf, bs.currentOffset+bs.slice) >> (256-8*nbytes)), 115 | )) 116 | } 117 | } 118 | 119 | public func bytestream_getN(bs: ByteStream, nbytes: uint) -> option<(ByteStream, ByteArray)> { 120 | if bs.currentOffset + nbytes > bs.capacity { 121 | return None; 122 | } 123 | Some(( 124 | bs with { currentOffset: bs.currentOffset + nbytes }, 125 | bytearray_wrap_slice(bs.buf, bs.currentOffset + bs.slice, nbytes) 126 | )) 127 | } 128 | 129 | public func bytestream_getRemainingBytes(bs: ByteStream) -> ByteArray { 130 | if bs.capacity > bs.currentOffset { 131 | bytearray_wrap_slice(bs.buf, bs.currentOffset + bs.slice, bs.capacity - bs.currentOffset) 132 | } else { 133 | bytearray_new(0) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /stdlib/expandingIntArrayTest.mini: -------------------------------------------------------------------------------- 1 | 2 | use std::expandingIntArray::ExpandingIntArray; 3 | use std::expandingIntArray::expandingIntArray_new; 4 | use std::expandingIntArray::expandingIntArray_get; 5 | use std::expandingIntArray::expandingIntArray_set; 6 | use std::expandingIntArray::expandingIntArray_size; 7 | use std::expandingIntArray::expandingIntArray_getConsecutive; 8 | use std::expandingIntArray::expandingIntArray_opConsecutive; 9 | use std::expandingIntArray::expandingIntArray_op; 10 | 11 | use std::bytearray::opClosure; 12 | 13 | 14 | throw write func main() { 15 | asm(tests()) { log }; 16 | } 17 | 18 | throw func tests() -> uint { 19 | 20 | let a = expandingIntArray_new(); 21 | 22 | let index = 0; 23 | while index < 24 { 24 | a = expandingIntArray_set(a, index, index); 25 | index = index + 1; 26 | } 27 | 28 | let index = 0; 29 | while index < 23 { 30 | let pair = expandingIntArray_getConsecutive(a, index); 31 | if pair.0 != index || pair.1 != index + 1 { 32 | return 1; 33 | } 34 | index = index + 1; 35 | } 36 | let index = 25; 37 | while index < 1024 { 38 | let pair = expandingIntArray_getConsecutive(a, index); 39 | if pair.0 != 0 || pair.1 != 0 { 40 | return 1; 41 | } 42 | index = index + 1; 43 | } 44 | 45 | if expandingIntArray_size(a) != 64 { 46 | return 2; 47 | } 48 | 49 | a = expandingIntArray_set(a, 64, 64); 50 | if expandingIntArray_get(a, 64) != 64 { 51 | return 2; 52 | } 53 | a = expandingIntArray_set(a, 728, 728); 54 | if expandingIntArray_size(a) != 4096 { 55 | return 2; 56 | } 57 | 58 | let index = 0; 59 | while index < 4096 { 60 | let before = expandingIntArray_get(a, index); 61 | a = expandingIntArray_op(a, index, unsafecast(struct { f: addFunc, val: index, })).0; 62 | let after = expandingIntArray_get(a, index); 63 | if before != after - index { 64 | return after; 65 | } 66 | 67 | a = expandingIntArray_opConsecutive( 68 | a, 69 | index, 70 | unsafecast(struct { f: addFunc, val: index }), 71 | unsafecast(struct { f: addFunc, val: index }) 72 | ).0; 73 | 74 | if expandingIntArray_get(a, index) != after + index { 75 | return after; 76 | } 77 | 78 | index = index + 1; 79 | } 80 | if expandingIntArray_size(a) != 4096 * 8 { 81 | return expandingIntArray_size(a); 82 | } 83 | 84 | 0 85 | } 86 | 87 | func addFunc(argument: any, value: uint) -> (uint, any) { 88 | let res = value + unsafecast(argument); 89 | (res, res) 90 | } 91 | -------------------------------------------------------------------------------- /stdlib/fixedpoint.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | // This file implements fixed-point numbers, consisting of a numerator and denominator. 6 | // Some operations can only be done on values that have the same denominator. 7 | // We assume that num and denom will always be less than 2**128. If this is true, arithmetic will never overflow. 8 | 9 | use std::safeMath::safeAdd; 10 | use std::safeMath::safeSub; 11 | use std::safeMath::safeMul; 12 | use std::safeMath::trySafeAdd; 13 | use std::safeMath::trySafeMul; 14 | 15 | 16 | type FixedPoint = struct { 17 | val: uint, 18 | shiftFactor: uint, 19 | }; 20 | 21 | public func fixedPoint_new(val: uint, shiftFactor: uint) -> FixedPoint { 22 | struct { 23 | val: val, 24 | shiftFactor: shiftFactor, 25 | } 26 | } 27 | 28 | public func fixedPoint_zero() -> FixedPoint { 29 | fixedPoint_new(0, 1) 30 | } 31 | 32 | public func fixedPoint_isZero(x: FixedPoint) -> bool { 33 | x.val == 0 34 | } 35 | 36 | public throw func fixedPoint_fromUintMatch(x: uint, fp: FixedPoint) -> FixedPoint { 37 | fp with { val: safeMul(x, fp.shiftFactor) } 38 | } 39 | 40 | public func fixedPoint_getNum(x: FixedPoint) -> uint { 41 | x.val 42 | } 43 | 44 | public func fixedPoint_getDenom(x: FixedPoint) -> uint { 45 | x.shiftFactor 46 | } 47 | 48 | public throw func fixedPoint_equal(x: FixedPoint, y: FixedPoint) -> bool { 49 | safeMul(x.val, y.shiftFactor) == safeMul(y.val, x.shiftFactor) 50 | } 51 | 52 | public throw func fixedPoint_compare(x: FixedPoint, y: FixedPoint) -> int { 53 | let xProd = safeMul(x.val, y.shiftFactor); 54 | let yProd = safeMul(y.val, x.shiftFactor); 55 | if xProd > yProd { int(1) } 56 | else if xProd == yProd { int(0) } 57 | else { -int(1) } 58 | } 59 | 60 | public throw func fixedPoint_add(x: FixedPoint, y: FixedPoint) -> option { 61 | if x.shiftFactor == y.shiftFactor { 62 | Some(x with { val: safeAdd(x.val, y.val) }) 63 | } else { 64 | None 65 | } 66 | } 67 | 68 | public func fixedPoint_tryAddRescale(x: FixedPoint, y: FixedPoint) -> option { 69 | Some(fixedPoint_new( 70 | trySafeAdd(trySafeMul(x.val, y.shiftFactor)?, trySafeMul(y.val, x.shiftFactor)?)?, 71 | trySafeMul(x.shiftFactor, y.shiftFactor)?, 72 | )) 73 | } 74 | 75 | public throw func fixedPoint_plusOne(x: FixedPoint) -> FixedPoint { 76 | x with { val: safeAdd(x.val, x.shiftFactor) } 77 | } 78 | 79 | public func fixedPoint_minusOne(x: FixedPoint) -> option { 80 | if x.val >= x.shiftFactor { 81 | Some(fixedPoint_new(x.val - x.shiftFactor, x.shiftFactor)) 82 | } else { 83 | None 84 | } 85 | } 86 | 87 | public throw func fixedPoint_sub(x: FixedPoint, y: FixedPoint) -> option { 88 | if x.shiftFactor == y.shiftFactor { 89 | Some(x with { val: safeSub(x.val, y.val) }) 90 | } else { 91 | None 92 | } 93 | } 94 | 95 | public throw func fixedPoint_mul(x: FixedPoint, y: FixedPoint) -> option { 96 | if x.shiftFactor == y.shiftFactor { 97 | Some(x with { val: safeMul(x.val, y.val) / x.shiftFactor }) 98 | } else { 99 | None 100 | } 101 | } 102 | 103 | public throw func fixedPoint_mulByUint(x: FixedPoint, i: uint) -> FixedPoint { 104 | x with { val: safeMul(x.val, i) } 105 | } 106 | 107 | public throw func fixedPoint_mulByUintTrunc(x: FixedPoint, i: uint) -> uint { 108 | safeMul(x.val, i) / x.shiftFactor 109 | } 110 | 111 | public throw func fixedPoint_div(x: FixedPoint, y: FixedPoint) -> option { 112 | if (x.shiftFactor == y.shiftFactor) && (x.shiftFactor != 0) { 113 | Some(fixedPoint_new(x.val, y.val)) 114 | } else { 115 | let denom = safeMul(x.shiftFactor, y.val); 116 | if denom == 0 { 117 | None 118 | } else { 119 | Some(fixedPoint_new(safeMul(x.val, y.shiftFactor), denom)) 120 | } 121 | } 122 | } 123 | 124 | public func fixedPoint_integerPart(x: FixedPoint) -> uint { 125 | x.val / x.shiftFactor 126 | } 127 | 128 | public func fixedPoint_fractionalPart(x: FixedPoint) -> FixedPoint { 129 | x with { val: x.val % x.shiftFactor } 130 | } 131 | -------------------------------------------------------------------------------- /stdlib/fixedpointtest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::fixedpoint::fixedPoint_new; 6 | use std::fixedpoint::fixedPoint_fromUintMatch; 7 | use std::fixedpoint::fixedPoint_integerPart; 8 | use std::fixedpoint::fixedPoint_add; 9 | use std::fixedpoint::fixedPoint_sub; 10 | use std::fixedpoint::fixedPoint_mul; 11 | use std::fixedpoint::fixedPoint_div; 12 | use std::fixedpoint::fixedPoint_equal; 13 | use std::fixedpoint::fixedPoint_compare; 14 | use std::fixedpoint::fixedPoint_zero; 15 | use std::fixedpoint::fixedPoint_isZero; 16 | use std::fixedpoint::fixedPoint_getNum; 17 | use std::fixedpoint::fixedPoint_getDenom; 18 | use std::fixedpoint::fixedPoint_mulByUint; 19 | use std::fixedpoint::fixedPoint_plusOne; 20 | use std::fixedpoint::fixedPoint_fractionalPart; 21 | 22 | throw write func main() { 23 | asm(tests()) { log }; 24 | } 25 | 26 | throw func tests() -> uint { 27 | let s9292 = fixedPoint_new(179292, 9292); 28 | let s1000 = fixedPoint_new(17820, 1000); 29 | 30 | if fixedPoint_integerPart(s9292) != 179292/9292 { 31 | return 1; 32 | } 33 | 34 | if let Some(_) = fixedPoint_add(s9292, s1000) { 35 | return 2; 36 | } 37 | 38 | if let Some(sum) = fixedPoint_add(s1000, s1000) { 39 | if !fixedPoint_equal(sum, fixedPoint_new(17820*2, 1000)) { 40 | return 3; 41 | } 42 | } else { 43 | return 4; 44 | } 45 | 46 | if let Some(_) = fixedPoint_mul(s9292, s1000) { 47 | return 5; 48 | } 49 | 50 | if let Some(prod) = fixedPoint_mul(s1000, s1000) { 51 | if !fixedPoint_equal(prod, fixedPoint_new(17820*17820/1000, 1000)) { 52 | return 6; 53 | } 54 | } else { 55 | return 7; 56 | } 57 | 58 | if let Some(prod) = fixedPoint_mul(s9292, fixedPoint_fromUintMatch(970000, s9292)) { 59 | if !fixedPoint_equal(prod, fixedPoint_new(179292*970000, 9292)) { 60 | return 8; 61 | } 62 | } else { 63 | return 9; 64 | } 65 | 66 | let zero = fixedPoint_zero(); 67 | let zero2 = fixedPoint_mulByUint(zero, 9292); 68 | 69 | if let Some(diff) = fixedPoint_sub(zero, zero2) { 70 | if fixedPoint_getNum(diff) != 0 { 71 | return 11; 72 | } 73 | if fixedPoint_getDenom(diff) != fixedPoint_getDenom(zero) { 74 | return 11; 75 | } 76 | if !fixedPoint_isZero(diff) { 77 | return 11; 78 | } 79 | } else { 80 | return 10; 81 | } 82 | 83 | let slope = fixedPoint_new(9, 5); 84 | let inverse = fixedPoint_new(5, 9); 85 | 86 | if fixedPoint_compare(inverse, slope) != -int(1) { 87 | return 12; 88 | } 89 | if fixedPoint_compare(slope, inverse) != int(1) { 90 | return 12; 91 | } 92 | if let Some(issue) = fixedPoint_sub(slope, inverse) { 93 | return 13; 94 | } 95 | if let Some(quotient) = fixedPoint_div(slope, inverse) { 96 | if !fixedPoint_equal(quotient, fixedPoint_new(81, 25)) { 97 | return 14; 98 | } 99 | } else { 100 | return 14; 101 | } 102 | 103 | let frac = fixedPoint_fractionalPart(slope); 104 | let expected_frac = fixedPoint_new(4, 5); 105 | let improper_frac = fixedPoint_plusOne(frac); 106 | 107 | if fixedPoint_compare(frac, expected_frac) != int(0) { 108 | return 15; 109 | } 110 | if fixedPoint_compare(slope, improper_frac) != int(0) { 111 | return 15; 112 | } 113 | 114 | 0 115 | } 116 | -------------------------------------------------------------------------------- /stdlib/keccaktest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::ByteArray; 6 | use std::bytearray::MarshalledBytes; 7 | 8 | use std::bytearray::bytearray_new; 9 | use std::bytearray::bytearray_unmarshalBytes; 10 | use std::bytearray::bytearray_size; 11 | use std::bytearray::bytearray_getByte; 12 | use std::bytearray::bytearray_setByte; 13 | use std::bytearray::bytearray_get256; 14 | use std::bytearray::bytearray_set256; 15 | use std::bytearray::bytearray_copy; 16 | use std::keccak::keccak256; 17 | use std::keccak::hasher_new; 18 | use std::keccak::hasher_pushByte; 19 | use std::keccak::hasher_pushN_be; 20 | use std::keccak::hasher_finish; 21 | 22 | 23 | throw write func main() { 24 | if let Some(x) = tests() { 25 | asm(x) { log }; 26 | } else { 27 | asm(666) { log }; 28 | } 29 | } 30 | 31 | throw func tests() -> option { 32 | let ba = bytearray_new(0); 33 | let kecEmpty = keccak256(ba, 0, 0); 34 | if kecEmpty != bytes32(0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) { 35 | return Some(bytes32(1)); 36 | } 37 | if ! hasherMatches(ba) { return Some(bytes32(101)); } 38 | if ! hasherMatchesByBlocks(ba) { return Some(bytes32(201)); } 39 | 40 | ba = bytearray_new(0); 41 | ba = bytearray_setByte(ba, 0, 0xcc); 42 | let kecCC = keccak256(ba, 0, 1); 43 | if kecCC != bytes32(0xeead6dbfc7340a56caedc044696a168870549a6a7f6f56961e84a54bd9970b8a) { 44 | return Some(bytes32(2)); 45 | } 46 | if ! hasherMatches(ba) { return Some(bytes32(102)); } 47 | if ! hasherMatchesByBlocks(ba) { return Some(bytes32(202)); } 48 | 49 | ba = bytearray_new(0); 50 | ba = bytearray_setByte(ba, 0, 0xcc); 51 | ba = bytearray_setByte(ba, 1, 0xaa); 52 | let kecCCAA = keccak256(ba, 0, 2); 53 | if kecCCAA != bytes32(0x33f234f4a499894af4036ace8c63b93a0bb7685472d79f3a9808cf95ed7047b4) { 54 | return Some(bytes32(3)); 55 | } 56 | if ! hasherMatches(ba) { return Some(bytes32(103)); } 57 | if ! hasherMatchesByBlocks(ba) { return Some(bytes32(203)); } 58 | 59 | let kec67 = keccak256(setupFromUnmarshal(), 0, 67); 60 | if kec67 != bytes32(0x3dd4c9bc4aa93bb5b8a21a06ecbef5336c378a7b9814a5a3a743406a54a3cc7b) { 61 | return Some(bytes32(4)); 62 | } 63 | if ! hasherMatches(setupFromUnmarshal()) { return Some(bytes32(104)); } 64 | if ! hasherMatchesByBlocks(ba) { return Some(bytes32(204)); } 65 | 66 | let kec67 = keccak256( 67 | bytearray_copy(setupFromUnmarshal(), 0, bytearray_new(0), 13, 67), 68 | 13, 69 | 67 70 | ); 71 | if kec67 != bytes32(0x3dd4c9bc4aa93bb5b8a21a06ecbef5336c378a7b9814a5a3a743406a54a3cc7b) { 72 | return Some(bytes32(5)); 73 | } 74 | 75 | let ba = bytearray_new(0); 76 | let i = 0; 77 | while i < 5 { 78 | ba = bytearray_set256(ba, i*32, 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f); 79 | i = i+1; 80 | } 81 | let kec532 = keccak256(ba, 0, 5*32); 82 | if kec532 != bytes32(0xaa9038072c0559cb6890b2632b172625239a59d6d93c1ef69df62aebfdd2f4e2) { 83 | return Some(bytes32(6)); 84 | } 85 | if ! hasherMatches(ba) { return Some(bytes32(106)); } 86 | if ! hasherMatchesByBlocks(ba) { return Some(bytes32(206)); } 87 | 88 | Some(bytes32(0)) 89 | } 90 | 91 | throw func setupFromUnmarshal() -> ByteArray { 92 | let marshalledStruct = unsafecast(( 93 | 67, 94 | ( 95 | 0x4041420000000000000000000000000000000000000000000000000000000000, 96 | ( 97 | 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f, 98 | ( 99 | 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, 100 | (), 101 | ), 102 | ), 103 | ), 104 | )); 105 | if let Some(ba) = bytearray_unmarshalBytes(marshalledStruct) { 106 | ba 107 | } else { 108 | error 109 | } 110 | } 111 | 112 | throw func hasherMatches(ba: ByteArray) -> bool { 113 | let hasher = hasher_new(); 114 | let i = 0; 115 | while i < bytearray_size(ba) { 116 | hasher = hasher_pushByte(hasher, bytearray_getByte(ba, i)); 117 | i = i + 1; 118 | } 119 | hasher_finish(hasher) == keccak256(ba, 0, bytearray_size(ba)) 120 | } 121 | 122 | throw func hasherMatchesByBlocks(ba: ByteArray) -> bool { 123 | let hasher = hasher_new(); 124 | let offset = 0; 125 | let size = bytearray_size(ba); 126 | if size > 27 { 127 | // put the hasher into a challenging alignment 128 | hasher = hasher_pushN_be(hasher, bytearray_get256(ba, offset), 27); 129 | offset = offset + 27; 130 | } 131 | while offset+32 < size { 132 | hasher = hasher_pushN_be(hasher, bytearray_get256(ba, offset), 32); 133 | offset = offset + 32; 134 | } 135 | hasher = hasher_pushN_be(hasher, bytearray_get256(ba, offset), size-offset); 136 | 137 | hasher_finish(hasher) == keccak256(ba, 0, bytearray_size(ba)) 138 | } 139 | -------------------------------------------------------------------------------- /stdlib/merkletree.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::ByteArray; 6 | use std::bytearray::bytearray_size; 7 | use std::bytearray::bytearray_toSizeAndBuffer; 8 | 9 | use std::keccak::keccak256; 10 | 11 | use std::queue::Queue; 12 | use std::queue::queue_new; 13 | use std::queue::queue_size; 14 | use std::queue::queue_isEmpty; 15 | use std::queue::queue_put; 16 | use std::queue::queue_getOrDie; 17 | 18 | 19 | public throw func merkleize(data: []ByteArray) -> (any, bytes32) { 20 | let length = len(data); 21 | let layer = newarray<(any, bytes32)>(length); 22 | let i = 0; 23 | while(i < length) { 24 | let (size, buf) = bytearray_toSizeAndBuffer(data[i]); 25 | layer = layer with { [i] = ((size, buf, ()), hash(keccak256(data[i], 0, bytearray_size(data[i])))) }; 26 | i = i+1; 27 | } 28 | 29 | while(length > 1) { 30 | let newLayer = newarray<(any, bytes32)>((length+1)/2); 31 | let i = 0; 32 | while(i < (length+1)/2) { 33 | if (2*i+1 == length) { 34 | newLayer = newLayer with { [i] = layer[2*i] }; 35 | } else { 36 | newLayer = newLayer with { [i] = ((layer[2*i].0, layer[2*i+1].0), hash(layer[2*i].1, layer[2*i+1].1)) }; 37 | } 38 | i = i+1; 39 | } 40 | layer = newLayer; 41 | length = len(layer); 42 | } 43 | 44 | return layer[0]; 45 | } 46 | 47 | public throw func merkleizeFromQueue(queue: Queue) -> (any, bytes32) { 48 | let num = queue_size(queue); 49 | let arr = newarray(num); 50 | let i = 0; 51 | while (i < num) { 52 | let (uq, item) = queue_getOrDie(queue); 53 | queue = uq; 54 | arr = arr with { [i] = unsafecast(item) }; 55 | i = i + 1; 56 | } 57 | return merkleize(arr); 58 | } 59 | 60 | type MerkleTreeBuilder = struct { 61 | contents: Queue, 62 | }; 63 | 64 | public throw func merkleTreeBuilder_new() -> MerkleTreeBuilder { 65 | return struct { contents: queue_new() }; 66 | } 67 | 68 | public func merkleTreeBuilder_isEmpty(mtb: MerkleTreeBuilder) -> bool { 69 | return queue_isEmpty(mtb.contents); 70 | } 71 | 72 | public throw func merkleTreeBuilder_add(mtb: MerkleTreeBuilder, item: ByteArray) -> MerkleTreeBuilder { 73 | return mtb with { contents: queue_put(mtb.contents, item) }; 74 | } 75 | 76 | public throw func merkleTreeBuilder_finish(mtb: MerkleTreeBuilder) -> (any, bytes32) { 77 | return merkleizeFromQueue(mtb.contents); 78 | } 79 | -------------------------------------------------------------------------------- /stdlib/outputbuffer.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | type OutputBuffer = struct { 6 | buf: buffer, 7 | size: uint, 8 | }; 9 | 10 | public throw func outputBuffer_new() -> OutputBuffer { 11 | return struct { 12 | buf: newbuffer(), 13 | size: 0, 14 | }; 15 | } 16 | 17 | public throw func outputBuffer_size(ob: OutputBuffer) -> uint { 18 | return ob.size; 19 | } 20 | 21 | public throw func outputBuffer_toBufferAndSize(ob: OutputBuffer) -> (buffer, uint) { 22 | return (ob.buf, ob.size); 23 | } 24 | 25 | public throw func outputBuffer_appendByte(ob: OutputBuffer, val: uint) -> OutputBuffer { 26 | return struct { 27 | buf: setbuffer8(ob.buf, ob.size, val), 28 | size: ob.size + 1, 29 | }; 30 | } 31 | 32 | public throw func outputBuffer_append256(ob: OutputBuffer, val: uint) -> OutputBuffer { 33 | return struct { 34 | buf: setbuffer256(ob.buf, ob.size, val), 35 | size: ob.size + 32, 36 | }; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /stdlib/priorityq.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use core::array::array; 6 | use core::array::array_resize; 7 | 8 | 9 | type PqItem = struct { 10 | priority: uint, 11 | item: any, 12 | }; 13 | 14 | type PriorityQ = struct { 15 | size: uint, 16 | capacity: uint, 17 | contents: []PqItem, 18 | }; 19 | 20 | public throw func priorityq_new() -> PriorityQ { 21 | struct { 22 | size: 0, 23 | capacity: 8, 24 | contents: newarray(8), 25 | } 26 | } 27 | 28 | public func priorityq_isEmpty(pq: PriorityQ) -> bool { 29 | pq.size == 0 30 | } 31 | 32 | public func priorityq_size(pq: PriorityQ) -> uint { 33 | pq.size 34 | } 35 | 36 | public throw func priorityq_get(pq: PriorityQ) -> option<(any, PriorityQ)> { 37 | if priorityq_isEmpty(pq) { 38 | None<(any, PriorityQ)> 39 | } else { 40 | let ret = pq.contents[0].item; 41 | let newpq = pq with { size: pq.size-1 } 42 | with { contents: pq.contents with { [0] = pq.contents[pq.size-1] } }; 43 | Some((ret, pq_pushDown(newpq, 0))) 44 | } 45 | } 46 | 47 | throw func pq_pushDown(pq: PriorityQ, index: uint) -> PriorityQ { 48 | loop { 49 | let firstKidIdx = 2*index+1; 50 | if firstKidIdx >= pq.size { 51 | return pq; 52 | } else if firstKidIdx+1 == pq.size { 53 | // only one kid is in play 54 | let this = pq.contents[index]; 55 | let kid = pq.contents[firstKidIdx]; 56 | if kid.priority > this.priority { 57 | return pq with { contents: pq.contents with { [index] = kid } 58 | with { [firstKidIdx] = this } }; 59 | } else { 60 | return pq; 61 | } 62 | } else { 63 | let this = pq.contents[index]; 64 | let firstKid = pq.contents[firstKidIdx]; 65 | let secondKidIdx = firstKidIdx+1; 66 | let secondKid = pq.contents[secondKidIdx]; 67 | if firstKid.priority > secondKid.priority { 68 | if firstKid.priority > this.priority { 69 | pq = pq with { contents: pq.contents with { [index] = firstKid } 70 | with { [firstKidIdx] = this } }; 71 | index = firstKidIdx; 72 | } else { 73 | return pq; 74 | } 75 | } else { 76 | if secondKid.priority > this.priority { 77 | pq = pq with { contents: pq.contents with { [index] = secondKid } 78 | with { [secondKidIdx] = this } }; 79 | index = secondKidIdx; 80 | } else { 81 | return pq; 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | throw func pq_pushUp(pq: PriorityQ, index: uint) -> PriorityQ { 89 | let this = pq.contents[index]; 90 | loop { 91 | if index == 0 { 92 | return pq; 93 | } 94 | let parentIdx = (index-1)/2; 95 | let parent = pq.contents[parentIdx]; 96 | if parent.priority >= this.priority { 97 | return pq; 98 | } else { 99 | pq = pq with { contents: pq.contents with { [index] = parent } 100 | with { [parentIdx] = this } }; 101 | index = parentIdx; 102 | } 103 | } 104 | } 105 | 106 | public throw func priorityq_insert(pq: PriorityQ, item: any, priority: uint) -> PriorityQ { 107 | if pq.size == pq.capacity { 108 | let newCapacity = 8*pq.capacity; 109 | pq = pq with { capacity: newCapacity } 110 | with { contents: unsafecast<[]PqItem>(array_resize(unsafecast(pq.contents), newCapacity, ())) }; 111 | } 112 | let index = pq.size; 113 | let newpq = pq with { size: index+1 } 114 | with { contents: pq.contents with { [index] = struct { priority: priority, item: item } } }; 115 | pq_pushUp(newpq, index) 116 | } 117 | 118 | public write throw func priorityq_printAsArray(pq: PriorityQ) -> uint { 119 | let ret = pq.size; 120 | let cont = pq.contents; 121 | let i = 0; 122 | while i < ret { 123 | debug(cont[i]); 124 | i = i+1; 125 | } 126 | ret 127 | } 128 | -------------------------------------------------------------------------------- /stdlib/priorityqtest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::priorityq::priorityq_new; 6 | use std::priorityq::priorityq_isEmpty; 7 | use std::priorityq::priorityq_insert; 8 | use std::priorityq::priorityq_get; 9 | 10 | 11 | throw write func main() { 12 | asm(tests()) { log }; 13 | } 14 | 15 | throw func tests() -> uint { 16 | let q = priorityq_new(); 17 | if !priorityq_isEmpty(q) { 18 | return 1; 19 | } 20 | 21 | q = priorityq_new(); 22 | q = priorityq_insert(q, 97, 3); 23 | if priorityq_isEmpty(q) { 24 | return 2; 25 | } 26 | 27 | q = priorityq_new(); 28 | let pq = priorityq_insert(q, 97, 3); 29 | if pq == q { 30 | return 3; 31 | } 32 | 33 | q = priorityq_new(); 34 | q = priorityq_insert(q, 97, 3); 35 | q = priorityq_insert(q, 98, 2); 36 | q = priorityq_insert(q, 96, 4); 37 | if let Some(res) = priorityq_get(q) { 38 | let (ret, _pq) = res; 39 | if ret != unsafecast(96) { 40 | return 4; 41 | } 42 | } else { 43 | return 104; 44 | } 45 | 46 | q = priorityq_new(); 47 | let i = 0; 48 | while i < 58 { 49 | q = priorityq_insert(q, i+30, i+100); 50 | i = i+1; 51 | } 52 | q = priorityq_insert(q, 97, 293); 53 | q = priorityq_insert(q, 98, 292); 54 | q = priorityq_insert(q, 96, 294); 55 | if let Some(res) = priorityq_get(q) { 56 | let (ret, _pq,) = res; 57 | if ret != unsafecast(96) { 58 | return 5; 59 | } 60 | } else { 61 | return 105; 62 | } 63 | 64 | 0 65 | } 66 | 67 | // stuff below here is for testing the compiler 68 | sensitive func nullFunc() { 69 | return; 70 | } 71 | 72 | sensitive func callNullFunc() { 73 | callNullFunc(); 74 | } 75 | -------------------------------------------------------------------------------- /stdlib/queuetest.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::queue::queue_new; 6 | use std::queue::queue_isEmpty; 7 | use std::queue::queue_put; 8 | use std::queue::queue_get; 9 | 10 | throw write func main() { 11 | if let Some(res) = tests() { 12 | asm(res) { log }; 13 | } else { 14 | asm(1000) { log }; 15 | } 16 | } 17 | 18 | throw func tests() -> option { 19 | 20 | let q = queue_new(); 21 | 22 | if !queue_isEmpty(q) { 23 | return Some(1); 24 | } 25 | 26 | q = queue_new(); 27 | q = queue_put(q, 42); 28 | if queue_isEmpty(q) { 29 | return Some(2); 30 | } 31 | 32 | q = queue_new(); 33 | q = queue_put(q, 42); 34 | q = queue_put(q, 97); 35 | let pair = queue_get(q)?; 36 | if pair.1 != any(42) { 37 | return Some(3); 38 | } 39 | 40 | q = queue_new(); 41 | let i = 0; 42 | while i < 25 { 43 | q = queue_put(q, i); 44 | i = i+1; 45 | } 46 | i = 0; 47 | while i < 21 { 48 | q = (queue_get(q)?).0; 49 | i = i+1; 50 | } 51 | if (queue_get(q)?).1 != any(21) { 52 | return Some(4); 53 | } 54 | 55 | Some(0) 56 | } 57 | -------------------------------------------------------------------------------- /stdlib/random.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | type RandomGenerator = bytes32; 6 | 7 | public func random_new(seed: bytes32) -> RandomGenerator { 8 | seed 9 | } 10 | 11 | public func random_refresh_seed(rand: RandomGenerator, refresh: bytes32) -> RandomGenerator { 12 | hash(rand, refresh) 13 | } 14 | 15 | public func random_next(rand: RandomGenerator) -> (bytes32, RandomGenerator) { 16 | ( 17 | hash(bytes32(0), rand), 18 | hash(bytes32(1), rand), 19 | ) 20 | } 21 | 22 | public func random_nextMod(rand: RandomGenerator, modulus: uint) -> (uint, RandomGenerator) { 23 | if modulus == 0 { 24 | return (0, rand); 25 | } 26 | let limit = modulus * ((~0)/modulus); 27 | let (b, r) = random_next(rand); 28 | rand = r; 29 | while uint(b) >= limit { 30 | let (ub, r) = random_next(rand); 31 | b = ub; 32 | rand = r; 33 | } 34 | (uint(b) % modulus, rand) 35 | } 36 | 37 | public throw func randomly_permute_array( 38 | rand: RandomGenerator, 39 | arr: []any, 40 | offset: uint, 41 | num: uint 42 | ) -> ([]any, RandomGenerator) { 43 | while num > 1 { 44 | let (rb, gen) = random_next(rand); 45 | rand = gen; 46 | let i = uint(rb) % num; // This isn't perfectly uniform, but difference is negligible for this use. 47 | arr = arr with { [offset+i] = arr[offset+num-1] } 48 | with { [offset+num-1] = arr[offset+i] }; 49 | num = num-1; 50 | } 51 | (arr, rand) 52 | } 53 | -------------------------------------------------------------------------------- /stdlib/rateEstimator.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2022, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | type RateEstimator = struct { 6 | timeConstant: uint, 7 | estimate: uint, 8 | usageSince: uint, 9 | lastTimeSeen: uint, 10 | }; 11 | 12 | public func rateEstimator_new(timeConstant: uint, initialEstimate: uint, currentTime: uint) -> RateEstimator { 13 | struct { 14 | timeConstant: timeConstant, 15 | estimate: initialEstimate, 16 | usageSince: 0, 17 | lastTimeSeen: currentTime, 18 | } 19 | } 20 | 21 | public func rateEstimator_get(ra: RateEstimator) -> uint { 22 | ra.estimate 23 | } 24 | 25 | public func rateEstimator_addUsage(ra: RateEstimator, newUsage: uint) -> RateEstimator { 26 | set ra.usageSince = ra.usageSince + newUsage; 27 | ra 28 | } 29 | 30 | public func rateEstimator_advanceTime(ra: RateEstimator, currentTime: uint) -> RateEstimator { 31 | if currentTime > ra.lastTimeSeen { 32 | set ra.estimate = (ra.estimate * ra.timeConstant + ra.usageSince) / (ra.timeConstant + currentTime - ra.lastTimeSeen); 33 | set ra.usageSince = 0; 34 | set ra.lastTimeSeen = currentTime; 35 | } 36 | ra 37 | } -------------------------------------------------------------------------------- /stdlib/ripemd160.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::ByteArray; 6 | use std::bytearray::bytearray_size; 7 | use std::bytearray::bytearray_get256; 8 | use std::bytearray::bytearray_getByte; 9 | 10 | use std::keccak::flipEndian64; 11 | 12 | 13 | type Ripemd160Hasher = struct { 14 | accumulator: bytes32, 15 | buf: [2]uint, 16 | offset: uint, 17 | totalSizeBytes: uint, 18 | }; 19 | 20 | public func ripemd160hasher_new() -> Ripemd160Hasher { 21 | struct { 22 | accumulator: bytes32(0x67452301EFCDAB8998BADCFE10325476C3D2E1F0), 23 | buf: unsafecast<[2]uint>((0,0)), 24 | offset: 0, 25 | totalSizeBytes: 0, 26 | } 27 | } 28 | 29 | public func ripemd160hasher_pushByte(h: Ripemd160Hasher, b: uint) -> Ripemd160Hasher { 30 | let word = h.offset / 32; 31 | let newBuffer = h.buf with { 32 | [word] = h.buf[word] ^ (b&0xff << (248-8*(h.offset % 32))) 33 | }; 34 | if h.offset >= 63 { 35 | h with { 36 | accumulator: asm(h.accumulator, newBuffer[0], newBuffer[1]) bytes32 { ripemd160f } 37 | } with { 38 | buf: unsafecast<[2]uint>((0,0)) 39 | } with { 40 | offset: 0 41 | } with { 42 | totalSizeBytes: 1 + h.totalSizeBytes 43 | } 44 | } else { 45 | h with { 46 | buf: newBuffer 47 | } with { 48 | offset: h.offset+1 49 | } with { 50 | totalSizeBytes: 1 + h.totalSizeBytes 51 | } 52 | } 53 | } 54 | 55 | public func ripemd160hasher_push256(h: Ripemd160Hasher, val: uint) -> Ripemd160Hasher { 56 | let offset = h.offset; 57 | if offset == 0 { 58 | h with { 59 | buf: h.buf with { [0] = val } 60 | } with { 61 | offset: 32 62 | } with { 63 | totalSizeBytes: 32+h.totalSizeBytes 64 | } 65 | } else if offset == 32 { 66 | h with { 67 | accumulator: asm(h.accumulator, h.buf[0], val) bytes32 { ripemd160f } 68 | } with { 69 | buf: unsafecast<[2]uint>((0,0)) 70 | } with { 71 | offset: 0 72 | } with { 73 | totalSizeBytes: 32+h.totalSizeBytes 74 | } 75 | } else if offset < 32 { 76 | h with { 77 | buf: h.buf with { 78 | [0] = h.buf[0] | (val >> (8*offset)) 79 | } with { 80 | [1] = h.buf[1] | (val << (8*(32-offset))) 81 | } 82 | } with { 83 | offset: 32+offset 84 | } with { 85 | totalSizeBytes: 32+h.totalSizeBytes 86 | } 87 | } else { 88 | offset = offset-32; 89 | h with { 90 | accumulator: asm( 91 | h.accumulator, 92 | h.buf[0], 93 | h.buf[1] | (val >> (8*offset)) 94 | ) bytes32 { ripemd160f } 95 | } with { 96 | buf: unsafecast<[2]uint>(( 97 | val << (8*(32-offset)), 98 | 0 99 | )) 100 | } with { 101 | offset: offset 102 | } with { 103 | totalSizeBytes: 32+h.totalSizeBytes 104 | } 105 | } 106 | } 107 | 108 | public func ripemd160hasher_finish(h: Ripemd160Hasher) -> bytes32 { 109 | // write the first padding byte 110 | h = ripemd160hasher_pushByte(h, 0x80) with { 111 | totalSizeBytes: h.totalSizeBytes // undo the +1 caused by ripemd160hasher_pushByte call 112 | }; 113 | 114 | // make sure there is space for the 64-bit total length 115 | if h.offset > 56 { 116 | h = h with { 117 | accumulator: asm(h.accumulator, h.buf[0], h.buf[1]) bytes32 { ripemd160f } 118 | } with { 119 | buf: unsafecast<[2]uint>((0,0)) 120 | }; 121 | } 122 | 123 | // insert the total size, and invoke the compression function 124 | asm( 125 | h.accumulator, 126 | h.buf[0], 127 | h.buf[1] | flipEndian64(8*h.totalSizeBytes), //TODO: check endianness 128 | ) bytes32 { ripemd160f } 129 | } 130 | 131 | public func ripemd160_byteArray(ba: ByteArray) -> bytes32 { 132 | let hasher = ripemd160hasher_new(); 133 | let i = 0; 134 | let sz = bytearray_size(ba); 135 | while i+32 < sz { 136 | hasher = ripemd160hasher_push256(hasher, bytearray_get256(ba, i)); 137 | i = i+32; 138 | } 139 | while i < sz { 140 | hasher = ripemd160hasher_pushByte(hasher, bytearray_getByte(ba, i)); 141 | i = i+1; 142 | } 143 | ripemd160hasher_finish(hasher) 144 | } 145 | 146 | 147 | -------------------------------------------------------------------------------- /stdlib/ripemd160test.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::bytearray_new; 6 | 7 | use std::ripemd160::ripemd160hasher_new; 8 | use std::ripemd160::ripemd160hasher_finish; 9 | use std::ripemd160::ripemd160_byteArray; 10 | use std::ripemd160::ripemd160hasher_pushByte; 11 | use std::ripemd160::ripemd160hasher_push256; 12 | 13 | write func main() { 14 | asm(tests()) { log }; 15 | } 16 | 17 | func tests() -> uint { 18 | let x = asm( 19 | 0x67452301EFCDAB8998BADCFE10325476C3D2E1F0, 20 | 0, 21 | 0 22 | ) bytes32 { ripemd160f }; 23 | if (x != bytes32(0xc59b7e1835958b24ccddd6d0304e7c981f1045cb)) { 24 | return 1; 25 | } 26 | 27 | let x = asm( 28 | 0x67452301EFCDAB8998BADCFE10325476C3D2E1F0, 29 | 0x8000000000000000000000000000000000000000000000000000000000000000, 30 | 0 31 | ) bytes32 { ripemd160f }; 32 | if (x != bytes32(0x9c1185a5c5e9fc54612808977ee8f548b2258d31)) { 33 | return 2; 34 | } 35 | 36 | if (ripemd160_byteArray(bytearray_new(0)) != bytes32(0x9c1185a5c5e9fc54612808977ee8f548b2258d31)) { 37 | return 3; 38 | } 39 | 40 | if (ripemd160hasher_finish(ripemd160hasher_new()) != bytes32(0x9c1185a5c5e9fc54612808977ee8f548b2258d31)) { 41 | return 4; 42 | } 43 | 44 | let h = ripemd160hasher_new(); 45 | h = ripemd160hasher_pushByte(h, 0x61); 46 | if (ripemd160hasher_finish(h) != bytes32(0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffe)) { 47 | return 5; 48 | } 49 | 50 | let h = ripemd160hasher_new(); 51 | let i = 0x41; 52 | while(i < 0x5b) { 53 | h = ripemd160hasher_pushByte(h, i); 54 | i = i+1; 55 | } 56 | i = 0x61; 57 | while(i < 0x7b) { 58 | h = ripemd160hasher_pushByte(h, i); 59 | i = i+1; 60 | } 61 | if (ripemd160hasher_finish(h) != bytes32(0x74e856c137772846e18c0c6d955666133f1d32dc)) { 62 | return 6; 63 | } 64 | 65 | let h = ripemd160hasher_new(); 66 | let i = 0x00; 67 | while (i < 55) { 68 | h = ripemd160hasher_pushByte(h, i); 69 | i = i + 1; 70 | } 71 | if (ripemd160hasher_finish(h) != bytes32(0x3c86963b3ff646a65ae42996e9664c747cc7e5e6)) { 72 | return 7; 73 | } 74 | 75 | let h = ripemd160hasher_new(); 76 | h = ripemd160hasher_push256(h, uint(0x0cdd6941697c828be460f241d6e71106a408c854e351c54f46b6500b106e2cdb)); 77 | if (ripemd160hasher_finish(h) != bytes32(0xfe27e5ab7fdee1be58e9ee498709c337d4b54ba4)) { 78 | return 8; 79 | } 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /stdlib/safeMath.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | const BasisPointsForOne = 10000; 6 | 7 | public throw func safeAdd(x: uint, y: uint) -> uint { 8 | let ret = x + y; 9 | if ret < x { 10 | error; 11 | } 12 | ret 13 | } 14 | 15 | public func trySafeAdd(x: uint, y: uint) -> option { 16 | let ret = x + y; 17 | if ret < x { 18 | None 19 | } else { 20 | Some(ret) 21 | } 22 | } 23 | 24 | public throw func safeSub(x: uint, y: uint) -> uint { 25 | if x < y { 26 | error; 27 | } 28 | x - y 29 | } 30 | 31 | public func trySafeSub(x: uint, y: uint) -> option { 32 | if x < y { 33 | None 34 | } else { 35 | Some(x - y) 36 | } 37 | } 38 | 39 | public throw func safeMul(x: uint, y: uint) -> uint { 40 | let ret = x * y; 41 | if (x != 0) && (y != (ret/x)) { 42 | error; 43 | } 44 | ret 45 | } 46 | 47 | public func trySafeMul(x: uint, y: uint) -> option { 48 | let ret = x * y; 49 | if (x != 0) && (y != (ret/x)) { 50 | None 51 | } else { 52 | Some(ret) 53 | } 54 | } 55 | 56 | public throw func safeToInt(x: uint) -> int { 57 | let ret = int(x); 58 | if ret < int(0) { 59 | error; 60 | } 61 | int(ret) 62 | } 63 | 64 | public throw func safeAddInt(x: int, y: int) -> int { 65 | let ret = x + y; 66 | if x > int(0) { 67 | if ret < y { 68 | error; 69 | } 70 | } else if x < int(0) { 71 | if ret > y { 72 | error; 73 | } 74 | } 75 | ret 76 | } 77 | 78 | public throw func safeSubInt(x: int, y: int) -> int { 79 | let ret = x - y; 80 | if y > int(0) { 81 | if ret > x { 82 | error; 83 | } 84 | } else if y < int(0) { 85 | if ret < x { 86 | error; 87 | } 88 | } 89 | ret 90 | } 91 | 92 | public func trySafeSubInt(x: int, y: int) -> option { 93 | let ret = x - y; 94 | if y > int(0) { 95 | if ret > x { 96 | None 97 | } else { 98 | Some(ret) 99 | } 100 | } else if y < int(0) { 101 | if ret < x { 102 | None 103 | } else { 104 | Some(ret) 105 | } 106 | } else { 107 | Some(ret) 108 | } 109 | } 110 | 111 | public throw func safeMulInt(x: int, y: int) -> int { 112 | let ret = x * y; 113 | if (x != int(0)) && (y != (ret/x)) { 114 | error; 115 | } 116 | ret 117 | } 118 | 119 | public func trySafeMulInt(x: int, y: int) -> option { 120 | let ret = x * y; 121 | if (x != int(0)) && (y != (ret/x)) { 122 | None 123 | } else { 124 | Some(ret) 125 | } 126 | } 127 | 128 | public throw func safeToUint(x: int) -> uint { 129 | if x < int(0) { 130 | error; 131 | } 132 | uint(x) 133 | } 134 | 135 | // This takes an argument, denominated in basis points, and returns 136 | // exp(in) denominated in basis points. This uses a Taylor series approximation, up to the x**4 term, 137 | // centered at x==0. The result is clamped to a constant if the input is greater than 2**64. 138 | // The approximation error will about 5% when in == 2*BasisPointsForOne. The result will be closer to 139 | // BasisPointsForOne than the true exp function would be. 140 | public func approxExpBasisPoints(in: int) -> uint { 141 | let (x, wasNegative) = if in < int(0) { 142 | (uint(-in), true) 143 | } else { 144 | (uint(in), false) 145 | }; 146 | if x > (1<<64) { 147 | x = 1<<64; 148 | } 149 | let res = const::BasisPointsForOne + x / 4; 150 | res = const::BasisPointsForOne + res * x / (3 * const::BasisPointsForOne); 151 | res = const::BasisPointsForOne + res * x / (2 * const::BasisPointsForOne); 152 | res = const::BasisPointsForOne + res * x / const::BasisPointsForOne; 153 | if wasNegative { (const::BasisPointsForOne * const::BasisPointsForOne) / res } else { res } 154 | } 155 | -------------------------------------------------------------------------------- /stdlib/sha256.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::ByteArray; 6 | use std::bytearray::bytearray_size; 7 | use std::bytearray::bytearray_get256; 8 | use std::bytearray::bytearray_getByte; 9 | 10 | 11 | type Sha256Hasher = struct { 12 | accumulator: bytes32, 13 | buf: [2]uint, 14 | offset: uint, 15 | totalSizeBits: uint, 16 | }; 17 | 18 | public func sha256hasher_new() -> Sha256Hasher { 19 | struct { 20 | accumulator: bytes32(0x6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19), 21 | buf: unsafecast<[2]uint>((0,0)), 22 | offset: 0, 23 | totalSizeBits: 0, 24 | } 25 | } 26 | 27 | public func sha256hasher_pushByte(h: Sha256Hasher, b: uint) -> Sha256Hasher { 28 | let word = h.offset / 32; 29 | let newBuffer = h.buf with { 30 | [word] = h.buf[word] ^ (b&0xff << (248-8*(h.offset % 32))) 31 | }; 32 | if h.offset >= 63 { 33 | h with { 34 | accumulator: asm(h.accumulator, newBuffer[0], newBuffer[1]) bytes32 { sha256f } 35 | } with { 36 | buf: unsafecast<[2]uint>((0,0)) 37 | } with { 38 | offset: 0 39 | } with { 40 | totalSizeBits: 8 + h.totalSizeBits 41 | } 42 | } else { 43 | h with { 44 | buf: newBuffer 45 | } with { 46 | offset: h.offset+1 47 | } with { 48 | totalSizeBits: 8 + h.totalSizeBits 49 | } 50 | } 51 | } 52 | 53 | public func sha256hasher_push256(h: Sha256Hasher, val: uint) -> Sha256Hasher { 54 | let offset = h.offset; 55 | if offset == 0 { 56 | h with { 57 | buf: h.buf with { [0] = val } 58 | } with { 59 | offset: 32 60 | } with { 61 | totalSizeBits: 256+h.totalSizeBits 62 | } 63 | } else if offset == 32 { 64 | h with { 65 | accumulator: asm(h.accumulator, h.buf[0], val) bytes32 { sha256f } 66 | } with { 67 | buf: unsafecast<[2]uint>((0,0)) 68 | } with { 69 | offset: 0 70 | } with { 71 | totalSizeBits: 256+h.totalSizeBits 72 | } 73 | } else if offset < 32 { 74 | h with { 75 | buf: h.buf with { 76 | [0] = h.buf[0] | (val >> (8*offset)) 77 | } with { 78 | [1] = h.buf[1] | (val << (8*(32-offset))) 79 | } 80 | } with { 81 | offset: 32+offset 82 | } with { 83 | totalSizeBits: 256+h.totalSizeBits 84 | } 85 | } else { 86 | offset = offset-32; 87 | h with { 88 | accumulator: asm( 89 | h.accumulator, 90 | h.buf[0], 91 | h.buf[1] | (val >> (8*offset)) 92 | ) bytes32 { sha256f } 93 | } with { 94 | buf: unsafecast<[2]uint>(( 95 | val << 8*(32-offset), 96 | 0 97 | )) 98 | } with { 99 | offset: offset 100 | } with { 101 | totalSizeBits: 256+h.totalSizeBits 102 | } 103 | } 104 | } 105 | 106 | public func sha256hasher_finish(h: Sha256Hasher) -> bytes32 { 107 | // write the first padding byte 108 | h = sha256hasher_pushByte(h, 0x80) with { 109 | totalSizeBits: h.totalSizeBits // undo the +8 caused by sha256hasher_pushByte call 110 | }; 111 | 112 | // make sure there is space for the 64-bit total length 113 | if h.offset > 56 { 114 | h = h with { 115 | accumulator: asm(h.accumulator, h.buf[0], h.buf[1]) bytes32 { sha256f } 116 | } with { 117 | buf: unsafecast<[2]uint>((0,0)) 118 | }; 119 | } 120 | 121 | // insert the total size, and invoke the compression function 122 | asm( 123 | h.accumulator, 124 | h.buf[0], 125 | h.buf[1] | (h.totalSizeBits & 0xffffffffffffffff), 126 | ) bytes32 { sha256f } 127 | } 128 | 129 | public func sha256_byteArray(ba: ByteArray) -> bytes32 { 130 | let hasher = sha256hasher_new(); 131 | let i = 0; 132 | let sz = bytearray_size(ba); 133 | while i+32 < sz { 134 | hasher = sha256hasher_push256(hasher, bytearray_get256(ba, i)); 135 | i = i+32; 136 | } 137 | while i < sz { 138 | hasher = sha256hasher_pushByte(hasher, bytearray_getByte(ba, i)); 139 | i = i+1; 140 | } 141 | sha256hasher_finish(hasher) 142 | } 143 | 144 | -------------------------------------------------------------------------------- /stdlib/sha256test.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use std::bytearray::bytearray_new; 6 | use std::bytearray::bytearray_getByte; 7 | use std::bytearray::bytearray_setByte; 8 | use std::bytearray::bytearray_get256; 9 | 10 | use std::sha256::sha256hasher_new; 11 | use std::sha256::sha256hasher_finish; 12 | use std::sha256::sha256_byteArray; 13 | use std::sha256::sha256hasher_pushByte; 14 | use std::sha256::sha256hasher_push256; 15 | 16 | throw write func main() { 17 | if let Some(x) = tests() { 18 | asm(x) { log }; 19 | } else { 20 | asm(666) { log }; 21 | } 22 | } 23 | 24 | throw func tests() -> option { 25 | let x = asm(0x6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19, 0, 0) bytes32 { sha256f }; 26 | if (x != bytes32(0xDA5698BE17B9B46962335799779FBECA8CE5D491C0D26243BAFEF9EA1837A9D8)) { 27 | return Some(bytes32(1)); 28 | } 29 | 30 | let x = asm( 31 | 0x6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19, 32 | 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 33 | 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 34 | ) bytes32 { sha256f }; 35 | if (x != bytes32(0xEF0C748DF4DA50A8D6C43C013EDC3CE76C9D9FA9A1458ADE56EB86C0A64492D2)) { 36 | return Some(bytes32(2)); 37 | } 38 | 39 | let x = asm( 40 | 0x6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19, 41 | 0x243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89, 42 | 0x452821E638D01377BE5466CF34E90C6CC0AC29B7C97C50DD3f84D5B5b5470917 43 | ) bytes32 { sha256f }; 44 | if (x != bytes32(0xCF0AE4EB67D38FFEB94068984B22ABDE4E92BC548D14585E48DCA8882D7B09CE)) { 45 | return Some(bytes32(3)); 46 | } 47 | 48 | let h = sha256hasher_new(); 49 | let sEmpty = sha256hasher_finish(h); 50 | if (sEmpty != bytes32(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)) { 51 | return Some(bytes32(10)); 52 | } 53 | let ba = bytearray_new(0); 54 | let sEmpty = sha256_byteArray(ba); 55 | if (sEmpty != bytes32(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)) { 56 | return Some(bytes32(11)); 57 | } 58 | 59 | let ba = bytearray_new(0); 60 | ba = bytearray_setByte(ba, 0, 0xcc); 61 | let sCc = sha256_byteArray(ba); 62 | if (sCc != bytes32(0x1dd8312636f6a0bf3d21fa2855e63072507453e93a5ced4301b364e91c9d87d6)) { 63 | return Some(bytes32(12)); 64 | } 65 | 66 | let ba = bytearray_new(0); 67 | let i = 0; 68 | while(i < 61) { 69 | ba = bytearray_setByte(ba, i, i); 70 | i = i+1; 71 | } 72 | let s61 = sha256_byteArray(ba); 73 | if (s61 != bytes32(0x4b5c2783c91ceccb7c839213bcbb6a902d7fe8c2ec866877a51f433ea17f3e85)) { 74 | return Some(bytes32(13)); 75 | } 76 | 77 | let ba = bytearray_new(0); 78 | let i = 0; 79 | let h = sha256hasher_new(); 80 | while (i < 131) { 81 | ba = bytearray_setByte(ba, i, i); 82 | h = sha256hasher_pushByte(h, i); 83 | i = i+1; 84 | } 85 | let h0 = sha256hasher_finish(h); 86 | h = sha256hasher_new(); 87 | let i = 0; 88 | while (i < 4) { 89 | h = sha256hasher_push256(h, bytearray_get256(ba, 32*i)); 90 | i = i+1; 91 | } 92 | h = sha256hasher_pushByte(h, bytearray_getByte(ba, 128)); 93 | h = sha256hasher_pushByte(h, bytearray_getByte(ba, 129)); 94 | h = sha256hasher_pushByte(h, bytearray_getByte(ba, 130)); 95 | let h1 = sha256hasher_finish(h); 96 | if (h0 != h1) { 97 | return Some(bytes32(14)); 98 | } 99 | h = sha256hasher_new(); 100 | h = sha256hasher_pushByte(h, bytearray_getByte(ba, 0)); 101 | h = sha256hasher_pushByte(h, bytearray_getByte(ba, 1)); 102 | h = sha256hasher_pushByte(h, bytearray_getByte(ba, 2)); 103 | i = 0; 104 | while (i < 4) { 105 | h = sha256hasher_push256(h, bytearray_get256(ba, 32*i+3)); 106 | i = i+1; 107 | } 108 | let h2 = sha256hasher_finish(h); 109 | if (h0 != h2) { 110 | return Some(bytes32(15)); 111 | } 112 | 113 | return Some(bytes32(0)); 114 | } 115 | -------------------------------------------------------------------------------- /stdlib/stack.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | type Stack = option; 6 | 7 | type StackCell = struct { 8 | top: [8]any, 9 | num: uint, 10 | rest: option 11 | }; 12 | 13 | 14 | public func stack_new() -> Stack { 15 | None 16 | } 17 | 18 | public func stack_push(stack: Stack, value: any) -> Stack { 19 | if let Some(cell) = unsafecast >(stack) { 20 | if cell.num < 8 { 21 | return Some( 22 | cell with { 23 | top: cell.top with { [cell.num] = value } 24 | } with { 25 | num: 1 + cell.num 26 | } 27 | ); 28 | } 29 | } 30 | 31 | Some(struct { 32 | top: newfixedarray(8) with { [0] = value }, 33 | num: 1, 34 | rest: stack 35 | }) 36 | } 37 | 38 | public func stack_isEmpty(stack: Stack) -> bool { 39 | stack == None 40 | } 41 | 42 | public func stack_pop(stack: Stack) -> option<(Stack, any)> { 43 | let cell = stack?; 44 | if cell.num == 1 { 45 | Some((unsafecast(cell.rest), cell.top[0])) 46 | } else { 47 | Some(( 48 | Some(cell with { num: cell.num - 1 }), 49 | cell.top[cell.num-1] 50 | )) 51 | } 52 | } 53 | 54 | public func stack_size(stack: Stack) -> uint { 55 | let size = 0; 56 | loop { 57 | if let Some(res) = stack_pop(stack) { 58 | stack = res.0; 59 | size = size+1; 60 | } else { 61 | return size; 62 | } 63 | } 64 | } 65 | 66 | public throw func stack_discardDeepestItems(stack: Stack, numToDiscard: uint) -> Stack { 67 | stack_ddi2(stack, numToDiscard).0 68 | } 69 | 70 | throw func stack_ddi2(stack: Stack, numToDiscard: uint) -> (Stack, uint) { 71 | if let Some(res) = stack_pop(stack) { 72 | let (ustack, val) = res; 73 | let (subStack, subNum) = stack_ddi2(ustack, numToDiscard); 74 | if subNum == 0 { 75 | (stack_push(subStack, val), 0) 76 | } else { 77 | (subStack, subNum-1) 78 | } 79 | } else { 80 | (stack, numToDiscard) // stack is empty 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /stdlib2/arraytest.mini: -------------------------------------------------------------------------------- 1 | 2 | use std2::array::array_new; 3 | use std2::array::array_get; 4 | use std2::array::array_set; 5 | 6 | throw write func main() { 7 | asm(tests().1) { log }; 8 | } 9 | 10 | throw func tests() -> string { 11 | let a = test_1(); 12 | let b = test_2(); 13 | if (a != "") { 14 | return a; 15 | } 16 | return b; 17 | } 18 | 19 | throw func test_1() -> string { 20 | 21 | let a = array_new::(17, 8); 22 | if (array_get::(a, 6) != 8) { 23 | return "new small array isn't initialized"; 24 | } 25 | 26 | let a = array_new::(71, true); 27 | if (array_get::(a, 6) != true) { 28 | return "new large array isn't initialized"; 29 | } 30 | 31 | let a = array_new::(64, 8); 32 | a = array_set::(a, 17, 3); 33 | if (array_get::(a, 17) != 3) { 34 | return "array assignment doesn't work"; 35 | } 36 | 37 | let a = array_new::(111, 8); 38 | a = array_set::(a, 42, 3); 39 | a = array_set::(a, 99, 4); 40 | a = array_set::(a, 42, 5); 41 | if (array_get::(a, 42) != 5) { 42 | return "array overwrites don't work"; 43 | } 44 | 45 | let a = array_new::(111, 8); 46 | a = array_set::(a, 42, 3); 47 | a = array_set::(a, 99, 4); 48 | a = array_set::(a, 42, 5); 49 | if (array_get::(a, 99) != 4) { 50 | return "array overwrites corrupt other entries"; 51 | } 52 | 53 | return ""; 54 | } 55 | 56 | func test_2() -> string { 57 | 58 | let spider = "//\(oo)/\\"; 59 | 60 | let _ = array_new::(17, spider); 61 | 62 | /*if (a[6] != spider || a[7] != a[8]) { 63 | return "new small array isn't initialized"; 64 | } 65 | 66 | let a = array_new::(71, true); 67 | if (a[6] != true) { 68 | return "new large array isn't initialized"; 69 | } 70 | 71 | let a = array_new::(64, 8); 72 | a = array_set::(a, 17, 3); 73 | if (a[17] != 3) { 74 | return "array assignment doesn't work"; 75 | } 76 | 77 | let a = array_new::(111, 8); 78 | a = array_set::(a, 42, 3); 79 | a = array_set::(a, 99, 4); 80 | a = array_set::(a, 42, 5); 81 | if (a[42] != 5) { 82 | return "array overwrites don't work"; 83 | } 84 | 85 | let a = array_new::(111, 8); 86 | a = array_set::(a, 42, 3); 87 | a = array_set::(a, 99, 4); 88 | a = array_set::(a, 42, 5); 89 | if (a[99] != 4) { 90 | return "array overwrites corrupt other entries"; 91 | }*/ 92 | 93 | return ""; 94 | } 95 | -------------------------------------------------------------------------------- /stdlib2/priorityq.mini: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | // 4 | 5 | use core::array::array; 6 | use core::array::array_resize; 7 | 8 | type PqItem = struct { 9 | priority: uint, 10 | item: T, 11 | }; 12 | 13 | type PriorityQ = struct { 14 | size: uint, 15 | capacity: uint, 16 | contents: []PqItem, 17 | }; 18 | 19 | public throw func priorityq_new() -> PriorityQ { 20 | struct { 21 | size: 0, 22 | capacity: 8, 23 | contents: newarray >(8), 24 | } 25 | } 26 | 27 | public func priorityq_isEmpty(pq: PriorityQ) -> bool { 28 | pq.size == 0 29 | } 30 | 31 | public func priorityq_size(pq: PriorityQ) -> uint { 32 | pq.size 33 | } 34 | 35 | public throw func priorityq_get(pq: PriorityQ) -> option<(T, PriorityQ)> { 36 | if priorityq_isEmpty::(pq) { 37 | None 38 | } else { 39 | let ret = pq.contents[0].item; 40 | let newpq = pq with { size: pq.size-1 } 41 | with { contents: pq.contents with { [0] = pq.contents[pq.size-1] } }; 42 | Some((ret, pq_pushDown::(newpq, 0))) 43 | } 44 | } 45 | 46 | throw func pq_pushDown(pq: PriorityQ, index: uint) -> PriorityQ { 47 | loop { 48 | let firstKidIdx = 2*index+1; 49 | if firstKidIdx >= pq.size { 50 | return pq; 51 | } else if firstKidIdx+1 == pq.size { 52 | // only one kid is in play 53 | let this = pq.contents[index]; 54 | let kid = pq.contents[firstKidIdx]; 55 | if kid.priority > this.priority { 56 | return pq with { contents: pq.contents with { [index] = kid } 57 | with { [firstKidIdx] = this } }; 58 | } else { 59 | return pq; 60 | } 61 | } else { 62 | let this = pq.contents[index]; 63 | let firstKid = pq.contents[firstKidIdx]; 64 | let secondKidIdx = firstKidIdx+1; 65 | let secondKid = pq.contents[secondKidIdx]; 66 | if firstKid.priority > secondKid.priority { 67 | if firstKid.priority > this.priority { 68 | pq = pq with { contents: pq.contents with { [index] = firstKid } 69 | with { [firstKidIdx] = this } }; 70 | index = firstKidIdx; 71 | } else { 72 | return pq; 73 | } 74 | } else { 75 | if secondKid.priority > this.priority { 76 | pq = pq with { contents: pq.contents with { [index] = secondKid } 77 | with { [secondKidIdx] = this } }; 78 | index = secondKidIdx; 79 | } else { 80 | return pq; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | throw func pq_pushUp(pq: PriorityQ, index: uint) -> PriorityQ { 88 | let this = pq.contents[index]; 89 | loop { 90 | if index == 0 { 91 | return pq; 92 | } 93 | let parentIdx = (index-1)/2; 94 | let parent = pq.contents[parentIdx]; 95 | if parent.priority >= this.priority { 96 | return pq; 97 | } else { 98 | pq = pq with { contents: pq.contents with { [index] = parent } 99 | with { [parentIdx] = this } }; 100 | index = parentIdx; 101 | } 102 | } 103 | } 104 | 105 | public throw func priorityq_insert(pq: PriorityQ, item: T, priority: uint) -> PriorityQ { 106 | if pq.size == pq.capacity { 107 | let newCapacity = 8*pq.capacity; 108 | pq = pq with { capacity: newCapacity } 109 | with { contents: unsafecast<[]PqItem >(array_resize(unsafecast(pq.contents), newCapacity, ())) }; 110 | } 111 | let index = pq.size; 112 | let newpq = pq with { size: index+1 } 113 | with { contents: pq.contents with { [index] = struct { priority: priority, item: item, } } }; 114 | pq_pushUp::(newpq, index) 115 | } 116 | 117 | public write throw func priorityq_printAsArray(pq: PriorityQ) -> uint { 118 | let ret = pq.size; 119 | let cont = pq.contents; 120 | let i = 0; 121 | while i < ret { 122 | debug(cont[i]); 123 | i = i+1; 124 | } 125 | ret 126 | } 127 | -------------------------------------------------------------------------------- /stdlib2/priorityqtest.mini: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Offchain Labs, Inc. All rights reserved. 3 | */ 4 | 5 | use std2::priorityq::priorityq_new; 6 | use std2::priorityq::priorityq_isEmpty; 7 | use std2::priorityq::priorityq_insert; 8 | use std2::priorityq::priorityq_get; 9 | 10 | 11 | throw write func main() { 12 | asm(tests().1) { log }; 13 | } 14 | 15 | throw func tests() -> string { 16 | let q = priorityq_new::(); 17 | if ( ! priorityq_isEmpty::(q)) { 18 | return "new queue isn't empty"; 19 | } 20 | 21 | let q = priorityq_new::(); 22 | q = priorityq_insert::(q, 97, 3); 23 | if (priorityq_isEmpty::(q)) { 24 | return "queue with insert looks empty"; 25 | } 26 | 27 | q = priorityq_new::(); 28 | let pq = priorityq_insert::(q, 97, 3); 29 | if (pq == q) { 30 | return "new queue is equal to one with contents"; 31 | } 32 | 33 | q = priorityq_new::(); 34 | q = priorityq_insert::(q, 97, 3); 35 | q = priorityq_insert::(q, 98, 2); 36 | q = priorityq_insert::(q, 96, 4); 37 | if let Some(res) = priorityq_get::(q) { 38 | if (res.0 != 96) { 39 | return "queue didn't place 96 at the front"; 40 | } 41 | } else { 42 | return "queue is somehow empty"; 43 | } 44 | 45 | q = priorityq_new::(); 46 | let i = 0; 47 | while (i < 58) { 48 | q = priorityq_insert::(q, i+30, i+100); 49 | i = i+1; 50 | } 51 | q = priorityq_insert::(q, 97, 293); 52 | q = priorityq_insert::(q, 98, 292); 53 | q = priorityq_insert::(q, 96, 294); 54 | if let Some(res) = priorityq_get::(q) { 55 | if (res.0 != 96) { 56 | return "stress-tested queue didn't place 96 at the front"; 57 | } 58 | } else { 59 | return "stress-tested queue is somehow empty"; 60 | } 61 | 62 | return ""; 63 | } 64 | -------------------------------------------------------------------------------- /testmap.mini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OffchainLabs/arb-os/234cf670016d675095110cd944cb82fde9c460b8/testmap.mini -------------------------------------------------------------------------------- /upgradeConfig.toml: -------------------------------------------------------------------------------- 1 | data = [] 2 | 3 | -------------------------------------------------------------------------------- /upgradetests/regcopy_new.mexe: -------------------------------------------------------------------------------- 1 | {"arbos_version":58,"code":[{"opcode":59,"immediate":{"Tuple":[]},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":51,"immediate":{"Tuple":[]},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":56,"immediate":null,"debug_info":{"location":{"line":6,"column":11,"absolute":108,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":56,"immediate":{"Tuple":[]},"debug_info":{"location":{"line":6,"column":11,"absolute":108,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":64,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":144,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":64,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":82,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":144,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":82,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":20,"immediate":{"Int":"3"},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":53,"immediate":{"CodePoint":{"Internal":17}},"debug_info":{"location":{"line":9,"column":4,"absolute":222,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":57,"immediate":{"Int":"0"},"debug_info":{"location":{"line":10,"column":8,"absolute":265,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":48,"immediate":null,"debug_info":{"location":{"line":10,"column":8,"absolute":265,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":57,"immediate":null,"debug_info":{"location":{"line":10,"column":8,"absolute":265,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":52,"immediate":null,"debug_info":{"location":{"line":10,"column":8,"absolute":265,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":52,"immediate":{"CodePoint":{"Internal":17}},"debug_info":{"location":{"line":9,"column":4,"absolute":222,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":57,"immediate":{"Int":"1"},"debug_info":{"location":{"line":12,"column":4,"absolute":289,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":48,"immediate":null,"debug_info":{"location":{"line":12,"column":4,"absolute":289,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":57,"immediate":null,"debug_info":{"location":{"line":12,"column":4,"absolute":289,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}},{"opcode":52,"immediate":null,"debug_info":{"location":{"line":12,"column":4,"absolute":289,"file_id":5167093027911251668},"attributes":{"breakpoint":false}}}],"static_val":{"Tuple":[]},"globals":[{"id":18446744073709551615,"name":"_jump_table","tipe":"Any","offset":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}}],"file_info_chart":{"5167093027911251668":"regcopy_new","9360984261226144135":"core::kvs","11013747379006575874":"core::array"},"type_tree":{"inner":{"core, array, 0":[{"FixedArray":["Any",8]},"block"],"core, array, 1":[{"Struct":[{"name":"size","tipe":"Uint"},{"name":"topstep","tipe":"Uint"},{"name":"contents","tipe":{"Nominal":[["core","array"],0,[]]}}]},"array"],"core, array, 31":[{"Struct":[{"name":"bloc","tipe":{"Nominal":[["core","array"],0,[]]}},{"name":"val","tipe":"Any"}]},"arraySwapSubResult"],"core, array, 33":[{"Struct":[{"name":"f","tipe":{"Func":[{"view":false,"write":false,"throw":false,"safe":false,"sensitive":false,"closure":false,"public":false,"returns":true,"nargs":2,"nouts":1},["Any","Any"],{"Tuple":["Any","Any"]}]}},{"name":"val","tipe":"Any"}]},"opClosure"],"core, kvs, 0":[{"Struct":[{"name":"tree","tipe":{"Nominal":[["core","kvs"],2,[]]}},{"name":"size","tipe":"Uint"}]},"Kvs"],"core, kvs, 14":[{"Struct":[{"name":"kvs","tipe":{"FixedArray":["Any",8]}},{"name":"index","tipe":"Uint"},{"name":"next","tipe":{"Option":{"Nominal":[["core","kvs"],14,[]]}}}]},"Unwinder"],"core, kvs, 2":[{"FixedArray":["Any",8]},"KvsNode"],"core, kvs, 4":[{"Struct":[{"name":"key","tipe":"Any"},{"name":"value","tipe":{"Option":"Any"}}]},"KvsCell"],"regcopy_new, 0":[{"Struct":[{"name":"global1","tipe":"Uint"},{"name":"global2","tipe":{"Option":"Int"}},{"name":"jump_table","tipe":"Any"}]},"OldGlobals"]}}} 2 | -------------------------------------------------------------------------------- /upgradetests/regcopy_new.mini: -------------------------------------------------------------------------------- 1 | type OldGlobals = struct { 2 | global1: uint, 3 | global2: option, 4 | jump_table: any, 5 | }; 6 | 7 | write func main(old: OldGlobals) -> bool { 8 | asm(old) { debugprint }; 9 | asm(asm(old) uint { tlen },) { debugprint }; 10 | if (asm(old) uint { tlen } != 3) { 11 | return false; 12 | } 13 | return true; 14 | } 15 | -------------------------------------------------------------------------------- /upgradetests/regcopy_old.mexe: -------------------------------------------------------------------------------- 1 | {"arbos_version":58,"code":[{"opcode":59,"immediate":{"Tuple":[]},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":51,"immediate":{"Tuple":[{"Int":"0"},{"Tuple":[{"Int":"0"}]},{"Tuple":[]}]},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":56,"immediate":null,"debug_info":{"location":{"line":3,"column":11,"absolute":57,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}},{"opcode":56,"immediate":{"Tuple":[]},"debug_info":{"location":{"line":3,"column":11,"absolute":57,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}},{"opcode":50,"immediate":{"Int":"5"},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":81,"immediate":{"Int":"0"},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":51,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":50,"immediate":{"Tuple":[{"Int":"1"},{"Int":"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"}]},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":81,"immediate":{"Int":"1"},"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":51,"immediate":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}},{"opcode":57,"immediate":null,"debug_info":{"location":{"line":3,"column":11,"absolute":57,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}},{"opcode":48,"immediate":null,"debug_info":{"location":{"line":3,"column":11,"absolute":57,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}},{"opcode":57,"immediate":null,"debug_info":{"location":{"line":3,"column":11,"absolute":57,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}},{"opcode":52,"immediate":null,"debug_info":{"location":{"line":3,"column":11,"absolute":57,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}}],"static_val":{"Tuple":[]},"globals":[{"id":0,"name":"global1","tipe":"Uint","offset":0,"debug_info":{"location":{"line":0,"column":4,"absolute":4,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}},{"id":1,"name":"global2","tipe":{"Option":"Int"},"offset":1,"debug_info":{"location":{"line":1,"column":4,"absolute":23,"file_id":4324297106567844897},"attributes":{"breakpoint":false}}},{"id":18446744073709551615,"name":"_jump_table","tipe":"Any","offset":null,"debug_info":{"location":null,"attributes":{"breakpoint":false}}}],"file_info_chart":{"4324297106567844897":"regcopy_old","9360984261226144135":"core::kvs","11013747379006575874":"core::array"},"type_tree":{"inner":{"core, array, 0":[{"FixedArray":["Any",8]},"block"],"core, array, 1":[{"Struct":[{"name":"size","tipe":"Uint"},{"name":"topstep","tipe":"Uint"},{"name":"contents","tipe":{"Nominal":[["core","array"],0,[]]}}]},"array"],"core, array, 31":[{"Struct":[{"name":"bloc","tipe":{"Nominal":[["core","array"],0,[]]}},{"name":"val","tipe":"Any"}]},"arraySwapSubResult"],"core, array, 33":[{"Struct":[{"name":"f","tipe":{"Func":[{"view":false,"write":false,"throw":false,"safe":false,"sensitive":false,"closure":false,"public":false,"returns":true,"nargs":2,"nouts":1},["Any","Any"],{"Tuple":["Any","Any"]}]}},{"name":"val","tipe":"Any"}]},"opClosure"],"core, kvs, 0":[{"Struct":[{"name":"tree","tipe":{"Nominal":[["core","kvs"],2,[]]}},{"name":"size","tipe":"Uint"}]},"Kvs"],"core, kvs, 14":[{"Struct":[{"name":"kvs","tipe":{"FixedArray":["Any",8]}},{"name":"index","tipe":"Uint"},{"name":"next","tipe":{"Option":{"Nominal":[["core","kvs"],14,[]]}}}]},"Unwinder"],"core, kvs, 2":[{"FixedArray":["Any",8]},"KvsNode"],"core, kvs, 4":[{"Struct":[{"name":"key","tipe":"Any"},{"name":"value","tipe":{"Option":"Any"}}]},"KvsCell"]}}} 2 | -------------------------------------------------------------------------------- /upgradetests/regcopy_old.mini: -------------------------------------------------------------------------------- 1 | var global1: uint; 2 | var global2: option; 3 | 4 | write func main() { 5 | global1 = 5; 6 | global2 = Some(-3s); 7 | } 8 | -------------------------------------------------------------------------------- /upgradetests/upgrade1_new.mini: -------------------------------------------------------------------------------- 1 | type OldGlobals = struct { 2 | global1: uint, 3 | global2: option, 4 | jump_table: any, 5 | }; 6 | 7 | type NewGlobals = struct { 8 | newGlobal1: option, 9 | newGlobal2: uint, 10 | jump_table: any, 11 | }; 12 | 13 | var newGlobal1: option; 14 | var newGlobal2: uint; 15 | 16 | view write throw func main(old: OldGlobals) -> uint{ 17 | asm(struct{ 18 | newGlobal1: None, 19 | newGlobal2: 0, 20 | jump_table: asm() any { rget }, 21 | },) { rset }; 22 | 23 | newGlobal1 = old.global2; 24 | newGlobal2 = old.global1; 25 | 26 | if (newGlobal1 != Some(-3s)) { 27 | error; 28 | } 29 | 30 | if (newGlobal2 != 5) { 31 | error; 32 | } 33 | 34 | asm(42,) { debugprint }; 35 | return 2*myExpr(3); 36 | } 37 | 38 | func plusFour(x: uint) -> uint { 39 | return x+4; 40 | } 41 | 42 | func myExpr(x: uint) -> uint { 43 | return 3*(plusFour(x)); 44 | } 45 | -------------------------------------------------------------------------------- /upgradetests/upgrade1_old.mini: -------------------------------------------------------------------------------- 1 | 2 | use std::bytearray::bytearray_fromSizeAndBuffer; 3 | 4 | use std::bytestream::bytestream_new; 5 | 6 | use std::avmcodebuilder::avmCodeBuilder_new; 7 | use std::avmcodebuilder::avmCodeBuilder_append; 8 | use std::avmcodebuilder::avmCodeBuilder_finish; 9 | 10 | 11 | var global1: uint; 12 | var global2: option; 13 | 14 | view write throw func main() { 15 | global1 = 5; 16 | global2 = Some(-3s); 17 | 18 | let (sz, buf) = asm() (uint, buffer) { inbox }; 19 | let stream = bytestream_new(bytearray_fromSizeAndBuffer(sz, buf)); 20 | 21 | let builder = avmCodeBuilder_new(false); 22 | builder = if let Some(bu) = avmCodeBuilder_append(builder, stream) { 23 | bu 24 | } else { 25 | error 26 | }; 27 | let jumpTarget = avmCodeBuilder_finish(builder); 28 | 29 | asm(jumpTarget, 78) { jump }; 30 | 31 | // should never get here 32 | error; 33 | } 34 | -------------------------------------------------------------------------------- /v40-before-to-v41.sh: -------------------------------------------------------------------------------- 1 | 2 | cp arb_os/arbos_before.mexe.bkp arb_os/arbos_before.mexe 3 | 4 | sed -i 's/"Func":\[true,/"Func":\[{"view":true,"write":true,"closure":false},/g' arb_os/arbos_before.mexe 5 | sed -i 's/"Func":\[false,/"Func":\[{"view":false,"write":false,"closure":false},/g' arb_os/arbos_before.mexe 6 | 7 | cat arb_os/arbos_before.mexe | jq . | grep -i -b2 -a12 --color=always func 8 | --------------------------------------------------------------------------------