mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-29 22:57:56 +00:00
Use the EVM call gas syscall variants (#436)
- Emit the `call_evm` and `delegate_call_evm` syscalls for contract calls. - The call gas is no longer ignored. --------- Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
@@ -16,10 +16,12 @@ Supported `polkadot-sdk` rev: `unstable2507`
|
|||||||
- Standard JSON mode: Don't forward EVM bytecode related output selections to solc.
|
- Standard JSON mode: Don't forward EVM bytecode related output selections to solc.
|
||||||
- The supported `polkadot-sdk` release is `unstable2507`.
|
- The supported `polkadot-sdk` release is `unstable2507`.
|
||||||
- The `INVALID` opcode and OOB memory accesses now consume all remaining gas.
|
- The `INVALID` opcode and OOB memory accesses now consume all remaining gas.
|
||||||
|
- Emit the `call_evm` and `delegate_call_evm` syscalls for contract calls.
|
||||||
|
|
||||||
### Fixed:
|
### Fixed:
|
||||||
- The missing `STOP` instruction at the end of `code` blocks.
|
- The missing `STOP` instruction at the end of `code` blocks.
|
||||||
- The missing bounds check in the internal sbrk implementation.
|
- The missing bounds check in the internal sbrk implementation.
|
||||||
|
- The call gas is no longer ignored.
|
||||||
|
|
||||||
## v0.5.0
|
## v0.5.0
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
// Use a non-zero call gas that works with call gas clipping but not with a truncate.
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Upload": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Other"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "CallGas"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": "1000000000000000000000000000000000000000000000000000000000000001"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract Other {
|
||||||
|
address public last;
|
||||||
|
uint public foo;
|
||||||
|
|
||||||
|
fallback() external {
|
||||||
|
last = msg.sender;
|
||||||
|
foo += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract CallGas {
|
||||||
|
constructor(uint _gas) payable {
|
||||||
|
Other other = new Other();
|
||||||
|
address(other).call{ gas: _gas }(hex"");
|
||||||
|
assert(other.last() == address(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,11 +23,6 @@ pragma solidity ^0.8;
|
|||||||
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -36,11 +31,6 @@ pragma solidity ^0.8;
|
|||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -48,11 +38,6 @@ pragma solidity ^0.8;
|
|||||||
},
|
},
|
||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,6 @@ pragma solidity ^0.8;
|
|||||||
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -36,11 +31,6 @@ pragma solidity ^0.8;
|
|||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -48,11 +38,6 @@ pragma solidity ^0.8;
|
|||||||
},
|
},
|
||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol");
|
|||||||
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
|
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
|
||||||
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");
|
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");
|
||||||
test_spec!(clz, "CountLeadingZeros", "CountLeadingZeros.sol");
|
test_spec!(clz, "CountLeadingZeros", "CountLeadingZeros.sol");
|
||||||
|
test_spec!(call_gas, "CallGas", "CallGas.sol");
|
||||||
|
|
||||||
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
|
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
|
||||||
vec![Instantiate {
|
vec![Instantiate {
|
||||||
@@ -451,50 +452,6 @@ fn ext_code_size() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "ReentranceDenied")]
|
|
||||||
fn send_denies_reentrancy() {
|
|
||||||
let value = 1000;
|
|
||||||
Specs {
|
|
||||||
actions: vec![
|
|
||||||
instantiate("contracts/Send.sol", "Send").remove(0),
|
|
||||||
Call {
|
|
||||||
origin: TestAddress::Alice,
|
|
||||||
dest: TestAddress::Instantiated(0),
|
|
||||||
value,
|
|
||||||
gas_limit: None,
|
|
||||||
storage_deposit_limit: None,
|
|
||||||
data: Contract::send_self(U256::from(value)).calldata,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
differential: false,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "ReentranceDenied")]
|
|
||||||
fn transfer_denies_reentrancy() {
|
|
||||||
let value = 1000;
|
|
||||||
Specs {
|
|
||||||
actions: vec![
|
|
||||||
instantiate("contracts/Transfer.sol", "Transfer").remove(0),
|
|
||||||
Call {
|
|
||||||
origin: TestAddress::Alice,
|
|
||||||
dest: TestAddress::Instantiated(0),
|
|
||||||
value,
|
|
||||||
gas_limit: None,
|
|
||||||
storage_deposit_limit: None,
|
|
||||||
data: Contract::transfer_self(U256::from(value)).calldata,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
differential: false,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create2_salt() {
|
fn create2_salt() {
|
||||||
let salt = U256::from(777);
|
let salt = U256::from(777);
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ use inkwell::values::BasicValue;
|
|||||||
|
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
|
|
||||||
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
const STATIC_CALL_FLAG: u64 = 0b0001_0000;
|
||||||
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
const REENTRANT_CALL_FLAG: u64 = 0b0000_1000;
|
||||||
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
|
|
||||||
|
|
||||||
/// Translates a contract call.
|
/// Translates a contract call.
|
||||||
pub fn call<'ctx>(
|
pub fn call<'ctx>(
|
||||||
@@ -38,33 +37,12 @@ pub fn call<'ctx>(
|
|||||||
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||||
context.build_store(output_length_pointer, output_length)?;
|
context.build_store(output_length_pointer, output_length)?;
|
||||||
|
|
||||||
let (flags, deposit_limit_value) = if static_call {
|
let flags = if static_call {
|
||||||
let flags = REENTRANT_CALL_FLAG | STATIC_CALL_FLAG;
|
REENTRANT_CALL_FLAG | STATIC_CALL_FLAG
|
||||||
(
|
|
||||||
context.xlen_type().const_int(flags as u64, false),
|
|
||||||
context.word_type().const_zero(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
call_reentrancy_heuristic(context, gas, input_length, output_length)?
|
REENTRANT_CALL_FLAG
|
||||||
};
|
};
|
||||||
|
|
||||||
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
|
||||||
context.build_store(deposit_pointer, deposit_limit_value)?;
|
|
||||||
|
|
||||||
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
|
||||||
context.builder(),
|
|
||||||
context.llvm(),
|
|
||||||
flags,
|
|
||||||
address_pointer.to_int(context),
|
|
||||||
"address_and_callee",
|
|
||||||
)?;
|
|
||||||
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
|
||||||
context.builder(),
|
|
||||||
context.llvm(),
|
|
||||||
deposit_pointer.to_int(context),
|
|
||||||
value_pointer.to_int(context),
|
|
||||||
"deposit_and_value",
|
|
||||||
)?;
|
|
||||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
context.builder(),
|
context.builder(),
|
||||||
context.llvm(),
|
context.llvm(),
|
||||||
@@ -85,10 +63,10 @@ pub fn call<'ctx>(
|
|||||||
.build_runtime_call(
|
.build_runtime_call(
|
||||||
name,
|
name,
|
||||||
&[
|
&[
|
||||||
flags_and_callee.into(),
|
context.xlen_type().const_int(flags, false).into(),
|
||||||
context.register_type().const_all_ones().into(),
|
address_pointer.to_int(context).into(),
|
||||||
context.register_type().const_all_ones().into(),
|
value_pointer.to_int(context).into(),
|
||||||
deposit_and_value.into(),
|
clip_call_gas(context, gas)?,
|
||||||
input_data.into(),
|
input_data.into(),
|
||||||
output_data.into(),
|
output_data.into(),
|
||||||
],
|
],
|
||||||
@@ -111,7 +89,7 @@ pub fn call<'ctx>(
|
|||||||
|
|
||||||
pub fn delegate_call<'ctx>(
|
pub fn delegate_call<'ctx>(
|
||||||
context: &mut Context<'ctx>,
|
context: &mut Context<'ctx>,
|
||||||
_gas: inkwell::values::IntValue<'ctx>,
|
gas: inkwell::values::IntValue<'ctx>,
|
||||||
address: inkwell::values::IntValue<'ctx>,
|
address: inkwell::values::IntValue<'ctx>,
|
||||||
input_offset: inkwell::values::IntValue<'ctx>,
|
input_offset: inkwell::values::IntValue<'ctx>,
|
||||||
input_length: inkwell::values::IntValue<'ctx>,
|
input_length: inkwell::values::IntValue<'ctx>,
|
||||||
@@ -132,18 +110,6 @@ pub fn delegate_call<'ctx>(
|
|||||||
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||||
context.build_store(output_length_pointer, output_length)?;
|
context.build_store(output_length_pointer, output_length)?;
|
||||||
|
|
||||||
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
|
||||||
context.build_store(deposit_pointer, context.word_type().const_all_ones())?;
|
|
||||||
|
|
||||||
let flags = context.xlen_type().const_int(0u64, false);
|
|
||||||
|
|
||||||
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
|
||||||
context.builder(),
|
|
||||||
context.llvm(),
|
|
||||||
flags,
|
|
||||||
address_pointer.to_int(context),
|
|
||||||
"address_and_callee",
|
|
||||||
)?;
|
|
||||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
context.builder(),
|
context.builder(),
|
||||||
context.llvm(),
|
context.llvm(),
|
||||||
@@ -164,10 +130,9 @@ pub fn delegate_call<'ctx>(
|
|||||||
.build_runtime_call(
|
.build_runtime_call(
|
||||||
name,
|
name,
|
||||||
&[
|
&[
|
||||||
flags_and_callee.into(),
|
context.xlen_type().const_int(0u64, false).into(),
|
||||||
context.register_type().const_all_ones().into(),
|
address_pointer.to_int(context).into(),
|
||||||
context.register_type().const_all_ones().into(),
|
clip_call_gas(context, gas)?,
|
||||||
deposit_pointer.to_int(context).into(),
|
|
||||||
input_data.into(),
|
input_data.into(),
|
||||||
output_data.into(),
|
output_data.into(),
|
||||||
],
|
],
|
||||||
@@ -201,82 +166,22 @@ pub fn linker_symbol<'ctx>(
|
|||||||
context.build_load_address(context.get_global(path)?.into())
|
context.build_load_address(context.get_global(path)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Solidity `address.transfer` and `address.send` call detection heuristic.
|
/// The runtime implements gas as `u64` so we clip the stipend to `u64::MAX`.
|
||||||
///
|
fn clip_call_gas<'ctx>(
|
||||||
/// # Why
|
context: &Context<'ctx>,
|
||||||
/// This heuristic is an additional security feature to guard against re-entrancy attacks
|
|
||||||
/// in case contract authors violate Solidity best practices and use `address.transfer` or
|
|
||||||
/// `address.send`.
|
|
||||||
/// While contract authors are supposed to never use `address.transfer` or `address.send`,
|
|
||||||
/// for a small cost we can be extra defensive about it.
|
|
||||||
///
|
|
||||||
/// # How
|
|
||||||
/// The gas stipend emitted by solc for `transfer` and `send` is not static, thus:
|
|
||||||
/// - Dynamically allow re-entrancy only for calls considered not transfer or send.
|
|
||||||
/// - Detected balance transfers will supply 0 deposit limit instead of `u256::MAX`.
|
|
||||||
///
|
|
||||||
/// Calls are considered transfer or send if:
|
|
||||||
/// - (Input length | Output lenght) == 0;
|
|
||||||
/// - Gas <= 2300;
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The call flags xlen `IntValue` and the deposit limit word `IntValue`.
|
|
||||||
fn call_reentrancy_heuristic<'ctx>(
|
|
||||||
context: &mut Context<'ctx>,
|
|
||||||
gas: inkwell::values::IntValue<'ctx>,
|
gas: inkwell::values::IntValue<'ctx>,
|
||||||
input_length: inkwell::values::IntValue<'ctx>,
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
||||||
output_length: inkwell::values::IntValue<'ctx>,
|
let builder = context.builder();
|
||||||
) -> anyhow::Result<(
|
|
||||||
inkwell::values::IntValue<'ctx>,
|
let clipped = context.register_type().const_all_ones();
|
||||||
inkwell::values::IntValue<'ctx>,
|
let is_overflow = builder.build_int_compare(
|
||||||
)> {
|
inkwell::IntPredicate::UGT,
|
||||||
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
|
|
||||||
let input_length_or_output_length =
|
|
||||||
context
|
|
||||||
.builder()
|
|
||||||
.build_or(input_length, output_length, "input_length_or_output_length")?;
|
|
||||||
let is_no_input_no_output = context.builder().build_int_compare(
|
|
||||||
inkwell::IntPredicate::EQ,
|
|
||||||
context.xlen_type().const_zero(),
|
|
||||||
input_length_or_output_length,
|
|
||||||
"is_no_input_no_output",
|
|
||||||
)?;
|
|
||||||
let gas_stipend = context
|
|
||||||
.word_type()
|
|
||||||
.const_int(SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD, false);
|
|
||||||
let is_gas_stipend_for_transfer_or_send = context.builder().build_int_compare(
|
|
||||||
inkwell::IntPredicate::ULE,
|
|
||||||
gas,
|
gas,
|
||||||
gas_stipend,
|
builder.build_int_z_extend(clipped, context.word_type(), "gas_clipped")?,
|
||||||
"is_gas_stipend_for_transfer_or_send",
|
"is_gas_overflow",
|
||||||
)?;
|
)?;
|
||||||
let is_balance_transfer = context.builder().build_and(
|
let truncated = builder.build_int_truncate(gas, context.register_type(), "gas_truncated")?;
|
||||||
is_no_input_no_output,
|
let call_gas = builder.build_select(is_overflow, clipped, truncated, "call_gas")?;
|
||||||
is_gas_stipend_for_transfer_or_send,
|
|
||||||
"is_balance_transfer",
|
|
||||||
)?;
|
|
||||||
let is_regular_call = context
|
|
||||||
.builder()
|
|
||||||
.build_not(is_balance_transfer, "is_balance_transfer_inverted")?;
|
|
||||||
|
|
||||||
// Call flag: Left shift the heuristic boolean value.
|
Ok(call_gas)
|
||||||
let is_regular_call_xlen = context.builder().build_int_z_extend(
|
|
||||||
is_regular_call,
|
|
||||||
context.xlen_type(),
|
|
||||||
"is_balance_transfer_xlen",
|
|
||||||
)?;
|
|
||||||
let call_flags = context.builder().build_left_shift(
|
|
||||||
is_regular_call_xlen,
|
|
||||||
context.xlen_type().const_int(3, false),
|
|
||||||
"flags",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Deposit limit value: Sign-extended the heuristic boolean value.
|
|
||||||
let deposit_limit_value = context.builder().build_int_s_extend(
|
|
||||||
is_regular_call,
|
|
||||||
context.word_type(),
|
|
||||||
"deposit_limit_value",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok((call_flags, deposit_limit_value))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ POLKAVM_IMPORT(void, block_hash, uint32_t, uint32_t)
|
|||||||
|
|
||||||
POLKAVM_IMPORT(void, block_number, uint32_t)
|
POLKAVM_IMPORT(void, block_number, uint32_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(uint32_t, call, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)
|
POLKAVM_IMPORT(uint32_t, call_evm, uint32_t, uint32_t, uint32_t, uint64_t, uint64_t, uint64_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(void, call_data_copy, uint32_t, uint32_t, uint32_t)
|
POLKAVM_IMPORT(void, call_data_copy, uint32_t, uint32_t, uint32_t)
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)
|
|||||||
|
|
||||||
POLKAVM_IMPORT(void, consume_all_gas)
|
POLKAVM_IMPORT(void, consume_all_gas)
|
||||||
|
|
||||||
POLKAVM_IMPORT(uint32_t, delegate_call, uint64_t, uint64_t, uint64_t, uint32_t, uint64_t, uint64_t)
|
POLKAVM_IMPORT(uint32_t, delegate_call_evm, uint32_t, uint32_t, uint64_t, uint64_t, uint64_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
|
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub static BLOCK_HASH: &str = "block_hash";
|
|||||||
|
|
||||||
pub static BLOCK_NUMBER: &str = "block_number";
|
pub static BLOCK_NUMBER: &str = "block_number";
|
||||||
|
|
||||||
pub static CALL: &str = "call";
|
pub static CALL: &str = "call_evm";
|
||||||
|
|
||||||
pub static CALL_DATA_COPY: &str = "call_data_copy";
|
pub static CALL_DATA_COPY: &str = "call_data_copy";
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ pub static CODE_SIZE: &str = "code_size";
|
|||||||
|
|
||||||
pub static CODE_HASH: &str = "code_hash";
|
pub static CODE_HASH: &str = "code_hash";
|
||||||
|
|
||||||
pub static DELEGATE_CALL: &str = "delegate_call";
|
pub static DELEGATE_CALL: &str = "delegate_call_evm";
|
||||||
|
|
||||||
pub static DEPOSIT_EVENT: &str = "deposit_event";
|
pub static DEPOSIT_EVENT: &str = "deposit_event";
|
||||||
|
|
||||||
|
|||||||
@@ -113,10 +113,9 @@ impl Error {
|
|||||||
let message = r#"
|
let message = r#"
|
||||||
Warning: It looks like you are using '<address payable>.send/transfer(<X>)'.
|
Warning: It looks like you are using '<address payable>.send/transfer(<X>)'.
|
||||||
Using '<address payable>.send/transfer(<X>)' is deprecated and strongly discouraged!
|
Using '<address payable>.send/transfer(<X>)' is deprecated and strongly discouraged!
|
||||||
The resolc compiler uses a heuristic to detect '<address payable>.send/transfer(<X>)' calls,
|
The revive runtime uses a heuristic to detect '<address payable>.send/transfer(<X>)' calls and
|
||||||
which disables call re-entrancy and supplies all remaining gas instead of the 2300 gas stipend.
|
the gas stipend used by the runtime is different from the EVM.
|
||||||
However, detection is not guaranteed. You are advised to carefully test this, employ
|
You are advised to carefully test this, employ re-entrancy guards or use the withdrawal pattern instead!
|
||||||
re-entrancy guards or use the withdrawal pattern instead!
|
|
||||||
Learn more on https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy
|
Learn more on https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy
|
||||||
and https://docs.soliditylang.org/en/latest/common-patterns.html#withdrawal-from-contracts
|
and https://docs.soliditylang.org/en/latest/common-patterns.html#withdrawal-from-contracts
|
||||||
"#
|
"#
|
||||||
|
|||||||
Reference in New Issue
Block a user