Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rgignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testdata/solidity
28 changes: 28 additions & 0 deletions integration/stylus/milestone_3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;

contract C {
function test() public view returns (uint256, bytes32, bytes32, uint256) {
bytes memory code = address(this).code;

uint256 balance = address(this).balance;
bytes32 codehash = address(this).codehash;
bytes32 manual_codehash = keccak256(code);
uint256 gasprice = tx.gasprice;

print("balance = {}".format(balance));
print("codehash = {}".format(codehash));
print("manual_codehash = {}".format(manual_codehash));
print("gasprice = {}".format(gasprice));

assert(codehash == manual_codehash);

return (balance, codehash, manual_codehash, gasprice);
}

function getCode() public view returns (bytes) {
return address(this).code;
}

function accept_donation() public payable {}
}
2 changes: 2 additions & 0 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1807,6 +1807,7 @@ pub enum Builtin {
Calldata,
ChainId,
ContractCode,
ContractCodehash,
Gasleft,
GasLimit,
Gasprice,
Expand Down Expand Up @@ -1910,6 +1911,7 @@ impl From<&ast::Builtin> for Builtin {
ast::Builtin::BaseFee => Builtin::BaseFee,
ast::Builtin::PrevRandao => Builtin::PrevRandao,
ast::Builtin::ContractCode => Builtin::ContractCode,
ast::Builtin::ContractCodehash => Builtin::ContractCodehash,
ast::Builtin::StringConcat | ast::Builtin::BytesConcat => Builtin::Concat,
ast::Builtin::RequireAuth => Builtin::RequireAuth,
ast::Builtin::AuthAsCurrContract => Builtin::AuthAsCurrContract,
Expand Down
5 changes: 4 additions & 1 deletion src/emit/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ macro_rules! emit_context {
$binary
.builder
.build_call(
$binary.module.get_function($name).unwrap(),
$binary
.module
.get_function($name)
.unwrap_or_else(|| panic!("`{}` is unimplemented", $name)),
$args,
$call_name,
)
Expand Down
20 changes: 20 additions & 0 deletions src/emit/stylus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ impl StylusTarget {
target.emit_dispatch(&mut bin);

bin.internalize(&[
"account_balance",
"account_code",
"account_code_size",
"account_codehash",
"block_basefee",
"block_coinbase",
"block_gas_limit",
Expand All @@ -99,6 +103,11 @@ impl StylusTarget {
"delegate_call_contract",
"emit_log",
"evm_gas_left",
"math_div",
"math_mod",
"math_pow",
"math_add_mod",
"math_mul_mod",
"log_txt",
"msg_reentrant",
"msg_sender",
Expand All @@ -114,6 +123,7 @@ impl StylusTarget {
"storage_load_bytes32",
"transient_store_bytes32",
"transient_load_bytes32",
"tx_gas_price",
"tx_origin",
"write_result",
]);
Expand Down Expand Up @@ -171,6 +181,10 @@ impl StylusTarget {
};
}

external!("account_balance", void_type, u8_ptr, u8_ptr);
external!("account_code", i32_type, u8_ptr, u32_val, u32_val, u8_ptr);
external!("account_code_size", i32_type, u8_ptr);
external!("account_codehash", void_type, u8_ptr, u8_ptr);
external!("block_basefee", void_type, u8_ptr);
external!("block_coinbase", void_type, u8_ptr);
external!("block_gas_limit", i64_type);
Expand Down Expand Up @@ -203,6 +217,11 @@ impl StylusTarget {
external!("evm_gas_left", i64_type);
external!("log_txt", void_type, u8_ptr, u32_val);
external!("msg_reentrant", i32_type);
external!("math_add_mod", void_type, u8_ptr, u8_ptr, u8_ptr);
external!("math_div", void_type, u8_ptr, u8_ptr);
external!("math_mod", void_type, u8_ptr, u8_ptr);
external!("math_mul_mod", void_type, u8_ptr, u8_ptr);
external!("math_pow", void_type, u8_ptr, u8_ptr);
external!("msg_sender", void_type, u8_ptr);
external!("msg_value", void_type, u8_ptr);
external!("native_keccak256", void_type, u8_ptr, u32_val, u8_ptr);
Expand All @@ -224,6 +243,7 @@ impl StylusTarget {
external!("storage_load_bytes32", void_type, u8_ptr, u8_ptr);
external!("transient_store_bytes32", void_type, u8_ptr, u8_ptr);
external!("transient_load_bytes32", void_type, u8_ptr, u8_ptr);
external!("tx_gas_price", void_type, u8_ptr);
external!("tx_origin", void_type, u8_ptr);
external!("write_result", void_type, u8_ptr, u32_val);
}
Expand Down
158 changes: 155 additions & 3 deletions src/emit/stylus/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::codegen::{Builtin, Expression};
use crate::emit::binary::Binary;
use crate::emit::storage::StorageSlot;
use crate::emit::stylus::StylusTarget;
use crate::emit::{ContractArgs, TargetRuntime, Variable};
use crate::emit::{expression::expression, ContractArgs, TargetRuntime, Variable};
use crate::emit_context;
use crate::sema::ast::{self, CallTy};
use crate::sema::ast::{Function, Type};
Expand Down Expand Up @@ -306,7 +306,7 @@ impl<'a> TargetRuntime<'a> for StylusTarget {

call!(
"vector_new",
&[len.into(), i32_const!(1).into(), buffer.into(),]
&[len.into(), i32_const!(1).into(), buffer.into()]
)
.try_as_basic_value()
.left()
Expand Down Expand Up @@ -895,6 +895,47 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
emit_context!(bin);

match expr {
Expression::Builtin {
kind: Builtin::Balance,
args,
..
} => {
let address = expression(self, bin, &args[0], vartab, function).into_array_value();

let address_ptr = bin
.builder
.build_alloca(bin.address_type(), "address")
.unwrap();

bin.builder.build_store(address_ptr, address).unwrap();

let balance = bin
.builder
.build_alloca(bin.value_type(), "balance")
.unwrap();

call!(
"account_balance",
&[address_ptr.into(), balance.into()],
"account_balance"
);

// smoelius: Balance is big-endian and must be byte-swapped.
let temp = bin.builder.build_alloca(bin.value_type(), "hash").unwrap();

call!(
"__beNtoleN",
&[
balance.into(),
temp.into(),
i32_const!(bin.ns.value_length as u64).into()
]
);

bin.builder
.build_load(bin.value_type(), temp, "balance")
.unwrap()
}
Expression::Builtin {
kind: Builtin::BaseFee,
..
Expand Down Expand Up @@ -951,6 +992,90 @@ impl<'a> TargetRuntime<'a> for StylusTarget {

chainid.into()
}
Expression::Builtin {
kind: Builtin::ContractCode,
args,
..
} => {
let address = expression(self, bin, &args[0], vartab, function).into_array_value();

let address_ptr = bin
.builder
.build_alloca(bin.address_type(), "address")
.unwrap();

bin.builder.build_store(address_ptr, address).unwrap();

let size = call!(
"account_code_size",
&[address_ptr.into()],
"account_code_size"
)
.try_as_basic_value()
.left()
.unwrap()
.into_int_value();

let account_code = bin
.builder
.build_array_alloca(bin.context.i8_type(), size, "account_code")
.unwrap();

call!(
"account_code",
&[
address_ptr.into(),
i32_zero!().into(),
size.into(),
account_code.into(),
],
"account_code"
);

call!(
"vector_new",
&[size.into(), i32_const!(1).into(), account_code.into()]
)
.try_as_basic_value()
.left()
.unwrap()
}
Expression::Builtin {
kind: Builtin::ContractCodehash,
args,
..
} => {
let address = expression(self, bin, &args[0], vartab, function).into_array_value();

let address_ptr = bin
.builder
.build_alloca(bin.address_type(), "address")
.unwrap();

bin.builder.build_store(address_ptr, address).unwrap();

let ty = bin.context.custom_width_int_type(256);

let digest_ptr = bin.builder.build_alloca(ty, "digest").unwrap();

call!(
"account_codehash",
&[address_ptr.into(), digest_ptr.into()],
"account_codehash"
);

call!(
"vector_new",
&[
i32_const!(32).into(),
i32_const!(1).into(),
digest_ptr.into()
]
)
.try_as_basic_value()
.left()
.unwrap()
}
Expression::Builtin {
kind: Builtin::Gasleft,
..
Expand Down Expand Up @@ -1020,6 +1145,33 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
call!("__memcpy", &[dest.into(), args.into(), args_len.into()]);
v
}
Expression::Builtin {
kind: Builtin::Gasprice,
..
} => {
let gasprice = bin
.builder
.build_alloca(bin.value_type(), "gasprice")
.unwrap();

call!("tx_gas_price", &[gasprice.into()], "tx_gas_price");

// smoelius: `gasprice` is big-endian and must be byte-swapped.
let temp = bin.builder.build_alloca(bin.value_type(), "hash").unwrap();

call!(
"__beNtoleN",
&[
gasprice.into(),
temp.into(),
i32_const!(bin.ns.value_length as u64).into()
]
);

bin.builder
.build_load(bin.value_type(), temp, "balance")
.unwrap()
}
Expression::Builtin {
kind: Builtin::Origin,
..
Expand Down Expand Up @@ -1108,7 +1260,7 @@ impl<'a> TargetRuntime<'a> for StylusTarget {

call!(
"vector_new",
&[size.into(), i32_const!(1).into(), return_data.into(),]
&[size.into(), i32_const!(1).into(), return_data.into()]
)
.try_as_basic_value()
.left()
Expand Down
1 change: 1 addition & 0 deletions src/sema/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,7 @@ pub enum StringLocation<T> {
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Builtin {
ContractCode,
ContractCodehash,
GetAddress,
Balance,
PayableSend,
Expand Down
2 changes: 1 addition & 1 deletion src/sema/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ pub static BUILTIN_VARIABLE: Lazy<[Prototype; 17]> = Lazy::new(|| {
name: "gasprice",
params: vec![],
ret: vec![Type::Value],
target: vec![Target::default_polkadot(), Target::EVM],
target: vec![Target::default_polkadot(), Target::EVM, Target::Stylus],
doc: "gas price for one gas unit",
constant: false,
},
Expand Down
18 changes: 17 additions & 1 deletion src/sema/expression/member_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ pub(super) fn member_access(
});
}
Type::Address(_) if id.name == "code" => {
if ns.target != Target::EVM {
if ns.target != Target::EVM && ns.target != Target::Stylus {
diagnostics.push(Diagnostic::error(
expr.loc(),
format!("'address.code' is not supported on {}", ns.target),
Expand All @@ -407,6 +407,22 @@ pub(super) fn member_access(
args: vec![expr],
});
}
Type::Address(_) if id.name == "codehash" => {
if ns.target != Target::Stylus {
diagnostics.push(Diagnostic::error(
expr.loc(),
format!("'address.codehash' is not supported on {}", ns.target),
));
return Err(());
}
used_variable(ns, &expr, symtable);
return Ok(Expression::Builtin {
loc: *loc,
tys: vec![Type::DynamicBytes],
kind: Builtin::ContractCodehash,
args: vec![expr],
});
}
Type::Contract(ref_contract_no) => {
let mut name_matches = 0;
let mut ext_expr = Err(());
Expand Down
3 changes: 2 additions & 1 deletion src/sema/mutability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,8 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
| Builtin::MinimumBalance
| Builtin::Balance
| Builtin::Accounts
| Builtin::ContractCode,
| Builtin::ContractCode
| Builtin::ContractCodehash,
..
} => state.read(loc),

Expand Down
2 changes: 2 additions & 0 deletions tests/polkadot_tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: Apache-2.0

#![allow(dead_code)]

mod enums;

#[allow(clippy::unreadable_literal, clippy::naive_bytecount)]
Expand Down
Loading