Skip to content

Commit 2c3de1a

Browse files
committed
Introduce the management of balances within Ethereum contracts.
1 parent a3e6eb9 commit 2c3de1a

File tree

11 files changed

+913
-437
lines changed

11 files changed

+913
-437
lines changed

linera-base/src/data_types.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::{
1717
str::FromStr,
1818
};
1919

20+
use alloy_primitives::U256;
2021
use async_graphql::{InputObject, SimpleObject};
2122
use custom_debug_derive::Debug;
2223
use linera_witty::{WitLoad, WitStore, WitType};
@@ -79,6 +80,38 @@ impl<'de> Deserialize<'de> for Amount {
7980
}
8081
}
8182

83+
impl From<Amount> for U256 {
84+
fn from(amount: Amount) -> U256 {
85+
U256::from(amount.0)
86+
}
87+
}
88+
89+
/// Converting amount from U256 to Amount can fail since
90+
/// Amount is a u128.
91+
#[derive(Error, Debug)]
92+
pub struct AmountConversionError(U256);
93+
94+
impl fmt::Display for AmountConversionError {
95+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96+
write!(f, "Amount conversion error for {}", self.0)
97+
}
98+
}
99+
100+
impl TryFrom<U256> for Amount {
101+
type Error = AmountConversionError;
102+
fn try_from(value: U256) -> Result<Amount, Self::Error> {
103+
let vec: [u8; 32] = value.to_be_bytes();
104+
for val in vec.iter().take(16) {
105+
if *val != 0 {
106+
return Err(AmountConversionError(value));
107+
}
108+
}
109+
let value: [u8; 16] = vec[16..].try_into().expect("value should be of length 16");
110+
let value = u128::from_be_bytes(value);
111+
Ok(Amount(value))
112+
}
113+
}
114+
82115
/// A block height to identify blocks in a chain.
83116
#[derive(
84117
Eq,
@@ -1572,6 +1605,8 @@ mod metrics {
15721605
mod tests {
15731606
use std::str::FromStr;
15741607

1608+
use alloy_primitives::U256;
1609+
15751610
use super::Amount;
15761611

15771612
#[test]
@@ -1599,4 +1634,12 @@ mod tests {
15991634
format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
16001635
);
16011636
}
1637+
1638+
#[test]
1639+
fn test_conversion_amount_u256() {
1640+
let value_amount = Amount::from_tokens(15656565652209004332);
1641+
let value_u256: U256 = value_amount.into();
1642+
let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
1643+
assert_eq!(value_amount, value_amount_rev);
1644+
}
16021645
}

linera-base/src/identifiers.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ impl AccountOwner {
7070
}
7171
}
7272

73+
#[cfg(with_revm)]
74+
impl From<Address> for AccountOwner {
75+
fn from(address: Address) -> Self {
76+
let address = address.into_array();
77+
AccountOwner::Address20(address)
78+
}
79+
}
80+
7381
#[cfg(with_testing)]
7482
impl From<CryptoHash> for AccountOwner {
7583
fn from(address: CryptoHash) -> Self {

linera-execution/src/evm/database.rs

Lines changed: 103 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use std::{
99
sync::{Arc, Mutex},
1010
};
1111

12-
use linera_base::vm::VmRuntime;
12+
use linera_base::{
13+
identifiers::{Account, AccountOwner},
14+
vm::VmRuntime,
15+
};
1316
use linera_views::common::from_bytes_option;
1417
use revm::{primitives::keccak256, Database, DatabaseCommit, DatabaseRef};
1518
use revm_context::BlockEnv;
@@ -18,7 +21,11 @@ use revm_database::{AccountState, DBErrorMarker};
1821
use revm_primitives::{address, Address, B256, U256};
1922
use revm_state::{AccountInfo, Bytecode, EvmState};
2023

21-
use crate::{ApplicationId, BaseRuntime, Batch, ContractRuntime, ExecutionError, ServiceRuntime};
24+
use crate::{
25+
evm::{read_amount, inputs::ZERO_ADDRESS},
26+
BaseRuntime, Batch, ContractRuntime, EvmExecutionError, ExecutionError,
27+
ServiceRuntime,
28+
};
2229

2330
// The runtime costs are not available in service operations.
2431
// We need to set a limit to gas usage in order to avoid blocking
@@ -74,19 +81,28 @@ pub(crate) struct DatabaseRuntime<Runtime> {
7481
/// This is the EVM address of the contract.
7582
/// At the creation, it is set to `Address::ZERO` and then later set to the correct value.
7683
pub contract_address: Address,
84+
/// The caller to the smart contract
85+
pub caller: Address,
86+
/// The value of the smart contract
87+
pub value: U256,
7788
/// The runtime of the contract.
7889
pub runtime: Arc<Mutex<Runtime>>,
7990
/// The uncommitted changes to the contract.
8091
pub changes: EvmState,
92+
/// The error that can occur during runtime.
93+
pub error: Arc<Mutex<Option<String>>>,
8194
}
8295

8396
impl<Runtime> Clone for DatabaseRuntime<Runtime> {
8497
fn clone(&self) -> Self {
8598
Self {
8699
storage_stats: self.storage_stats.clone(),
87100
contract_address: self.contract_address,
101+
caller: self.caller,
102+
value: self.value,
88103
runtime: self.runtime.clone(),
89104
changes: self.changes.clone(),
105+
error: self.error.clone(),
90106
}
91107
}
92108
}
@@ -98,23 +114,17 @@ pub enum KeyCategory {
98114
Storage,
99115
}
100116

101-
fn application_id_to_address(application_id: ApplicationId) -> Address {
102-
let application_id: [u64; 4] = <[u64; 4]>::from(application_id.application_description_hash);
103-
let application_id: [u8; 32] = linera_base::crypto::u64_array_to_be_bytes(application_id);
104-
Address::from_slice(&application_id[0..20])
105-
}
106-
107117
impl<Runtime: BaseRuntime> DatabaseRuntime<Runtime> {
108118
/// Encode the `index` of the EVM storage associated to the smart contract
109119
/// in a linera key.
110-
fn get_linera_key(key_prefix: &[u8], index: U256) -> Result<Vec<u8>, ExecutionError> {
120+
fn get_linera_key(key_prefix: &[u8], index: U256) -> Vec<u8> {
111121
let mut key = key_prefix.to_vec();
112-
bcs::serialize_into(&mut key, &index)?;
113-
Ok(key)
122+
key.extend(index.as_le_slice());
123+
key
114124
}
115125

116126
/// Returns the tag associated to the contract.
117-
fn get_address_key(&self, prefix: u8, address: Address) -> Vec<u8> {
127+
fn get_address_key(prefix: u8, address: Address) -> Vec<u8> {
118128
let mut key = vec![prefix];
119129
key.extend(address);
120130
key
@@ -129,8 +139,11 @@ impl<Runtime: BaseRuntime> DatabaseRuntime<Runtime> {
129139
Self {
130140
storage_stats: Arc::new(Mutex::new(storage_stats)),
131141
contract_address: Address::ZERO,
142+
caller: Address::ZERO,
143+
value: U256::ZERO,
132144
runtime: Arc::new(Mutex::new(runtime)),
133145
changes: HashMap::new(),
146+
error: Arc::new(Mutex::new(None)),
134147
}
135148
}
136149

@@ -144,6 +157,23 @@ impl<Runtime: BaseRuntime> DatabaseRuntime<Runtime> {
144157
*storage_stats_read = StorageStats::default();
145158
storage_stats
146159
}
160+
161+
/// Insert error into the database
162+
pub fn insert_error(&self, exec_error: ExecutionError) {
163+
let mut error = self.error.lock().expect("The lock should be possible");
164+
*error = Some(format!("Runtime error {:?}", exec_error));
165+
}
166+
167+
/// Process the error.
168+
pub fn process_any_error(&self) -> Result<(), EvmExecutionError> {
169+
let error = self.error.lock().expect("The lock should be possible");
170+
if error.is_some() {
171+
if let Some(error) = error.clone() {
172+
return Err(EvmExecutionError::RuntimeError(error));
173+
}
174+
}
175+
Ok(())
176+
}
147177
}
148178

149179
impl DBErrorMarker for ExecutionError {}
@@ -192,11 +222,42 @@ where
192222
return Ok(Some(account.info.clone()));
193223
}
194224
let mut runtime = self.runtime.lock().expect("The lock should be possible");
195-
let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, address);
225+
let account_owner = if address == self.contract_address {
226+
let application_id = runtime.application_id()?;
227+
application_id.into()
228+
} else {
229+
address.into()
230+
};
231+
// The balances being used are the ones of Linera. So, we need to
232+
// access them at first.
233+
let balance = runtime.read_owner_balance(account_owner)?;
234+
235+
let balance: U256 = balance.into();
236+
let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, address);
196237
let promise = runtime.read_value_bytes_new(key_info)?;
197238
let result = runtime.read_value_bytes_wait(&promise)?;
198-
let account_info = from_bytes_option::<AccountInfo>(&result)?;
199-
Ok(account_info)
239+
let mut account_info = match result {
240+
None => AccountInfo::default(),
241+
Some(bytes) => bcs::from_bytes(&bytes)?,
242+
};
243+
// The funds have been immediately deposited in deposit_funds.
244+
// The EVM will do the same before even the execution of
245+
// the constructor or function.
246+
// Therefore, we need to adjust the values.
247+
// This will ensure that at any time the balances in EVM
248+
// and Linera are exactly matching during the execution.
249+
let start_balance = if self.caller == address {
250+
balance + self.value
251+
} else if self.contract_address == address {
252+
balance - self.value
253+
} else {
254+
balance
255+
};
256+
account_info.balance = start_balance;
257+
// The balance is non-zero. Therefore, the account exists.đ
258+
// However, the state is None. Therefore, we need to create
259+
// a default account first.
260+
Ok(Some(account_info))
200261
}
201262

202263
fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
@@ -211,8 +272,8 @@ where
211272
Some(slot) => slot.present_value(),
212273
});
213274
}
214-
let key_prefix = self.get_address_key(KeyCategory::Storage as u8, address);
215-
let key = Self::get_linera_key(&key_prefix, index)?;
275+
let key_prefix = Self::get_address_key(KeyCategory::Storage as u8, address);
276+
let key = Self::get_linera_key(&key_prefix, index);
216277
{
217278
let mut storage_stats = self
218279
.storage_stats
@@ -249,9 +310,9 @@ where
249310
if !account.is_touched() {
250311
continue;
251312
}
252-
let key_prefix = self.get_address_key(KeyCategory::Storage as u8, *address);
253-
let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, *address);
254-
let key_state = self.get_address_key(KeyCategory::AccountState as u8, *address);
313+
let key_prefix = Self::get_address_key(KeyCategory::Storage as u8, *address);
314+
let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, *address);
315+
let key_state = Self::get_address_key(KeyCategory::AccountState as u8, *address);
255316
if account.is_selfdestructed() {
256317
batch.delete_key_prefix(key_prefix);
257318
batch.put_key_value(key_info, &AccountInfo::default())?;
@@ -278,7 +339,7 @@ where
278339
if value.present_value() == value.original_value() {
279340
storage_stats.key_no_operation += 1;
280341
} else {
281-
let key = Self::get_linera_key(&key_prefix, *index)?;
342+
let key = Self::get_linera_key(&key_prefix, *index);
282343
if value.original_value() == U256::ZERO {
283344
batch.put_key_value(key, &value.present_value())?;
284345
storage_stats.key_set += 1;
@@ -317,7 +378,7 @@ where
317378
pub fn set_contract_address(&mut self) -> Result<(), ExecutionError> {
318379
let mut runtime = self.runtime.lock().expect("The lock should be possible");
319380
let application_id = runtime.application_id()?;
320-
self.contract_address = application_id_to_address(application_id);
381+
self.contract_address = application_id.evm_address();
321382
Ok(())
322383
}
323384

@@ -326,7 +387,7 @@ where
326387
pub fn is_initialized(&self) -> Result<bool, ExecutionError> {
327388
let mut runtime = self.runtime.lock().expect("The lock should be possible");
328389
let evm_address = runtime.application_id()?.evm_address();
329-
let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, evm_address);
390+
let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, evm_address);
330391
let promise = runtime.contains_key_new(key_info)?;
331392
let result = runtime.contains_key_wait(&promise)?;
332393
Ok(result)
@@ -394,6 +455,25 @@ where
394455
block_env.gas_limit = gas_limit;
395456
Ok(block_env)
396457
}
458+
459+
pub fn deposit_funds(&self) -> Result<(), ExecutionError> {
460+
if self.value != U256::ZERO {
461+
let mut runtime = self.runtime.lock().expect("The lock should be possible");
462+
if self.caller == ZERO_ADDRESS {
463+
let error = EvmExecutionError::UnknownSigner;
464+
return Err(error.into());
465+
}
466+
let source: AccountOwner = self.caller.into();
467+
let chain_id = runtime.chain_id()?;
468+
let application_id = runtime.application_id()?;
469+
let owner: AccountOwner = application_id.into();
470+
let destination = Account { chain_id, owner };
471+
let amount = read_amount(self.value)?;
472+
473+
runtime.transfer(source, destination, amount)?;
474+
}
475+
Ok(())
476+
}
397477
}
398478

399479
impl<Runtime> DatabaseRuntime<Runtime>

0 commit comments

Comments
 (0)