Skip to content

Commit e97b8e4

Browse files
authored
EVM: call tests (#1183)
* test call happy path * test call alternative paths * test delegatecall * test staticcall * test invalid * rustfmt * add correctness check sled to test return/revert * clippy
1 parent 9b1f667 commit e97b8e4

File tree

2 files changed

+365
-0
lines changed

2 files changed

+365
-0
lines changed

actors/evm/src/interpreter/instructions/call.rs

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,16 @@ fn effective_gas_limit<RT: Runtime>(system: &System<RT>, gas: U256) -> u64 {
341341
#[cfg(test)]
342342
mod tests {
343343
use crate::evm_unit_test;
344+
use cid::Cid;
344345
use fil_actors_evm_shared::uints::U256;
346+
use fil_actors_runtime::test_utils::EVM_ACTOR_CODE_ID;
347+
use fvm_ipld_blockstore::Blockstore;
348+
use fvm_ipld_encoding::ipld_block::IpldBlock;
349+
use fvm_ipld_encoding::IPLD_RAW;
350+
use fvm_shared::address::Address as FilAddress;
351+
use fvm_shared::error::{ErrorNumber, ExitCode};
352+
use fvm_shared::sys::SendFlags;
353+
use num_traits::Zero;
345354

346355
#[test]
347356
fn test_calldataload() {
@@ -540,4 +549,304 @@ mod tests {
540549
assert_eq!(m.state.memory[0..3], m.bytecode[1..4]);
541550
};
542551
}
552+
553+
#[test]
554+
fn test_call() {
555+
let dest = EthAddress::from_id(1001);
556+
let fil_dest = FilAddress::new_id(1001);
557+
let input_data = vec![0x01, 0x02, 0x03, 0x04];
558+
let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE];
559+
evm_unit_test! {
560+
(rt) {
561+
rt.in_call = true;
562+
rt.expect_send(
563+
fil_dest,
564+
crate::Method::InvokeContract as u64,
565+
Some(IpldBlock { codec: IPLD_RAW, data: input_data }),
566+
TokenAmount::zero(),
567+
Some(1_000_000_000),
568+
SendFlags::empty(),
569+
Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }),
570+
ExitCode::OK,
571+
None,
572+
);
573+
rt.expect_gas_available(10_000_000_000);
574+
}
575+
(m) {
576+
// input data
577+
PUSH4; 0x01; 0x02; 0x03; 0x04;
578+
PUSH0;
579+
MSTORE;
580+
// the call
581+
CALL;
582+
}
583+
m.state.stack.push(U256::from(4)).unwrap(); // output size
584+
m.state.stack.push(U256::from(0)).unwrap(); // output offset
585+
m.state.stack.push(U256::from(4)).unwrap(); // input size
586+
m.state.stack.push(U256::from(28)).unwrap(); // input offset
587+
m.state.stack.push(U256::from(0)).unwrap(); // value
588+
m.state.stack.push(dest.as_evm_word()).unwrap(); // dest
589+
m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas
590+
for _ in 0..4 {
591+
m.step().expect("execution step failed");
592+
}
593+
assert_eq!(m.state.stack.len(), 1);
594+
assert_eq!(m.state.stack.pop().unwrap(), U256::from(1));
595+
assert_eq!(&m.state.return_data, &output_data);
596+
assert_eq!(&m.state.memory[0..4], &output_data);
597+
};
598+
}
599+
600+
#[test]
601+
fn test_call_revert() {
602+
let dest = EthAddress::from_id(1001);
603+
let fil_dest = FilAddress::new_id(1001);
604+
let input_data = vec![0x01, 0x02, 0x03, 0x04];
605+
let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE];
606+
evm_unit_test! {
607+
(rt) {
608+
rt.in_call = true;
609+
rt.expect_send(
610+
fil_dest,
611+
crate::Method::InvokeContract as u64,
612+
Some(IpldBlock { codec: IPLD_RAW, data: input_data }),
613+
TokenAmount::zero(),
614+
Some(1_000_000_000),
615+
SendFlags::empty(),
616+
Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }),
617+
crate::EVM_CONTRACT_REVERTED,
618+
None,
619+
);
620+
rt.expect_gas_available(10_000_000_000);
621+
}
622+
(m) {
623+
// input data
624+
PUSH4; 0x01; 0x02; 0x03; 0x04;
625+
PUSH0;
626+
MSTORE;
627+
// the call
628+
CALL;
629+
}
630+
m.state.stack.push(U256::from(4)).unwrap(); // output size
631+
m.state.stack.push(U256::from(0)).unwrap(); // output offset
632+
m.state.stack.push(U256::from(4)).unwrap(); // input size
633+
m.state.stack.push(U256::from(28)).unwrap(); // input offset
634+
m.state.stack.push(U256::from(0)).unwrap(); // value
635+
m.state.stack.push(dest.as_evm_word()).unwrap(); // dest
636+
m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas
637+
for _ in 0..4 {
638+
m.step().expect("execution step failed");
639+
}
640+
assert_eq!(m.state.stack.len(), 1);
641+
assert_eq!(m.state.stack.pop().unwrap(), U256::from(0));
642+
assert_eq!(&m.state.return_data, &output_data);
643+
assert_eq!(&m.state.memory[0..4], &output_data);
644+
};
645+
}
646+
647+
#[test]
648+
fn test_call_err() {
649+
let dest = EthAddress::from_id(1001);
650+
let fil_dest = FilAddress::new_id(1001);
651+
let input_data = vec![0x01, 0x02, 0x03, 0x04];
652+
evm_unit_test! {
653+
(rt) {
654+
rt.in_call = true;
655+
rt.expect_send(
656+
fil_dest,
657+
crate::Method::InvokeContract as u64,
658+
Some(IpldBlock { codec: IPLD_RAW, data: input_data }),
659+
TokenAmount::zero(),
660+
Some(1_000_000_000),
661+
SendFlags::empty(),
662+
None,
663+
ExitCode::OK,
664+
Some(ErrorNumber::IllegalOperation),
665+
);
666+
rt.expect_gas_available(10_000_000_000);
667+
}
668+
(m) {
669+
// input data
670+
PUSH4; 0x01; 0x02; 0x03; 0x04;
671+
PUSH0;
672+
MSTORE;
673+
// the call
674+
CALL;
675+
}
676+
m.state.stack.push(U256::from(4)).unwrap(); // output size
677+
m.state.stack.push(U256::from(0)).unwrap(); // output offset
678+
m.state.stack.push(U256::from(4)).unwrap(); // input size
679+
m.state.stack.push(U256::from(28)).unwrap(); // input offset
680+
m.state.stack.push(U256::from(0)).unwrap(); // value
681+
m.state.stack.push(dest.as_evm_word()).unwrap(); // dest
682+
m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas
683+
for _ in 0..4 {
684+
m.step().expect("execution step failed");
685+
}
686+
assert_eq!(m.state.stack.len(), 1);
687+
assert_eq!(m.state.stack.pop().unwrap(), U256::from(0));
688+
assert_eq!(m.state.return_data.len(), 0);
689+
};
690+
}
691+
692+
#[test]
693+
fn test_call_precompile() {
694+
let mut id_bytes = [0u8; 20];
695+
id_bytes[19] = 0x04;
696+
let dest = EthAddress(id_bytes);
697+
let mut output_data = [0u8; 32];
698+
output_data[28] = 0x01;
699+
output_data[29] = 0x02;
700+
output_data[30] = 0x03;
701+
output_data[31] = 0x04;
702+
evm_unit_test! {
703+
(rt) {
704+
rt.in_call = true;
705+
rt.expect_gas_available(10_000_000_000);
706+
}
707+
(m) {
708+
// input data
709+
PUSH4; 0x01; 0x02; 0x03; 0x04;
710+
PUSH0;
711+
MSTORE;
712+
// the call
713+
CALL;
714+
}
715+
m.state.stack.push(U256::from(32)).unwrap(); // output size
716+
m.state.stack.push(U256::from(0)).unwrap(); // output offset
717+
m.state.stack.push(U256::from(32)).unwrap(); // input size
718+
m.state.stack.push(U256::from(0)).unwrap(); // input offset
719+
m.state.stack.push(U256::from(0)).unwrap(); // value
720+
m.state.stack.push(dest.as_evm_word()).unwrap(); // dest
721+
m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas
722+
for _ in 0..4 {
723+
m.step().expect("execution step failed");
724+
}
725+
assert_eq!(m.state.stack.len(), 1);
726+
assert_eq!(m.state.stack.pop().unwrap(), U256::from(1));
727+
assert_eq!(&m.state.return_data, &output_data);
728+
assert_eq!(&m.state.memory[..], &output_data);
729+
};
730+
}
731+
732+
#[test]
733+
fn test_delegatecall() {
734+
let receiver = FilAddress::new_id(1000);
735+
let dest = EthAddress::from_id(1001);
736+
let caller = EthAddress::from_id(1002);
737+
let fil_dest = FilAddress::new_id(1001);
738+
let input_data = vec![0x01, 0x02, 0x03, 0x04];
739+
let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE];
740+
evm_unit_test! {
741+
(rt) {
742+
rt.in_call = true;
743+
rt.receiver = receiver;
744+
rt.set_address_actor_type(fil_dest, *EVM_ACTOR_CODE_ID);
745+
746+
let bytecode = vec![0xFE, 0xED, 0x43, 0x33];
747+
let bytecode_cid = Cid::try_from("baeaikaia").unwrap();
748+
rt.store.put_keyed(&bytecode_cid, bytecode.as_slice()).unwrap();
749+
750+
rt.expect_send(
751+
fil_dest,
752+
crate::Method::GetBytecode as u64,
753+
Default::default(),
754+
TokenAmount::zero(),
755+
None,
756+
SendFlags::READ_ONLY,
757+
IpldBlock::serialize_cbor(&bytecode_cid).unwrap(),
758+
ExitCode::OK,
759+
None,
760+
);
761+
762+
let params = crate::DelegateCallParams {
763+
code: bytecode_cid,
764+
input: input_data,
765+
caller,
766+
value: TokenAmount::zero(),
767+
};
768+
769+
rt.expect_send(
770+
receiver,
771+
crate::Method::InvokeContractDelegate as u64,
772+
IpldBlock::serialize_dag_cbor(&params).unwrap(),
773+
TokenAmount::zero(),
774+
Some(1_000_000_000),
775+
SendFlags::empty(),
776+
Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }),
777+
ExitCode::OK,
778+
None,
779+
);
780+
rt.expect_gas_available(10_000_000_000);
781+
}
782+
(m) {
783+
// input data
784+
PUSH4; 0x01; 0x02; 0x03; 0x04;
785+
PUSH0;
786+
MSTORE;
787+
// the call
788+
DELEGATECALL;
789+
}
790+
m.state.caller = caller;
791+
m.state.stack.push(U256::from(4)).unwrap(); // output size
792+
m.state.stack.push(U256::from(0)).unwrap(); // output offset
793+
m.state.stack.push(U256::from(4)).unwrap(); // input size
794+
m.state.stack.push(U256::from(28)).unwrap(); // input offset
795+
m.state.stack.push(dest.as_evm_word()).unwrap(); // dest
796+
m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas
797+
for _ in 0..4 {
798+
m.step().expect("execution step failed");
799+
}
800+
assert_eq!(m.state.stack.len(), 1);
801+
assert_eq!(m.state.stack.pop().unwrap(), U256::from(1));
802+
assert_eq!(&m.state.return_data, &output_data);
803+
assert_eq!(&m.state.memory[0..4], &output_data);
804+
};
805+
}
806+
807+
#[test]
808+
fn test_staticcall() {
809+
let dest = EthAddress::from_id(1001);
810+
let fil_dest = FilAddress::new_id(1001);
811+
let input_data = vec![0x01, 0x02, 0x03, 0x04];
812+
let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE];
813+
evm_unit_test! {
814+
(rt) {
815+
rt.in_call = true;
816+
rt.expect_send(
817+
fil_dest,
818+
crate::Method::InvokeContract as u64,
819+
Some(IpldBlock { codec: IPLD_RAW, data: input_data }),
820+
TokenAmount::zero(),
821+
Some(1_000_000_000),
822+
SendFlags::READ_ONLY,
823+
Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }),
824+
ExitCode::OK,
825+
None,
826+
);
827+
rt.expect_gas_available(10_000_000_000);
828+
}
829+
(m) {
830+
// input data
831+
PUSH4; 0x01; 0x02; 0x03; 0x04;
832+
PUSH0;
833+
MSTORE;
834+
// the call
835+
STATICCALL;
836+
}
837+
m.state.stack.push(U256::from(4)).unwrap(); // output size
838+
m.state.stack.push(U256::from(0)).unwrap(); // output offset
839+
m.state.stack.push(U256::from(4)).unwrap(); // input size
840+
m.state.stack.push(U256::from(28)).unwrap(); // input offset
841+
m.state.stack.push(dest.as_evm_word()).unwrap(); // dest
842+
m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas
843+
for _ in 0..4 {
844+
m.step().expect("execution step failed");
845+
}
846+
assert_eq!(m.state.stack.len(), 1);
847+
assert_eq!(m.state.stack.pop().unwrap(), U256::from(1));
848+
assert_eq!(&m.state.return_data, &output_data);
849+
assert_eq!(&m.state.memory[0..4], &output_data);
850+
};
851+
}
543852
}

actors/evm/src/interpreter/instructions/control.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,4 +396,60 @@ mod tests {
396396
assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS);
397397
};
398398
}
399+
400+
#[test]
401+
fn test_return() {
402+
evm_unit_test! {
403+
(m) {
404+
// output data
405+
PUSH4; 0x01; 0x02; 0x03; 0x04;
406+
PUSH0;
407+
MSTORE;
408+
// exit
409+
RETURN;
410+
// correctness check sled
411+
INVALID;
412+
}
413+
m.state.stack.push(U256::from(4)).unwrap(); // length
414+
m.state.stack.push(U256::from(28)).unwrap(); // offset
415+
let result = m.execute().expect("execution failed");
416+
assert_eq!(result.outcome, crate::Outcome::Return);
417+
let expected_data = vec![0x01, 0x02, 0x03, 0x04];
418+
assert_eq!(&result.return_data, &expected_data);
419+
};
420+
}
421+
422+
#[test]
423+
fn test_revert() {
424+
evm_unit_test! {
425+
(m) {
426+
// output data
427+
PUSH4; 0x01; 0x02; 0x03; 0x04;
428+
PUSH0;
429+
MSTORE;
430+
// exit
431+
REVERT;
432+
// correctness check sled
433+
INVALID;
434+
}
435+
m.state.stack.push(U256::from(4)).unwrap(); // length
436+
m.state.stack.push(U256::from(28)).unwrap(); // offset
437+
let result = m.execute().expect("execution failed");
438+
assert_eq!(result.outcome, crate::Outcome::Revert);
439+
let expected_data = vec![0x01, 0x02, 0x03, 0x04];
440+
assert_eq!(&result.return_data, &expected_data);
441+
};
442+
}
443+
444+
#[test]
445+
fn test_invalid() {
446+
evm_unit_test! {
447+
(m) {
448+
INVALID;
449+
}
450+
let result = m.step();
451+
assert!(result.is_err(), "execution succeeded");
452+
assert_eq!(result.err().unwrap().exit_code(), crate::EVM_CONTRACT_INVALID_INSTRUCTION);
453+
};
454+
}
399455
}

0 commit comments

Comments
 (0)