nascentxyz / pyrometer Goto Github PK
View Code? Open in Web Editor NEWA tool for analyzing the security and parameters of a solidity smart contract
License: Apache License 2.0
A tool for analyzing the security and parameters of a solidity smart contract
License: Apache License 2.0
pyrometer @ ed07912ff617ccdfb1255f23349d3ff0548da9de
Parsing a yul eq
expression, where one of its operands is an identifer panics with a No lhs range
error.
This occurs when simply running pyrometer Foo.sol
, where Foo.sol
:
pragma solidity ^0.8.19;
contract Foo {
struct Struct {
uint32 a;
}
function foo() public {
Struct memory data;
assembly {
let x := eq(data, 0xFF)
}
}
}
Here's the stack trace emitted:
thread 'main' panicked at 'No lhs range', src/context/exprs/cmp.rs:183:26
stack backtrace:
0: rust_begin_unwind
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/std/src/panicking.rs:578:5
1: core::panicking::panic_fmt
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/panicking.rs:67:14
2: core::panicking::panic_display
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/panicking.rs:150:5
3: core::panicking::panic_str
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/panicking.rs:134:5
4: core::option::expect_failed
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/option.rs:1932:5
5: pyrometer::context::exprs::cmp::Cmp::cmp_inner
6: pyrometer::context::exprs::cmp::Cmp::cmp_inner
at /home/inphi/src/pyrometer/src/context/exprs/cmp.rs:160:17
7: pyrometer::context::yul::yul_funcs::YulFuncCaller::yul_func_call::{{closure}}::{{closure}}
at /home/inphi/src/pyrometer/src/context/yul/yul_funcs.rs:227:25
8: pyrometer::context::ContextBuilder::apply_to_edges
at /home/inphi/src/pyrometer/src/context/mod.rs:1504:17
9: pyrometer::context::yul::yul_funcs::YulFuncCaller::yul_func_call::{{closure}}
at /home/inphi/src/pyrometer/src/context/yul/yul_funcs.rs:218:21
10: pyrometer::context::ContextBuilder::apply_to_edges
at /home/inphi/src/pyrometer/src/context/mod.rs:1504:17
11: pyrometer::context::yul::yul_funcs::YulFuncCaller::yul_func_call
at /home/inphi/src/pyrometer/src/context/yul/yul_funcs.rs:207:17
12: pyrometer::context::yul::YulBuilder::parse_ctx_yul_expr_inner
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:274:44
13: pyrometer::context::yul::YulBuilder::parse_ctx_yul_expr
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:250:13
14: pyrometer::context::yul::YulBuilder::parse_ctx_yul_stmt_inner::{{closure}}
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:134:25
15: pyrometer::context::ContextBuilder::apply_to_edges
at /home/inphi/src/pyrometer/src/context/mod.rs:1504:17
16: pyrometer::context::yul::YulBuilder::parse_ctx_yul_stmt_inner
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:70:19
17: pyrometer::context::yul::YulBuilder::parse_ctx_yul_stmt_inner::{{closure}}::{{closure}}
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:223:42
18: <core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::for_each
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/slice/iter/macros.rs:204:21
19: pyrometer::context::yul::YulBuilder::parse_ctx_yul_stmt_inner::{{closure}}
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:220:21
20: pyrometer::context::ContextBuilder::apply_to_edges
at /home/inphi/src/pyrometer/src/context/mod.rs:1504:17
21: pyrometer::context::yul::YulBuilder::parse_ctx_yul_stmt_inner
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:70:19
22: pyrometer::context::yul::YulBuilder::parse_ctx_yul_statement
at /home/inphi/src/pyrometer/src/context/yul/mod.rs:45:17
23: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner::{{closure}}
at /home/inphi/src/pyrometer/src/context/mod.rs:430:21
24: pyrometer::context::ContextBuilder::apply_to_edges
at /home/inphi/src/pyrometer/src/context/mod.rs:1504:17
25: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
at /home/inphi/src/pyrometer/src/context/mod.rs:429:27
26: pyrometer::context::ContextBuilder::parse_ctx_statement
at /home/inphi/src/pyrometer/src/context/mod.rs:62:33
27: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner::{{closure}}::{{closure}}
at /home/inphi/src/pyrometer/src/context/mod.rs:260:42
28: <core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::for_each
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/slice/iter/macros.rs:204:21
29: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner::{{closure}}
at /home/inphi/src/pyrometer/src/context/mod.rs:258:21
30: pyrometer::context::ContextBuilder::apply_to_edges
at /home/inphi/src/pyrometer/src/context/mod.rs:1504:17
31: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
at /home/inphi/src/pyrometer/src/context/mod.rs:257:27
32: pyrometer::context::ContextBuilder::parse_ctx_statement
at /home/inphi/src/pyrometer/src/context/mod.rs:62:33
33: pyrometer::context::func_call::FuncCaller::execute_call_inner
at /home/inphi/src/pyrometer/src/context/func_call/mod.rs:556:13
34: pyrometer::context::func_call::FuncCaller::func_call_inner::{{closure}}
35: pyrometer::context::ContextBuilder::apply_to_edges
at /home/inphi/src/pyrometer/src/context/mod.rs:1504:17
36: pyrometer::context::func_call::FuncCaller::func_call_inner
at /home/inphi/src/pyrometer/src/context/func_call/mod.rs:490:9
37: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
at /home/inphi/src/pyrometer/src/context/mod.rs:222:31
38: pyrometer::context::ContextBuilder::parse_ctx_statement
at /home/inphi/src/pyrometer/src/context/mod.rs:71:22
39: pyrometer::Analyzer::final_pass::{{closure}}::{{closure}}
at /home/inphi/src/pyrometer/src/lib.rs:487:21
40: core::iter::traits::iterator::Iterator::for_each::call::{{closure}}
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/iter/traits/iterator.rs:853:29
41: core::iter::traits::iterator::Iterator::fold
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/iter/traits/iterator.rs:2481:21
42: core::iter::traits::iterator::Iterator::for_each
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/iter/traits/iterator.rs:856:9
43: pyrometer::Analyzer::final_pass::{{closure}}
at /home/inphi/src/pyrometer/src/lib.rs:485:13
44: core::iter::traits::iterator::Iterator::for_each::call::{{closure}}
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/iter/traits/iterator.rs:853:29
45: core::iter::traits::iterator::Iterator::fold
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/iter/traits/iterator.rs:2481:21
46: core::iter::traits::iterator::Iterator::for_each
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/iter/traits/iterator.rs:856:9
47: pyrometer::Analyzer::final_pass
at /home/inphi/src/pyrometer/src/lib.rs:484:9
48: pyrometer::Analyzer::parse
at /home/inphi/src/pyrometer/src/lib.rs:445:21
49: pyrometer::main
at /home/inphi/src/pyrometer/cli/src/main.rs:218:9
50: core::ops::function::FnOnce::call_once
at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
In the above example, I expect typical solidity behavior, where the data
identifier in the eq
expression is parsed as a memory offset.
Cursed inheritance chaining leads to super
not finding functions in namespace
Seems to happen when inheritance 'gap' is >2 levels. Here is an example with what Ill call a 3 level 'gap', from D<>A:
abstract contract A {
function foo() virtual public returns (uint){
return 0;
}
}
contract B is A {}
contract C is B {}
contract D is C {
function foo() override public returns (uint){
return super.foo();
}
}
Putting foo
in B or C does not produce the issue
latest brock/fuzz branch
Bounds that are acceptable are labeled as "Execution guaranteed to revert here".
contract A {
function f(uint x, uint y) public pure returns (uint) {
if (x < 2) {
// fork true
return 1;
}
// fork false
if (y >= 42) {
// fork false, fork true
return 2;
}
// fork false, fork false
return 3;
}
}
Oddly, commenting the return 2;
doesn't lead to this behavior
contract A {
function f(uint x, uint y) public pure returns (uint) {
if (x < 2) {
// fork true
return 1;
}
// fork false
if (y >= 42) {
// fork false, fork true
// return 2;
}
// fork false, fork false
return 3;
}
}
Hey so I as testing out Pyrometer and used it against the backdoor example that the foundry fuzzer can't find in the hopes that it would pick up the constraints required to reach the execution path and show me the relevant bounds. Here is the contract code:
contract SymbolicTest {
// https://github.com/foundry-rs/foundry/issues/2851
function backdoor(uint256 x) external pure {
uint256 number = 99;
unchecked {
uint256 z = x - 1;
if (z == 6912213124124531) {
number = 0;
} else {
number = 1;
}
}
assert(number != 0);
}
}
My terminal output
~/repositories/personal/forge-playground main ❯ pyrometer ./contracts/SymbolicTest.sol -vv system
DONE ANALYZING IN: 1ms. Writing to cli...
Bounds: Bounds for function: function backdoor(uint256)
Bounds: Bounds for subcontext: backdoor(uint256) where:
1. (number != 0) == true
2. (z != 6912213124124531) == true
, killed: None
╭─[./contracts/SymbolicTest.sol:7:48]
│
7 │ ╭─▶ function backdoor(uint256 x) external pure {
8 │ │ uint256 number = 99;
│ │ ─────────┬─────────
│ │ ╰─────────── "number" == 99
┆ ┆
10 │ │ uint256 z = x - 1;
│ │ ────────┬────────
│ │ ╰────────── "z" ∈ [ 2**256 - 2, 2**256 - 1 ]
11 │ │ if (z == 6912213124124531) {
│ │ ┬
│ │ ╰── "z" ∈ [ 2**256 - 2, 2**256 - 1 ] && ∉ { == 6912213124124531}
┆ ┆
14 │ │ number = 1;
│ │ ───┬──
│ │ ╰──── "number" == 1
┆ ┆
18 │ ├─▶ }
│ │
│ ╰─────────── Entry function call
I would have expected a second trace where the constraint (z != 6912213124124531) == true
isn't applied.
Note I did take the assert statement out and tried as well with the same result.
Is this just a limitation of the current implementation? Just trying to understand, I know halmos picks up the counter example value.
example solidity:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
contract Sample {
uint256[49] private __gap;
}
run command:
pyrometer Sample.sol -v
out:
DONE ANALYZING IN: 0ms. Writing to cli...
Error: Graph IR Error: Node type confusion. This is potentially a bug. Please report it at https://github.com/nascentxyz/pyrometer
╭─[Sample.sol:5:2]
│
5 │ uint256[49] private __gap;
│ ─────┬─────
│ ╰─────── Node type confusion: expected node to be ContextVar but it was: Builtin(Uint(256))
───╯
Running pyrometer in toucanprotocol/contracts and one issue I am seeing:
$ pyrometer -vv -r remappings.txt contracts/RetirementCertificates.sol
[ ... ]
Error: Graph IR Error: Node type confusion. This is potentially a bug. Please report it at https://github.com/nascentxyz/pyrometer
╭─[node_modules/@openzeppelin/contracts-upgradeable/access/../utils/ContextUpgradeable.sol:36:5]
│
36 │ uint256[50] private __gap;
│ ─────┬─────
│ ╰─────── Node type confusion: expected node to be ContextVar but it was: Builtin(Uint(256))
thread 'main' panicked at 'called
Result::unwrap()on an
Errvalue: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/lib.rs:401:61 note: run with
RUST_BACKTRACE=1 environment variable to display a backtrace
The file exist.
Chaining constructor syntax leads to panic
contract A {
address a;
constructor(address _a) {
a = _a;
}
}
contract B is A {
constructor(address _a) A(_a) {}
}
contract C is B {
constructor(address _a) B(_a) {}
}
contract X {
address x;
constructor(address _x) {
x = _x;
}
}
abstract contract Y is X {
constructor(address _x) {} // abstract doesnt need to init bases
}
contract D is Y, C {
constructor(address _a) Y(_a) X(_a) C(_a) {} // inheriting abstract leads to needing to explicitly init the bases here
}
❯ pyrometer src/constructor.sol
thread 'main' panicked at 'Detached function: constructor(address)', ../code/packages/pyrometer/shared/src/nodes/func_ty.rs:237:36
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
https://docs.soliditylang.org/en/v0.8.20/contracts.html#multiple-inheritance-and-linearization
When following the installation instructions, I get the error:
[I] ➜ cargo install --path . --locked
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: pyrometer/cli/Cargo.toml
workspace: pyrometer/Cargo.toml
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package: pyrometer/cli/Cargo.toml
workspace: pyrometer/Cargo.toml
Installing cli v0.1.0 (pyrometer/cli)
Updating git repository `https://github.com/brockelmore/ariadne`
error: failed to load source for dependency `ariadne`
Caused by:
Unable to update https://github.com/brockelmore/ariadne#c2e3a8e7
Caused by:
failed to clone into: .cargo/git/db/ariadne-44ade852abe2148c
Caused by:
failed to authenticate when downloading repository: [email protected]:brockelmore/ariadne
* attempted ssh-agent authentication, but no usernames succeeded: `git`
if the git CLI succeeds then `net.git-fetch-with-cli` may help here
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
Caused by:
no authentication methods succeeded
The plan is to use Vypers IR and convert accordingly. I don't have the bandwidth to implement it on my own right now, but I opened the issue to have a place for this discussion.
The global
keyword was added in Solidity v0.8.13 and it's starting to be added to newer codebases. Supporting this will unlock pyrometer for a few different protocols!
Hi, I notice that pyrometer would parse all the dependencies used by import
. And in some projects, the dependencies may comes from npm or other package managers. If there is a need for support for import statements starts with "@", I can try to implement this and send a pull request to this repo.
There is an example
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../shared/ProtocolConstants.sol";
contract XVader is ProtocolConstants, ERC20Votes, ReentrancyGuard {
// Address of vader token
IERC20 public immutable vader;
/*
* @dev Initializes contract's state by setting vader's tokens address and
* setting current token's name and symbol.
**/
constructor(IERC20 _vader)
ERC20Permit("XVader")
ERC20("XVader", "xVADER")
{
require(
_vader != IERC20(_ZERO_ADDRESS),
"XVader::constructor: _vader cannot be a zero address"
);
vader = _vader;
}
......
Constants and immutable variables have range [0, 2**256-1]
pragma solidity ^0.8.19;
contract constant_fold {
uint constant N = 2_000_000;
// uint immutable N = 2_000_000;
function f(uint x) public returns (uint) {
if (x == N / 2) {
return 1;
}
return 2;
}
}
Would expect x == 1_000_000
inside the if.
Bounds: Bounds for function: function f(uint256)
Bounds: Bounds for subcontext: f(uint256).fork{ true }.resume{ f(uint256) } where:
1. (x == (N / 2)) == true
╭─[src/constant_fold.sol:7:46]
│
7 │ ╭─▶ function f(uint x) public returns (uint) {
8 │ │ if (x == N / 2) {
│ │ ─────┬────
│ │ ╰────── "x" ∈ [ 0, 2**255 - 1 ]
9 │ │ return 1;
│ │ ────┬───
│ │ ╰───── returns: "1" == 1
┆ ┆
12 │ ├─▶ }
│ │
│ ╰─────────── Function call
Unsure how this error manifests. Providing some behavior I noticed below. Seems to be happen when eq()
is used in conjunction with and
or or
.
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
library lib {
function foo(
address a
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
success := and(eq(a, 0), call(gas(), a, 4, 5, 6, 7, 8)) //error
// success := or(eq(a, 0), call(gas(), a, 4, 5, 6, 7, 8)) //error
// success := eq(eq(a, 0), call(gas(), a, 4, 5, 6, 7, 8)) //ok
// success := and(1, call(gas(), a, 4, 5, 6, 7, 8)) //ok, no eq
}
require(success);
}
}
Getting this error when trying to analyze a specific contract in toucanprotocol/contracts:
$ pyrometer -r remappings.txt contracts/pools/NatureCarbonTonne.sol
thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Aborted
Bounds can be propagated when memory is untouched and variables are stored and loaded at the same positions.
contract Bound {
function mask(uint256 a) public payable returns (uint) {
uint256 b = a & 0xff;
uint256 c;
assembly {
// store b and load into c
let free_mem := mload(0x40)
mstore(free_mem, b)
c := mload(free_mem)
}
// range context is lost
return c;
}
}
Would expect c
to share bounds with b
4 │ ╭─▶ function mask(uint256 a) public payable returns (uint) {
5 │ │ uint256 b = a & 0xff;
│ │ ──────────┬─────────
│ │ ╰─────────── "b" ∈ [ 0, 255 ]
┆ ┆
14 │ │ return c;
│ │ ────┬───
│ │ ╰───── returns: "c" ∈ [ 0, 2**256 - 1 ]
15 │ ├─▶ }
│ │
│ ╰─────────── Entry function call
Seems difficult to track untouched memory with operations in between that touch memory (ie: codecopy, calldatacopy, etc). Are dest, offset of those tracked?
abstract contract YInterface {
function foo() virtual external returns (uint);
}
contract Y is YInterface {
function foo() virtual override public returns (uint) {
return 1;
}
function bar(Y y) internal {
y.foo();
}
}
On the other hand, this is ok:
contract Y {
function foo() virtual public returns (uint) {
return 1;
}
function bar(Y y) internal {
y.foo();
}
}
Thinking extra layer with the virtual fn in the inherited interface matters.
If you type pyrometer --help
and look at -r, --remappings
, you need to provide 1 argument <REMAPPINGS>
which is correcponding to rh e
Looking at the ok statements, seems that the combo of the 3 (if, eq, input param) makes this an issue.
contract solady_erc20 {
function foo(address a) public {
assembly {
if eq(0x0, a) {} // bad
// if eq(0x0, 0x1) {} // ok
// let b:= eq(0x0, a) // ok
}
}
}
thread 'main' panicked at 'here Eq', src/context/exprs/require.rs:1427:26
Given the following setup:
forge init
forge install transmissions11/solmate
and the following contract in src/BasicERC721.sol
:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {ERC721} from "solmate/tokens/ERC721.sol";
contract BasicERC721 is ERC721 {
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}
function mint(address ptr) public {
_mint(ptr, 1);
}
function tokenURI(uint256) public view virtual override returns (string memory) {
return "tokenURI";
}
}
We get the following error:
pyrometer src/BasicSS2ERC721.sol
thread 'main' panicked at 'Could not find file for dependency: "solmate/tokens/ERC721.sol"'
Ideally, pyrometer should be able to parse the following remappings.txt and do the substitution:
solmate/=lib/solmate/src/
How does pyrometer work if there is storage access? For example, checking amountIn is less than user's erc20 balance.
Would expect x == 1_000_000
inside the if, however it becomes x == 1
pragma solidity ^0.8.19;
contract constant_fold {
uint N;
function f(uint x) public returns (uint) {
N = 2_000_000;
if (x == N / 2) {
return 1;
}
return 2;
}
}
Notice constraint number 2, seems that the numerator is also the denominator.
Bounds: Bounds for function: function f(uint256)
Bounds: Bounds for subcontext: f(uint256).fork{ true }.resume{ f(uint256) } where:
1. (N != 0) == true
2. (x == (N / N)) == true
╭─[src/constant_fold.sol:6:46]
│
6 │ ╭─▶ function f(uint x) public returns (uint) {
7 │ │ N = 2_000_000;
│ │ ──────┬──────
│ │ ╰──────── Storage var "N" == 2000000
8 │ │ if (x == N / 2) {
│ │ ─────┬────
│ │ ╰────── "x" == 1
9 │ │ return 1;
│ │ ────┬───
│ │ ╰───── returns: "1" == 1
┆ ┆
12 │ ├─▶ }
│ │
│ ╰─────────── Function call
(I know there's a lot of rearchitecting rn, just want to document and give a ex)
This has implications when using low level calls and trying to hit fallbacks or receives
contract A {
function foo() public {
address a = address(0);
a.call(hex"");
a.delegatecall(hex"");
bytes memory data = hex"01234567";
}
}
Notice that member access is looking for call(bytes1)
and delegatecall(bytes1)
Given the contract:
import {AccessControlEnumerableUpgradeablee} from "lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol";
contract MyContract is AccessControlEnumerableUpgradeable {
With the directory structure:
lib/
openzeppelin..../
contracts/
access/
AccessControlEnumerableUpgradeable.sol
src/
MyContract.sol
OZ defines imports in the lib as:
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControlEnumerableUpgradeable.sol";
import "./AccessControlUpgradeable.sol";
import "../utils/structs/EnumerableSetUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerableUpgradeable, AccessControlUpgradeable {
function __AccessControlEnumerable_init() internal onlyInitializing {
Pyrometer panics with thread 'main' panicked at 'Could not find file for dependency: "./IAccessControlEnumerableUpgradeable.sol"'
Could be wrong but I believe this is an issue with relative imports in libraries.
Not sure what the root cause is here. Doesn't seem to be about disambiguity, though commenting either of the internal fns removes the error, so somehow the signatures would be colliding?
contract solady_erc721 {
function foo(address from, address to, uint256 id) public {
foo(from, to, id, 0);
}
function foo(address from, address to, uint256 id, uint num) internal {}
function foo(address by, address from, address to, uint256 id) internal {}
}
2023-05-23T17:12:55.055133Z TRACE parse:parse_ctx_stmt_inner:func_call_inner:execute_call_inner:parse_ctx_stmt_inner:parse_ctx_stmt_inner:parse_ctx_expr_inner{ctx=foo(address, address, uint256)}:fn_call_expr:call_internal_func:pop_expr_latest: shared::context: popping var [from, to, id, 0] from: foo(address, address, uint256)
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: NodeConfusion("Expected variable type to be concrete but was: BuiltIn(BuiltInNode(0), Some(SolcRange { min: Concrete(RangeConcrete { val: Uint(256, 0), loc: Implicit }), min_cached: None, max: Concrete(RangeConcrete { val: Uint(256, 115792089237316195423570985008687907853269984665640564039457584007913129639935), loc: Implicit }), max_cached: None, exclusions: [] }))")', src/context/func_call/mod.rs:151:84
Spent some time simplifying the expression, left the parents commented in case it's useful for testing.
Seems to be caused by this expression require: (a | 0) > 0
failing to be concrete
contract conc_range {
function foo(address a) internal virtual {
assembly {
{
// if iszero(or(iszero(a), or(eq(a, 0x0), eq(a, 0x0)))) {} // bad
// if iszero(or(iszero(a), or(0x0, 0x0))) {} // bad
// if iszero(or(iszero(a), 0x0)) {} // bad
// if iszero(or(a, 0x0)) {} // bad
if or(a, 0x0) {} // bad
// if a {} // ok
}
}
}
}
2023-05-23T17:40:40.665609Z TRACE parse:parse_ctx_stmt_inner:func_call_inner:execute_call_inner:parse_ctx_stmt_inner:parse_ctx_stmt_inner:parse_ctx_yul_statement{ctx=foo(address)}:parse_ctx_yul_stmt_inner:parse_ctx_yul_stmt_inner:parse_ctx_yul_stmt_inner:yul_cond_op_stmt:require: pyrometer::context::exprs::require: require: (a | 0) > 0
2023-05-23T17:40:40.665636Z TRACE parse:parse_ctx_stmt_inner:func_call_inner:execute_call_inner:parse_ctx_stmt_inner:parse_ctx_stmt_inner:parse_ctx_yul_statement{ctx=foo(address)}:parse_ctx_yul_stmt_inner:parse_ctx_yul_stmt_inner:parse_ctx_yul_stmt_inner:yul_cond_op_stmt:require:update_nonconst_from_const: pyrometer::context::exprs::require: Setting range for nonconst from const
thread 'main' panicked at 'Was not concrete', src/context/exprs/require.rs:938:53
I'm currently playing around with the tool and observed that gasleft
is not yet implemented. I assume you track this somewhere, but I figured that I would still open an issue to track it properly.
thread 'main' panicked at 'not yet implemented: builtin function: "gasleft"', C:\dev\fun\pyrometer\src\context\func.rs:157:30
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Crashing when run on https://testnet.arbiscan.io/address/0x741654C43DC0562958E0b3c172F93f4bd54400F9#code (flat)
pyrometer -vvvvv ./SubaccountFactory.sol
thread 'main' panicked at 'No associated contract for context', /Users/d/dehat/src/pyrometer/shared/src/context/mod.rs:222:14
stack backtrace:
0: 0x104bf3ebc - std::backtrace_rs::backtrace::libunwind::trace::h8787edacd429d828
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
1: 0x104bf3ebc - std::backtrace_rs::backtrace::trace_unsynchronized::h7a9846606490caa8
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: 0x104bf3ebc - std::sys_common::backtrace::_print_fmt::h95438bed72312512
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/sys_common/backtrace.rs:65:5
3: 0x104bf3ebc - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h1027694b54c428d0
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/sys_common/backtrace.rs:44:22
4: 0x104c09ed8 - core::fmt::write::hb60cc483d75d6594
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/fmt/mod.rs:1213:17
5: 0x104bf18bc - std::io::Write::write_fmt::h6c907fc10bdb865b
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/io/mod.rs:1682:15
6: 0x104bf3cd0 - std::sys_common::backtrace::_print::h323796fb767df5e0
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/sys_common/backtrace.rs:47:5
7: 0x104bf3cd0 - std::sys_common::backtrace::print::h1a62458f14dd2797
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/sys_common/backtrace.rs:34:9
8: 0x104bf532c - std::panicking::default_hook::{{closure}}::h03c6918072c36210
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:267:22
9: 0x104bf5084 - std::panicking::default_hook::hd0f3cf66b6a0fb5e
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:286:9
10: 0x104bf5950 - std::panicking::rust_panic_with_hook::h9ed2a7a45efbd034
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:688:13
11: 0x104bf5770 - std::panicking::begin_panic_handler::{{closure}}::h535244d6186e3534
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:579:13
12: 0x104bf4324 - std::sys_common::backtrace::__rust_end_short_backtrace::ha542aa49031c5cb5
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/sys_common/backtrace.rs:137:18
13: 0x104bf54cc - rust_begin_unwind
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:575:5
14: 0x104c17514 - core::panicking::panic_fmt::hc1e7b11add95109d
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/panicking.rs:64:14
15: 0x104c08fe4 - core::panicking::panic_display::h4f6a6d9c90c2a577
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/panicking.rs:147:5
16: 0x104c08fa8 - core::panicking::panic_str::h093e45861a4f3940
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/panicking.rs:131:5
17: 0x104c174e0 - core::option::expect_failed::h13082a32d0e23cf7
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/option.rs:1924:5
18: 0x104a9c198 - shared::context::ContextNode::associated_contract::h29c7bbbb1891bb7d
19: 0x104a8ada8 - pyrometer::context::ContextBuilder::parse_ctx_expr::hb2cd7871a654ff4f
20: 0x104a67428 - pyrometer::context::func::FuncCaller::intrinsic_func_call::hc4a69e758636217f
21: 0x104a8b9e8 - pyrometer::context::ContextBuilder::parse_ctx_expr::hb2cd7871a654ff4f
22: 0x104acf550 - <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter::h4212837b07f4a57f
23: 0x104a8b814 - pyrometer::context::ContextBuilder::parse_ctx_expr::hb2cd7871a654ff4f
24: 0x104a887c4 - pyrometer::context::ContextBuilder::parse_ctx_stmt_inner::he163909ed75e91b7
25: 0x104a87ca8 - pyrometer::context::ContextBuilder::parse_ctx_stmt_inner::hd6e6110af1f15840
26: 0x104a696d0 - pyrometer::context::func::FuncCaller::execute_call_inner::h8a3c5e3f2c0f97b2
27: 0x104a68cd0 - pyrometer::context::func::FuncCaller::func_call_inner::h0603424edc9566cd
28: 0x104a865dc - pyrometer::context::ContextBuilder::parse_ctx_stmt_inner::hbc500f51158ad4e5
29: 0x104a8f948 - pyrometer::Analyzer::parse::h38ee9fc978d60b29
30: 0x104a0b710 - pyrometer::main::h8ea434558af1757b
at /Users/d/dehat/src/pyrometer/cli/src/main.rs:119:42
31: 0x104a4613c - core::ops::function::FnOnce::call_once::h095888031204766b
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ops/function.rs:250:5
32: 0x104a4613c - std::sys_common::backtrace::__rust_begin_short_backtrace::h020feb58b0580f9e
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/sys_common/backtrace.rs:121:18
33: 0x1049fb2f0 - std::rt::lang_start::{{closure}}::h822ff7b8425f1b04
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/rt.rs:166:18
34: 0x104beda68 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h602b6f29451c74c7
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ops/function.rs:287:13
35: 0x104beda68 - std::panicking::try::do_call::h8ad76c1527a3892b
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:483:40
36: 0x104beda68 - std::panicking::try::hbfc990f7ac38d34c
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:447:19
37: 0x104beda68 - std::panic::catch_unwind::h936bb5789e0b4241
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panic.rs:140:14
38: 0x104beda68 - std::rt::lang_start_internal::{{closure}}::h896fa40a3ab062d0
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/rt.rs:148:48
39: 0x104beda68 - std::panicking::try::do_call::h21fb8583a42bdaac
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:483:40
40: 0x104beda68 - std::panicking::try::h90121103ee57470a
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:447:19
41: 0x104beda68 - std::panic::catch_unwind::hfd5c7b5d06b8f149
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panic.rs:140:14
42: 0x104beda68 - std::rt::lang_start_internal::h9f0566e553deb11e
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/rt.rs:148:20
43: 0x104a10b78 - _main
pragma solidity >=0.8.0;
contract struct_push {
mapping(uint => uint) map;
uint public index;
uint public a;
function foo() public payable returns (uint, uint, uint, uint, uint) {
require(index == 0);
(a, map[++index]) = (index, bar(++index));
return (a, index, map[0], map[1], map[2]);
}
function bar(uint i) internal pure returns (uint) {
return i;
}
}
Foundry output of foo()
:
Running 1 test for test/any.t.sol:CounterTest
[PASS] testPush() (gas: 54875)
Traces:
[172906] CounterTest::setUp()
├─ [118365] → new struct_push@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
│ └─ ← 591 bytes of code
└─ ← ()
[54875] CounterTest::testPush()
├─ [49591] struct_push::foo()
│ └─ ← 0, 2, 0, 0, 1
└─ ← ()
Test result: ok. 1 passed; 0 failed; finished in 248.38µs
a == 1
, though is expected to be 0.
map[2] == [0, 2^256-1]
and is expected to be 1. Though all the mapping slots are pessimistic so this seems currently unsupported.
Low level calls use member access on address.
contract A {
function foo() public {
address a = address(0);
a.call(hex"");
a.delegatecall(hex"");
bytes memory data = hex"01234567";
a.call(data);
a.delegatecall(data);
}
}
edit: staticcall included as well
Also seen in #39
Version:
➜ pyrometer --version
cli 0.1.0
This line in our repo triggers an error.
Error: Unexpected type as array index
╭─[contracts/L1/L2OutputOracle.sol:296:16]
│
296 │ return l2Outputs[getL2OutputIndexAfter(_l2BlockNumber)];
│ ────────────────────────┬───────────────────────
│ ╰───────────────────────── Expected single expr evaluation of index expression, but was: (Single(NodeIndex(2871)), Multi([Single(NodeIndex(2864))])). This is a bug. Please report it at github.com/nascentxyz/pyrometer.
The command I'm using is:
cd packages/contracts-bedrock
pyrometer contracts/L1/L2OutputOracle.sol --remappings remappings.txt
A minimal test case is:
contract Test28 is Test {
bytes32[] public arr;
constructor() {
arr.push(hex"1234");
arr.push(hex"5678");
}
function getNum() public pure returns (uint256) {
return 1;
}
function test_pyrobug() external returns (bytes32) {
return arr[getNum()];
}
}
thread 'main' panicked at 'not yet implemented', ~/pyrometer/src/lib.rs:382:30
stack backtrace:
0: _rust_begin_unwind
1: core::panicking::panic_fmt
2: core::panicking::panic
3: pyrometer::Analyzer::parse_contract_def::{{closure}}
4: <core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::for_each
at /private/tmp/rust-20230210-6343-w92jca/rustc-1.67.1-src/library/core/src/slice/iter/macros.rs:211:21
5: pyrometer::Analyzer::parse_contract_def
at ~/pyrometer/src/lib.rs:352:9
6: pyrometer::Analyzer::parse_source_unit_part
at ~/pyrometer/src/lib.rs:259:37
7: pyrometer::Analyzer::parse_source_unit::{{closure}}
at ~/pyrometer/src/lib.rs:230:37
8: core::iter::traits::iterator::Iterator::for_each::call::{{closure}}
at /private/tmp/rust-20230210-6343-w92jca/rustc-1.67.1-src/library/core/src/iter/traits/iterator.rs:828:29
9: <core::iter::adapters::enumerate::Enumerate<I> as core::iter::traits::iterator::Iterator>::fold::enumerate::{{closure}}
at /private/tmp/rust-20230210-6343-w92jca/rustc-1.67.1-src/library/core/src/iter/adapters/enumerate.rs:106:27
10: core::iter::traits::iterator::Iterator::fold
at /private/tmp/rust-20230210-6343-w92jca/rustc-1.67.1-src/library/core/src/iter/traits/iterator.rs:2414:21
11: <core::iter::adapters::enumerate::Enumerate<I> as core::iter::traits::iterator::Iterator>::fold
at /private/tmp/rust-20230210-6343-w92jca/rustc-1.67.1-src/library/core/src/iter/adapters/enumerate.rs:112:9
12: core::iter::traits::iterator::Iterator::for_each
at /private/tmp/rust-20230210-6343-w92jca/rustc-1.67.1-src/library/core/src/iter/traits/iterator.rs:831:9
13: pyrometer::Analyzer::parse_source_unit
at ~/pyrometer/src/lib.rs:225:9
14: pyrometer::Analyzer::parse
at ~/pyrometer/src/lib.rs:189:29
15: pyrometer::main
at ~/pyrometer/cli/src/main.rs:119:42
16: core::ops::function::FnOnce::call_once
at /private/tmp/rust-20230210-6343-w92jca/rustc-1.67.1-src/library/core/src/ops/function.rs:507:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Similar to #38 but found the issue at additional contexts:
abstract contract A {
function foo() virtual external returns (uint) {}
}
abstract contract B is A {
A a;
function liquidateBorrowInternal(A _a) internal returns (uint, uint) {
// uint b = foo();
uint b2 = _a.foo();
uint b3 = a.foo();
// if (b != 1) {}
if (b2 != 1) {}
if (b3 != 1) {}
return (b2, b3);
}
function foo() public virtual override returns (uint){
return 1;
}
}
Getting this error when trying to run Pyrometer over a code base that imports OpenZeppelin's ERC-721:
(bool success, ) = recipient.call{value: amount}("");
Function call block is currently unsupported. Relevant changes onmsg
will not take affect
Full logs:
Installed the latest cargo install --path . --locked
beta release (0cfae6f) and got the following warnings. Opening this issue to simply track those and don't forget about it.
warning: unreachable call
--> shared\src\context\mod.rs:964:22
|
964 | e => Err(GraphError::NodeConfusion(panic!(
| ______________________^^^^^^^^^^^^^^^^^^^^^^^^^_-
| | |
| | unreachable call
965 | | "Node type confusion: expected node to be Context but it was: {e:?}"
966 | | ))),
| |_____________- any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default
Compiling pyrometer v0.1.0 (C:\Dev\Blockchain\pyrometer)
warning: unreachable statement
--> src\context\mod.rs:1055:21
|
1054 | todo!("{ty:?}");
| --------------- any code following this expression is unreachable
1055 | ctx.push_expr(ExprRet::Null, self).into_expr_err(*loc)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
|
= note: `#[warn(unreachable_code)]` on by default
warning: unreachable call
--> src\context\mod.rs:1378:24
|
1378 | return Err(ExprErr::GraphError(
| ^^^^^^^^^^^^^^^^^^^ unreachable call
...
1381 | / panic!(
1382 | | "Variable update of {} in old context: parent: {}, child: {:#?}",
1383 | | cvar_node.display_name(self).unwrap(),
1384 | | ctx.path(self),
1385 | | child
1386 | | ), //),
| |_________________- any code following this expression is unreachable
warning: unused import: `itertools::Itertools`
--> src\context\func_call\mod.rs:8:5
|
8 | use itertools::Itertools;
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: unused import: `CodeLocation`
--> src\context\analyzers\bounds.rs:15:25
|
15 | use solang_parser::pt::{CodeLocation, StorageLocation};
| ^^^^^^^^^^^^
warning: unused import: `analyzer::AnalyzerLike`
--> src\context\analyzers\bounds.rs:9:5
|
9 | analyzer::AnalyzerLike,
| ^^^^^^^^^^^^^^^^^^^^^^
warning: unused import: `shared::analyzer::AnalyzerLike`
--> src\context\analyzers\var_analyzer\report_display.rs:3:5
|
3 | use shared::analyzer::AnalyzerLike;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: `shared` (lib) generated 1 warning
warning: `pyrometer` (lib) generated 6 warnings
Getting this error when trying to analyze a specific contract in toucanprotocol/contracts:
$ pyrometer -vv -r remappings.txt contracts/CarbonOffsetBatches.sol
thread 'main' panicked at 'assertion failed: input_exprs.len() == 2', src/context/func_call/intrinsic_call.rs:201:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I have this when running on the $SAFEMOON contract:
https://bscscan.deth.net/address/0x8e7877fe58bc2a979692862df9e84e1bf7ec94ae
(contract is already flattened)
thread 'main' panicked at 'Unknown member access on address: "balance"', src/context/exprs/member_access.rs:576:30
stack backtrace:
0: rust_begin_unwind
at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/panicking.rs:579:5
1: core::panicking::panic_fmt
at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/core/src/panicking.rs:64:14
2: pyrometer::context::exprs::member_access::MemberAccess::builtin_member_access
3: pyrometer::context::exprs::member_access::MemberAccess::member_access
4: pyrometer::context::ContextBuilder::parse_ctx_expr
5: pyrometer::context::exprs::require::Require::handle_require
6: pyrometer::context::func_call::intrinsic_call::IntrinsicFuncCaller::intrinsic_func_call
7: pyrometer::context::func_call::FuncCaller::fn_call_expr
8: pyrometer::context::ContextBuilder::parse_ctx_expr
9: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
10: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
11: pyrometer::context::func_call::FuncCaller::execute_call_inner
12: pyrometer::context::func_call::FuncCaller::func_call_inner
13: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
14: pyrometer::Analyzer::final_pass
15: pyrometer::Analyzer::parse
16: pyrometer::main
at /home/cergyketh/dev/evm/external/pyrometer/cli/src/main.rs:137:9
17: core::ops::function::FnOnce::call_once
at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/core/src/ops/function.rs:250:5
Maybe the native .balance access is not handled?
cli 0.1.0
cmd:
pyrometer <path>/ConstantProduct2-Flat.sol
Flattened solidity file (.txt to support upload here): ConstantProduct2-Flat.sol.txt
Output:
ctx: calcReserve(uint256[], uint256, uint256, bytes), Assign(File(0, 15404, 15448), Variable(Identifier { loc: File(0, 15404, 15411), name: "reserve" }), Divide(File(0, 15414, 15448), Power(File(0, 15414, 15432), Variable(Identifier { loc: File(0, 15414, 15427), name: "lpTokenSupply" }), NumberLiteral(File(0, 15431, 15432), "2", "", None)), Variable(Identifier { loc: File(0, 15435, 15448), name: "EXP_PRECISION" })))
ctx: calcReserve(uint256[], uint256, uint256, bytes), Variable(Identifier { loc: File(0, 15404, 15411), name: "reserve" })
ctx: calcReserve(uint256[], uint256, uint256, bytes), Divide(File(0, 15414, 15448), Power(File(0, 15414, 15432), Variable(Identifier { loc: File(0, 15414, 15427), name: "lpTokenSupply" }), NumberLiteral(File(0, 15431, 15432), "2", "", None)), Variable(Identifier { loc: File(0, 15435, 15448), name: "EXP_PRECISION" }))
ctx: calcReserve(uint256[], uint256, uint256, bytes), Power(File(0, 15414, 15432), Variable(Identifier { loc: File(0, 15414, 15427), name: "lpTokenSupply" }), NumberLiteral(File(0, 15431, 15432), "2", "", None))
ctx: calcReserve(uint256[], uint256, uint256, bytes), Variable(Identifier { loc: File(0, 15414, 15427), name: "lpTokenSupply" })
ctx: calcReserve(uint256[], uint256, uint256, bytes), NumberLiteral(File(0, 15431, 15432), "2", "", None)
ctx: calcReserve(uint256[], uint256, uint256, bytes), Variable(Identifier { loc: File(0, 15435, 15448), name: "EXP_PRECISION" })
ctx: calcReserve(uint256[], uint256, uint256, bytes), Assign(File(0, 15458, 15521), Variable(Identifier { loc: File(0, 15458, 15465), name: "reserve" }), FunctionCall(File(0, 15468, 15521), MemberAccess(File(0, 15468, 15486), Variable(Identifier { loc: File(0, 15468, 15475), name: "LibMath" }), Identifier { loc: File(0, 15476, 15486), name: "roundedDiv" }), [Variable(Identifier { loc: File(0, 15487, 15494), name: "reserve" }), ArraySubscript(File(0, 15496, 15520), Variable(Identifier { loc: File(0, 15496, 15504), name: "reserves" }), Some(ConditionalOperator(File(0, 15505, 15519), Equal(File(0, 15505, 15511), Variable(Identifier { loc: File(0, 15505, 15506), name: "j" }), NumberLiteral(File(0, 15510, 15511), "1", "", None)), NumberLiteral(File(0, 15514, 15515), "0", "", None), NumberLiteral(File(0, 15518, 15519), "1", "", None))))]))
ctx: calcReserve(uint256[], uint256, uint256, bytes), Variable(Identifier { loc: File(0, 15458, 15465), name: "reserve" })
ctx: calcReserve(uint256[], uint256, uint256, bytes), FunctionCall(File(0, 15468, 15521), MemberAccess(File(0, 15468, 15486), Variable(Identifier { loc: File(0, 15468, 15475), name: "LibMath" }), Identifier { loc: File(0, 15476, 15486), name: "roundedDiv" }), [Variable(Identifier { loc: File(0, 15487, 15494), name: "reserve" }), ArraySubscript(File(0, 15496, 15520), Variable(Identifier { loc: File(0, 15496, 15504), name: "reserves" }), Some(ConditionalOperator(File(0, 15505, 15519), Equal(File(0, 15505, 15511), Variable(Identifier { loc: File(0, 15505, 15506), name: "j" }), NumberLiteral(File(0, 15510, 15511), "1", "", None)), NumberLiteral(File(0, 15514, 15515), "0", "", None), NumberLiteral(File(0, 15518, 15519), "1", "", None))))])
ctx: calcReserve(uint256[], uint256, uint256, bytes), Variable(Identifier { loc: File(0, 15468, 15475), name: "LibMath" })
ctx: calcReserve(uint256[], uint256, uint256, bytes), Variable(Identifier { loc: File(0, 15487, 15494), name: "reserve" })
ctx: calcReserve(uint256[], uint256, uint256, bytes), ArraySubscript(File(0, 15496, 15520), Variable(Identifier { loc: File(0, 15496, 15504), name: "reserves" }), Some(ConditionalOperator(File(0, 15505, 15519), Equal(File(0, 15505, 15511), Variable(Identifier { loc: File(0, 15505, 15506), name: "j" }), NumberLiteral(File(0, 15510, 15511), "1", "", None)), NumberLiteral(File(0, 15514, 15515), "0", "", None), NumberLiteral(File(0, 15518, 15519), "1", "", None))))
ctx: calcReserve(uint256[], uint256, uint256, bytes), Variable(Identifier { loc: File(0, 15496, 15504), name: "reserves" })
ctx: calcReserve(uint256[], uint256, uint256, bytes), ConditionalOperator(File(0, 15505, 15519), Equal(File(0, 15505, 15511), Variable(Identifier { loc: File(0, 15505, 15506), name: "j" }), NumberLiteral(File(0, 15510, 15511), "1", "", None)), NumberLiteral(File(0, 15514, 15515), "0", "", None), NumberLiteral(File(0, 15518, 15519), "1", "", None))
ctx: calcReserve(uint256[], uint256, uint256, bytes).fork.0, NumberLiteral(File(0, 15514, 15515), "0", "", None)
ctx: calcReserve(uint256[], uint256, uint256, bytes).fork.0, Variable(Identifier { loc: File(0, 15505, 15506), name: "j" })
ctx: calcReserve(uint256[], uint256, uint256, bytes).fork.0, NumberLiteral(File(0, 15510, 15511), "1", "", None)
ctx: calcReserve(uint256[], uint256, uint256, bytes).fork.1, NumberLiteral(File(0, 15518, 15519), "1", "", None)
ctx: calcReserve(uint256[], uint256, uint256, bytes).fork.1, Variable(Identifier { loc: File(0, 15505, 15506), name: "j" })
ctx: calcReserve(uint256[], uint256, uint256, bytes).fork.1, NumberLiteral(File(0, 15510, 15511), "1", "", None)
thread 'main' panicked at 'Expected single expr evaluation of index expression, but was: (Single((ContextNode(129), NodeIndex(152))), Fork(SingleLiteral((ContextNode(153), NodeIndex(157))), SingleLiteral((ContextNode(154), NodeIndex(167))))). This is a bug. Please report it at github.com/nascentxyz/pyrometer.', src/context/exprs/array.rs:96:18
stack backtrace:
0: rust_begin_unwind
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:575:5
1: core::panicking::panic_fmt
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/panicking.rs:64:14
2: pyrometer::context::exprs::array::Array::index_into_array_inner
3: pyrometer::context::exprs::array::Array::index_into_array
4: pyrometer::context::ContextBuilder::parse_ctx_expr
5: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter
6: pyrometer::context::func::FuncCaller::fn_call_expr
7: pyrometer::context::ContextBuilder::parse_ctx_expr
8: pyrometer::context::ContextBuilder::assign_exprs
9: pyrometer::context::ContextBuilder::parse_ctx_expr
10: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
11: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
12: pyrometer::context::func::FuncCaller::execute_call_inner
13: pyrometer::context::func::FuncCaller::func_call_inner
14: pyrometer::context::ContextBuilder::parse_ctx_stmt_inner
15: pyrometer::Analyzer::parse
16: pyrometer::main
at ./src/main.rs:119:42
17: core::ops::function::FnOnce::call_once
at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.