Skip to content

Commit f063a78

Browse files
IGI-111vaivaswathaxunilrj
authored
Expose external code loading with std::execution::run_external (#5685)
## Description Fixes #1846 Fixes #5691 ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: Vaivaswatha Nagaraj <vaivaswatha.nagaraj@fuel.sh> Co-authored-by: xunilrj <xunilrj@hotmail.com>
1 parent 290a41e commit f063a78

File tree

36 files changed

+304
-75
lines changed

36 files changed

+304
-75
lines changed

docs/book/src/blockchain-development/calling_contracts.md

+16
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,19 @@ impl ContractB for Contract {
191191
fn fallback() {
192192
}
193193
```
194+
195+
You may still access the method selector and arguments to the call in the fallback.
196+
For instance, let's assume a function `fn foobar(bool, u64) {}` gets called on a contract that doesn't have it with arguments `true` and `42`.
197+
It can execute the following fallback:
198+
199+
```sway
200+
#[fallback]
201+
fn fallback() {
202+
// the method selector is the first four bytes of sha256("foobar(bool,u64)")
203+
// per https://fuellabs.github.io/fuel-specs/master/protocol/abi#function-selector-encoding
204+
let method_selector = std::call_frames::first_param::<u64>();
205+
206+
// the arguments tuple is (true, 42)
207+
let arguments = std::call_frames::second_param::<(bool, u64)>();
208+
}
209+
```

sway-core/src/asm_generation/programs/abstract.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -106,27 +106,29 @@ impl AbstractProgram {
106106
/// Right now, it looks like this:
107107
///
108108
/// WORD OP
109-
/// 1 JI program_start
110-
/// - NOOP
109+
/// 1 MOV $scratch $pc
110+
/// - JMPF $zero i2
111111
/// 2 DATA_START (0-32) (in bytes, offset from $is)
112112
/// - DATA_START (32-64)
113-
/// 3 LW $ds $is 1 (where 1 is in words and $is is a byte address to base off of)
114-
/// - ADD $ds $ds $is
113+
/// 3 LW $ds $scratch 1
114+
/// - ADD $ds $ds $scratch
115115
/// 4 .program_start:
116116
fn build_preamble(&mut self) -> AllocatedAbstractInstructionSet {
117117
let label = self.reg_seqr.get_label();
118118
AllocatedAbstractInstructionSet {
119119
ops: [
120-
// word 1
121120
AllocatedAbstractOp {
122-
opcode: Either::Right(ControlFlowOp::Jump(label)),
121+
opcode: Either::Left(AllocatedOpcode::MOVE(
122+
AllocatedRegister::Constant(ConstantRegister::Scratch),
123+
AllocatedRegister::Constant(ConstantRegister::ProgramCounter),
124+
)),
123125
comment: String::new(),
124126
owning_span: None,
125127
},
126128
// word 1.5
127129
AllocatedAbstractOp {
128-
opcode: Either::Left(AllocatedOpcode::NOOP),
129-
comment: "".into(),
130+
opcode: Either::Right(ControlFlowOp::Jump(label)),
131+
comment: String::new(),
130132
owning_span: None,
131133
},
132134
// word 2 -- full word u64 placeholder
@@ -142,7 +144,11 @@ impl AbstractProgram {
142144
},
143145
// word 3 -- load the data offset into $ds
144146
AllocatedAbstractOp {
145-
opcode: Either::Left(AllocatedOpcode::DataSectionRegisterLoadPlaceholder),
147+
opcode: Either::Left(AllocatedOpcode::LW(
148+
AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
149+
AllocatedRegister::Constant(ConstantRegister::Scratch),
150+
VirtualImmediate12::new_unchecked(1, "1 doesn't fit in 12 bits"),
151+
)),
146152
comment: "".into(),
147153
owning_span: None,
148154
},
@@ -151,7 +157,7 @@ impl AbstractProgram {
151157
opcode: Either::Left(AllocatedOpcode::ADD(
152158
AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
153159
AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
154-
AllocatedRegister::Constant(ConstantRegister::InstructionStart),
160+
AllocatedRegister::Constant(ConstantRegister::Scratch),
155161
)),
156162
comment: "".into(),
157163
owning_span: None,

sway-core/src/asm_lang/allocated_ops.rs

-11
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,6 @@ pub(crate) enum AllocatedOpcode {
240240
/* Non-VM Instructions */
241241
BLOB(VirtualImmediate24),
242242
DataSectionOffsetPlaceholder,
243-
DataSectionRegisterLoadPlaceholder,
244243
LoadDataId(AllocatedRegister, DataId),
245244
Undefined,
246245
}
@@ -360,9 +359,6 @@ impl AllocatedOpcode {
360359
/* Non-VM Instructions */
361360
BLOB(_imm) => vec![],
362361
DataSectionOffsetPlaceholder => vec![],
363-
DataSectionRegisterLoadPlaceholder => vec![&AllocatedRegister::Constant(
364-
ConstantRegister::DataSectionStart,
365-
)],
366362
LoadDataId(r1, _i) => vec![r1],
367363
Undefined => vec![],
368364
})
@@ -491,7 +487,6 @@ impl fmt::Display for AllocatedOpcode {
491487
"DATA_SECTION_OFFSET[0..32]\nDATA_SECTION_OFFSET[32..64]"
492488
)
493489
}
494-
DataSectionRegisterLoadPlaceholder => write!(fmtr, "lw $ds $is 1"),
495490
LoadDataId(a, b) => write!(fmtr, "load {a} {b}"),
496491
Undefined => write!(fmtr, "undefined op"),
497492
}
@@ -681,12 +676,6 @@ impl AllocatedOp {
681676
DataSectionOffsetPlaceholder => {
682677
return Either::Right(offset_to_data_section.to_be_bytes())
683678
}
684-
DataSectionRegisterLoadPlaceholder => op::LW::new(
685-
fuel_asm::RegId::new(DATA_SECTION_REGISTER),
686-
ConstantRegister::InstructionStart.to_reg_id(),
687-
1.into(),
688-
)
689-
.into(),
690679
LoadDataId(a, b) => {
691680
return Either::Left(realize_load(a, b, data_section, offset_to_data_section))
692681
}

sway-core/src/asm_lang/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1104,9 +1104,6 @@ impl fmt::Display for VirtualOp {
11041104
/* Non-VM Instructions */
11051105
BLOB(a) => write!(fmtr, "blob {a}"),
11061106
DataSectionOffsetPlaceholder => write!(fmtr, "data section offset placeholder"),
1107-
DataSectionRegisterLoadPlaceholder => {
1108-
write!(fmtr, "data section register load placeholder")
1109-
}
11101107
LoadDataId(a, b) => write!(fmtr, "load {a} {b}"),
11111108
Undefined => write!(fmtr, "undefined op"),
11121109
}

sway-core/src/asm_lang/virtual_ops.rs

-17
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ pub(crate) enum VirtualOp {
198198
/* Non-VM Instructions */
199199
BLOB(VirtualImmediate24),
200200
DataSectionOffsetPlaceholder,
201-
DataSectionRegisterLoadPlaceholder,
202201
// LoadDataId takes a virtual register and a DataId, which points to a labeled piece
203202
// of data in the data section. Note that the ASM op corresponding to a LW is
204203
// subtly complex: $rB is in bytes and points to some mem address. The immediate
@@ -314,10 +313,6 @@ impl VirtualOp {
314313
/* Non-VM Instructions */
315314
BLOB(_imm) => vec![],
316315
DataSectionOffsetPlaceholder => vec![],
317-
DataSectionRegisterLoadPlaceholder => vec![
318-
&VirtualRegister::Constant(ConstantRegister::DataSectionStart),
319-
&VirtualRegister::Constant(ConstantRegister::InstructionStart),
320-
],
321316
LoadDataId(r1, _i) => vec![r1],
322317
Undefined => vec![],
323318
})
@@ -429,7 +424,6 @@ impl VirtualOp {
429424
// Virtual OPs
430425
| BLOB(_)
431426
| DataSectionOffsetPlaceholder
432-
| DataSectionRegisterLoadPlaceholder
433427
| Undefined => true
434428
}
435429
}
@@ -532,7 +526,6 @@ impl VirtualOp {
532526
| GTF(_, _, _)
533527
| BLOB(_)
534528
| DataSectionOffsetPlaceholder
535-
| DataSectionRegisterLoadPlaceholder
536529
| LoadDataId(_, _)
537530
| Undefined => vec![],
538531
})
@@ -646,9 +639,6 @@ impl VirtualOp {
646639
/* Non-VM Instructions */
647640
BLOB(_imm) => vec![],
648641
DataSectionOffsetPlaceholder => vec![],
649-
DataSectionRegisterLoadPlaceholder => vec![&VirtualRegister::Constant(
650-
ConstantRegister::InstructionStart,
651-
)],
652642
LoadDataId(_r1, _i) => vec![],
653643
Undefined => vec![],
654644
})
@@ -765,9 +755,6 @@ impl VirtualOp {
765755
BLOB(_imm) => vec![],
766756
LoadDataId(r1, _i) => vec![r1],
767757
DataSectionOffsetPlaceholder => vec![],
768-
DataSectionRegisterLoadPlaceholder => vec![&VirtualRegister::Constant(
769-
ConstantRegister::DataSectionStart,
770-
)],
771758
Undefined => vec![],
772759
})
773760
.into_iter()
@@ -1192,7 +1179,6 @@ impl VirtualOp {
11921179
/* Non-VM Instructions */
11931180
BLOB(i) => Self::BLOB(i.clone()),
11941181
DataSectionOffsetPlaceholder => Self::DataSectionOffsetPlaceholder,
1195-
DataSectionRegisterLoadPlaceholder => Self::DataSectionRegisterLoadPlaceholder,
11961182
LoadDataId(r1, i) => Self::LoadDataId(update_reg(reg_to_reg_map, r1), i.clone()),
11971183
Undefined => Self::Undefined,
11981184
}
@@ -1649,9 +1635,6 @@ impl VirtualOp {
16491635
/* Non-VM Instructions */
16501636
BLOB(imm) => AllocatedOpcode::BLOB(imm.clone()),
16511637
DataSectionOffsetPlaceholder => AllocatedOpcode::DataSectionOffsetPlaceholder,
1652-
DataSectionRegisterLoadPlaceholder => {
1653-
AllocatedOpcode::DataSectionRegisterLoadPlaceholder
1654-
}
16551638
LoadDataId(reg1, label) => {
16561639
AllocatedOpcode::LoadDataId(map_reg(&mapping, reg1), label.clone())
16571640
}

sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,18 @@ where
287287
_ => todo!(),
288288
};
289289

290-
assert!(!handler.has_errors(), "{:?} {}", handler, code);
290+
if handler.has_errors() {
291+
panic!(
292+
"{:?} {:?}",
293+
handler,
294+
module_id
295+
.and_then(|x| engines.se().get_source_ids_from_module_id(x))
296+
.unwrap()
297+
.iter()
298+
.map(|x| engines.se().get_file_name(x))
299+
.collect::<Vec<_>>()
300+
);
301+
}
291302
assert!(!handler.has_warnings(), "{:?}", handler);
292303

293304
let ctx = self.ctx.by_ref();
@@ -298,7 +309,19 @@ where
298309
)
299310
.unwrap();
300311

301-
assert!(!handler.has_errors(), "{:?} {}", handler, code);
312+
if handler.has_errors() {
313+
panic!(
314+
"{:?} {} {:?}",
315+
handler,
316+
code,
317+
module_id
318+
.and_then(|x| engines.se().get_source_ids_from_module_id(x))
319+
.unwrap()
320+
.iter()
321+
.map(|x| engines.se().get_path(x))
322+
.collect::<Vec<_>>()
323+
);
324+
}
302325
assert!(!handler.has_warnings(), "{:?}", handler);
303326

304327
Some(TyAstNode {

sway-lib-core/src/codec.sw

+17
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,23 @@ where
12841284
}
12851285
}
12861286

1287+
impl<T> AbiEncode for [T; 9]
1288+
where
1289+
T: AbiEncode,
1290+
{
1291+
fn abi_encode(self, ref mut buffer: Buffer) {
1292+
self[0].abi_encode(buffer);
1293+
self[1].abi_encode(buffer);
1294+
self[2].abi_encode(buffer);
1295+
self[3].abi_encode(buffer);
1296+
self[4].abi_encode(buffer);
1297+
self[5].abi_encode(buffer);
1298+
self[6].abi_encode(buffer);
1299+
self[7].abi_encode(buffer);
1300+
self[8].abi_encode(buffer);
1301+
}
1302+
}
1303+
12871304
// Encode Tuples
12881305

12891306

sway-lib-std/src/execution.sw

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
library;
2+
3+
use ::contract_id::ContractId;
4+
5+
/// Load and run the contract with the provided `ContractId`.
6+
///
7+
/// Contract code will be loaded using `LDC` and jumped into.
8+
/// Unlike a normal contract call, the context of the contract running
9+
/// `run_external` is retained for the loaded code.
10+
///
11+
/// As this function never returns to the original code that calls it, it returns `!`.
12+
#[inline(never)]
13+
pub fn run_external(load_target: ContractId) -> ! {
14+
asm(
15+
load_target: load_target,
16+
word,
17+
length,
18+
ssp_saved,
19+
cur_stack_size,
20+
) {
21+
csiz length load_target;
22+
move ssp_saved ssp;
23+
sub cur_stack_size sp ssp;
24+
cfs cur_stack_size;
25+
ldc load_target zero length;
26+
addi word zero i64;
27+
aloc word;
28+
sw hp ssp_saved i0;
29+
}
30+
__jmp_mem()
31+
}

sway-lib-std/src/lib.sw

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod alias;
2121
pub mod hash;
2222
pub mod asset_id;
2323
pub mod contract_id;
24+
pub mod execution;
2425
pub mod constants;
2526
pub mod call_frames;
2627
pub mod context;

sway-types/src/source_engine.rs

+5
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,9 @@ impl SourceEngine {
123123
v.sort();
124124
v
125125
}
126+
127+
pub fn get_source_ids_from_module_id(&self, module_id: ModuleId) -> Option<BTreeSet<SourceId>> {
128+
let s = self.module_to_sources_map.read().unwrap();
129+
s.get(&module_id).cloned()
130+
}
126131
}

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/array_of_structs_caller/src/main.sw

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use array_of_structs_abi::{Id, TestContract, Wrapper};
44
use std::hash::*;
55

66
#[cfg(experimental_new_encoding = false)]
7-
const CONTRACT_ID = 0xe2a4f86301f8b57ff2c93ce68366669fc2f0926dccd26f9f6550b049cb324a2c;
7+
const CONTRACT_ID = 0x7fae96947a8cad59cc2a25239f9f80897955d4c1b10d31510681f15842b93265;
88
#[cfg(experimental_new_encoding = true)]
9-
const CONTRACT_ID = 0xec759cace1887b3d0a7c38305cb72bef4a6799e0c503f04af587861603bb985f;
9+
const CONTRACT_ID = 0x53de34b4a064de1a09fb9eb8ed407fd2c3b205c27ad5d8e0c63b28c752a09bbc;
1010

1111
fn main() -> u64 {
1212
let addr = abi(TestContract, CONTRACT_ID);

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ use std::constants::DEFAULT_SUB_ID;
77
use test_fuel_coin_abi::*;
88

99
#[cfg(experimental_new_encoding = false)]
10-
const FUEL_COIN_CONTRACT_ID = 0x542c6e67e5e8768a2c119a80ddcbd1f8d01110ced16fda37e4aa77ebb6d6cdb9;
10+
const FUEL_COIN_CONTRACT_ID = 0x27447d931b1c2c0eaf94aa9ffd1c1ea09298ee23a632937accdac91947a502a0;
1111
#[cfg(experimental_new_encoding = true)]
12-
const FUEL_COIN_CONTRACT_ID = 0x8d3f7e1c4ae4c23e267f3d4b8f389a8d9e3753ab7adf455224062e49f69886f4;
12+
const FUEL_COIN_CONTRACT_ID = 0x32dcf3d874415397505a12b60c85c63f4f70eb36a33f984ee81fd8214d4faeeb;
1313

1414
#[cfg(experimental_new_encoding = false)]
15-
const BALANCE_CONTRACT_ID = 0xe50966cd6b1da8fe006e3e876e08f3df6948ce426e1a7cfe49fba411b0a11f89;
15+
const BALANCE_CONTRACT_ID = 0x3b8cb681056f61a41e138b8884d7e3bb9332fbd7a8e38e3e0b0ada766cabfa4e;
1616
#[cfg(experimental_new_encoding = true)]
17-
const BALANCE_CONTRACT_ID = 0xb0b0589ced70b31fb34cbb7fbb1b0e4046cc61c2ffe79cdb06a617bf24d9458c;
17+
const BALANCE_CONTRACT_ID = 0x2d15bdaa30e59eb25bab934e9533d10ace0a971ae942e47119e49ef411978d34;
1818

1919
fn main() -> bool {
2020
let default_gas = 1_000_000_000_000;

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/bal_opcode/src/main.sw

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use std::constants::BASE_ASSET_ID;
44
use balance_test_abi::BalanceTest;
55

66
#[cfg(experimental_new_encoding = false)]
7-
const CONTRACT_ID = 0xe50966cd6b1da8fe006e3e876e08f3df6948ce426e1a7cfe49fba411b0a11f89;
7+
const CONTRACT_ID = 0x3b8cb681056f61a41e138b8884d7e3bb9332fbd7a8e38e3e0b0ada766cabfa4e;
88
#[cfg(experimental_new_encoding = true)]
9-
const CONTRACT_ID = 0xb0b0589ced70b31fb34cbb7fbb1b0e4046cc61c2ffe79cdb06a617bf24d9458c;
9+
const CONTRACT_ID = 0x2d15bdaa30e59eb25bab934e9533d10ace0a971ae942e47119e49ef411978d34;
1010

1111
fn main() -> bool {
1212
let balance_test_contract = abi(BalanceTest, CONTRACT_ID);

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_abi_with_tuples/src/main.sw

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ script;
33
use abi_with_tuples::*;
44

55
#[cfg(experimental_new_encoding = false)]
6-
const CONTRACT_ID = 0x1200d031e9c10f8d9bd9dd556a98a0c88e74a4da991047556f78b1bcc1be2ab6;
6+
const CONTRACT_ID = 0xb351aff8258ce46d16a71be666dd2b0b09d72243105c51f4423765824e59cac9;
77
#[cfg(experimental_new_encoding = true)]
8-
const CONTRACT_ID = 0x5de3f05e6429ea4b14d51b4797ba3f02a11cd0b546853d339f7b5ff13991c700;
8+
const CONTRACT_ID = 0xde6ab165b5b0f9058daf26002d22f472e6d1af1c35e0210821e743d297d55b17;
99

1010
fn main() -> bool {
1111
let the_abi = abi(MyContract, CONTRACT_ID);

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ script;
22
use basic_storage_abi::{BasicStorage, Quad};
33

44
#[cfg(experimental_new_encoding = false)]
5-
const CONTRACT_ID = 0xd956f6bb7ee577561325f16f51534c001061342972a0bef9c2dcfc6d83919491;
5+
const CONTRACT_ID = 0xa75e1629f14cf3fa28c6fc442d2a2470cbeb966ee53574935ee4fe28bd24dcb1;
66
#[cfg(experimental_new_encoding = true)]
7-
const CONTRACT_ID = 0x4b4ffef69317dc68b6662b61819b219a2468ccb5b9785e580721eb5fe0447ae2;
7+
const CONTRACT_ID = 0x089ad9c09f74d319c4dd17ce8ac1ee2d41d7bb348831eb0b7438f09f82da4204;
88

99
fn main() -> u64 {
1010
let addr = abi(BasicStorage, CONTRACT_ID);

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_contract_with_type_aliases/src/main.sw

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ script;
33
use contract_with_type_aliases_abi::*;
44

55
#[cfg(experimental_new_encoding = false)]
6-
const CONTRACT_ID = 0xbd74e82536dd497dc73b8810ed5750b2b3b5b97a08d31e89b4135cb5360d447d;
6+
const CONTRACT_ID = 0x9d76ecbf446c30ef659efd1157d67d156de02b1e6c2ac2f9c744125571efa229;
77
#[cfg(experimental_new_encoding = true)]
8-
const CONTRACT_ID = 0xc577c27bda232264a316a51103d0d76ca4e285a2c049366b70dee5250b2177a1;
8+
const CONTRACT_ID = 0xdf7ff45bea6a49aa2b6ba9f12eac13dafa349b9c57cd8f7d4892bb0ebcc3782a;
99

1010
fn main() {
1111
let caller = abi(MyContract, CONTRACT_ID);

0 commit comments

Comments
 (0)