Skip to content

Commit d80b096

Browse files
committed
Add the Transfer operation.
1 parent bfc5a99 commit d80b096

File tree

1 file changed

+74
-23
lines changed

1 file changed

+74
-23
lines changed

linera-execution/src/evm/revm.rs

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ enum ContractRuntimePrecompile {
292292
},
293293
/// Calling `validation_round` of `ContractRuntime`
294294
ValidationRound,
295+
/// Calling `transfer` of `ContractRuntime`
296+
Transfer {
297+
account: Account,
298+
amount: Amount,
299+
},
295300
}
296301

297302
/// Some functionalities from the ServiceRuntime not in BaseRuntime
@@ -485,28 +490,62 @@ impl<'a, Runtime: ContractRuntime> PrecompileProvider<Ctx<'a, Runtime>> for Cont
485490
}
486491
}
487492

493+
fn get_evm_destination<Runtime: ContractRuntime>(
494+
context: &mut Ctx<'_, Runtime>,
495+
account: Account,
496+
) -> Result<Option<Address>, ExecutionError> {
497+
let mut runtime = context.db().0.runtime.lock().unwrap();
498+
let chain_id = runtime.chain_id()?;
499+
if chain_id == account.chain_id {
500+
return Ok(None);
501+
}
502+
Ok(account.owner.to_evm_address())
503+
}
504+
505+
506+
/// If we are using the `None` case of `fn call` and `fn create` then the transfer
507+
/// of ethers is done by REVM. On the other hand, if we are managing the call/create
508+
/// by hand, then we need to do the transfers ourselves.
509+
fn revm_transfer<Runtime: ContractRuntime>(
510+
context: &mut Ctx<'_, Runtime>,
511+
source: Address,
512+
destination: Address,
513+
value: U256,
514+
) -> Result<(), ExecutionError> {
515+
// In Ethereum, all transfers matter
516+
if let Some(error) = context.journal().transfer(source, destination, value)? {
517+
let error = format!("{error:?}");
518+
let error = EvmExecutionError::TransactError(error);
519+
return Err(error.into());
520+
}
521+
Ok(())
522+
}
523+
488524
impl<'a> ContractPrecompile {
489525
fn contract_runtime_call<Runtime: ContractRuntime>(
490526
request: ContractRuntimePrecompile,
491527
context: &mut Ctx<'a, Runtime>,
492528
) -> Result<Vec<u8>, ExecutionError> {
493-
let mut runtime = context.db().0.runtime.lock().unwrap();
494529
match request {
495530
ContractRuntimePrecompile::AuthenticatedOwner => {
531+
let mut runtime = context.db().0.runtime.lock().unwrap();
496532
let account_owner = runtime.authenticated_owner()?;
497533
Ok(bcs::to_bytes(&account_owner)?)
498534
}
499535

500536
ContractRuntimePrecompile::MessageOriginChainId => {
537+
let mut runtime = context.db().0.runtime.lock().unwrap();
501538
let origin_chain_id = runtime.message_origin_chain_id()?;
502539
Ok(bcs::to_bytes(&origin_chain_id)?)
503540
}
504541

505542
ContractRuntimePrecompile::MessageIsBouncing => {
543+
let mut runtime = context.db().0.runtime.lock().unwrap();
506544
let result = runtime.message_is_bouncing()?;
507545
Ok(bcs::to_bytes(&result)?)
508546
}
509547
ContractRuntimePrecompile::AuthenticatedCallerId => {
548+
let mut runtime = context.db().0.runtime.lock().unwrap();
510549
let application_id = runtime.authenticated_caller_id()?;
511550
Ok(bcs::to_bytes(&application_id)?)
512551
}
@@ -524,27 +563,34 @@ impl<'a> ContractPrecompile {
524563
grant,
525564
message,
526565
};
566+
let mut runtime = context.db().0.runtime.lock().unwrap();
527567
runtime.send_message(send_message_request)?;
528568
Ok(vec![])
529569
}
530570
ContractRuntimePrecompile::TryCallApplication { target, argument } => {
531571
let authenticated = true;
572+
let mut runtime = context.db().0.runtime.lock().unwrap();
532573
runtime.try_call_application(authenticated, target, argument)
533574
}
534575
ContractRuntimePrecompile::Emit { stream_name, value } => {
576+
let mut runtime = context.db().0.runtime.lock().unwrap();
535577
let result = runtime.emit(stream_name, value)?;
536578
Ok(bcs::to_bytes(&result)?)
537579
}
538580
ContractRuntimePrecompile::ReadEvent {
539581
chain_id,
540582
stream_name,
541583
index,
542-
} => runtime.read_event(chain_id, stream_name, index),
584+
} => {
585+
let mut runtime = context.db().0.runtime.lock().unwrap();
586+
runtime.read_event(chain_id, stream_name, index)
587+
}
543588
ContractRuntimePrecompile::SubscribeToEvents {
544589
chain_id,
545590
application_id,
546591
stream_name,
547592
} => {
593+
let mut runtime = context.db().0.runtime.lock().unwrap();
548594
runtime.subscribe_to_events(chain_id, application_id, stream_name)?;
549595
Ok(vec![])
550596
}
@@ -553,17 +599,40 @@ impl<'a> ContractPrecompile {
553599
application_id,
554600
stream_name,
555601
} => {
602+
let mut runtime = context.db().0.runtime.lock().unwrap();
556603
runtime.unsubscribe_from_events(chain_id, application_id, stream_name)?;
557604
Ok(vec![])
558605
}
559606
ContractRuntimePrecompile::QueryService {
560607
application_id,
561608
query,
562-
} => runtime.query_service(application_id, query),
609+
} => {
610+
let mut runtime = context.db().0.runtime.lock().unwrap();
611+
runtime.query_service(application_id, query)
612+
}
563613
ContractRuntimePrecompile::ValidationRound => {
614+
let mut runtime = context.db().0.runtime.lock().unwrap();
564615
let value = runtime.validation_round()?;
565616
Ok(bcs::to_bytes(&value)?)
566617
}
618+
ContractRuntimePrecompile::Transfer { account, amount } => {
619+
if amount != Amount::ZERO {
620+
let destination = {
621+
let destination = get_evm_destination(context, account)?;
622+
destination.unwrap_or(FAUCET_ADDRESS)
623+
};
624+
let application_id = {
625+
let mut runtime = context.db().0.runtime.lock().unwrap();
626+
let application_id = runtime.application_id()?;
627+
let source = application_id.into();
628+
runtime.transfer(source, account, amount)?;
629+
application_id
630+
};
631+
let source: Address = application_id.evm_address();
632+
revm_transfer(context, source, destination, amount.into())?;
633+
}
634+
Ok(vec![])
635+
}
567636
}
568637
}
569638

@@ -892,7 +961,7 @@ impl<Runtime: ContractRuntime> CallInterceptorContract<Runtime> {
892961
// a contract that do not yet exist.
893962
// It is a common construction. We can see that in ERC20 contract code
894963
// for example for burning and minting.
895-
Self::revm_transfer(
964+
revm_transfer(
896965
context,
897966
self.db.contract_address,
898967
FAUCET_ADDRESS,
@@ -994,7 +1063,7 @@ impl<Runtime: ContractRuntime> CallInterceptorContract<Runtime> {
9941063
let destination = Account { chain_id, owner };
9951064
runtime.transfer(source, destination, amount)?;
9961065
}
997-
Self::revm_transfer(context, inputs.caller, inputs.target_address, value)?;
1066+
revm_transfer(context, inputs.caller, inputs.target_address, value)?;
9981067
}
9991068
}
10001069
// Other smart contracts calls are handled by the runtime
@@ -1032,24 +1101,6 @@ impl<Runtime: ContractRuntime> CallInterceptorContract<Runtime> {
10321101
};
10331102
Ok(Some(call_outcome))
10341103
}
1035-
1036-
/// If we are using the `None` case of `fn call` and `fn create` then the transfer
1037-
/// of ethers is done by REVM. On the other hand, if we are managing the call/create
1038-
/// by hand, then we need to do the transfers ourselves.
1039-
fn revm_transfer(
1040-
context: &mut Ctx<'_, Runtime>,
1041-
source: Address,
1042-
destination: Address,
1043-
value: U256,
1044-
) -> Result<(), ExecutionError> {
1045-
// In Ethereum, all transfers matter
1046-
if let Some(error) = context.journal().transfer(source, destination, value)? {
1047-
let error = format!("{error:?}");
1048-
let error = EvmExecutionError::TransactError(error);
1049-
return Err(error.into());
1050-
}
1051-
Ok(())
1052-
}
10531104
}
10541105

10551106
struct CallInterceptorService<Runtime> {

0 commit comments

Comments
 (0)