Skip to content

Commit fbb3613

Browse files
anorthshamb0
authored andcommitted
Proof of concept exported API for Account actor (filecoin-project#797)
1 parent f678790 commit fbb3613

File tree

5 files changed

+109
-113
lines changed

5 files changed

+109
-113
lines changed

actors/account/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use num_traits::FromPrimitive;
1313
use crate::types::AuthenticateMessageParams;
1414
use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR;
1515
use fil_actors_runtime::runtime::{ActorCode, Runtime};
16-
use fil_actors_runtime::{actor_error, ActorError};
16+
use fil_actors_runtime::{actor_error, restrict_internal_api, ActorError};
1717
use fil_actors_runtime::{cbor, ActorDowncast};
1818

1919
pub use self::state::State;
@@ -32,6 +32,7 @@ pub enum Method {
3232
Constructor = METHOD_CONSTRUCTOR,
3333
PubkeyAddress = 2,
3434
AuthenticateMessage = 3,
35+
AuthenticateMessageExported = frc42_dispatch::method_hash!("AuthenticateMessage"),
3536
UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"),
3637
}
3738

@@ -108,6 +109,8 @@ impl ActorCode for Actor {
108109
where
109110
RT: Runtime,
110111
{
112+
restrict_internal_api(rt, method)?;
113+
111114
match FromPrimitive::from_u64(method) {
112115
Some(Method::Constructor) => {
113116
Self::constructor(rt, cbor::deserialize_params(params)?)?;
@@ -117,7 +120,7 @@ impl ActorCode for Actor {
117120
let addr = Self::pubkey_address(rt)?;
118121
Ok(RawBytes::serialize(addr)?)
119122
}
120-
Some(Method::AuthenticateMessage) => {
123+
Some(Method::AuthenticateMessage) | Some(Method::AuthenticateMessageExported) => {
121124
Self::authenticate_message(rt, cbor::deserialize_params(params)?)?;
122125
Ok(RawBytes::default())
123126
}

actors/account/tests/account_actor_test.rs

Lines changed: 45 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,9 @@ use fil_actors_runtime::test_utils::*;
1919
#[test]
2020
fn construction() {
2121
fn construct(addr: Address, exit_code: ExitCode) {
22-
let mut rt = MockRuntime {
23-
receiver: Address::new_id(100),
24-
caller: *SYSTEM_ACTOR_ADDR,
25-
caller_type: *SYSTEM_ACTOR_CODE_ID,
26-
..Default::default()
27-
};
28-
rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]);
22+
let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() };
23+
rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR);
24+
rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]);
2925

3026
if exit_code.is_success() {
3127
rt.call::<AccountActor>(
@@ -62,13 +58,9 @@ fn construction() {
6258

6359
#[test]
6460
fn token_receiver() {
65-
let mut rt = MockRuntime {
66-
receiver: Address::new_id(100),
67-
caller: *SYSTEM_ACTOR_ADDR,
68-
caller_type: *SYSTEM_ACTOR_CODE_ID,
69-
..Default::default()
70-
};
71-
rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]);
61+
let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() };
62+
rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR);
63+
rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]);
7264

7365
let param = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap();
7466
rt.call::<AccountActor>(
@@ -77,6 +69,7 @@ fn token_receiver() {
7769
)
7870
.unwrap();
7971

72+
rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000));
8073
rt.expect_validate_caller_any();
8174
let ret = rt.call::<AccountActor>(
8275
Method::FungibleTokenReceiverHook as MethodNum,
@@ -86,86 +79,15 @@ fn token_receiver() {
8679
assert_eq!(RawBytes::default(), ret.unwrap());
8780
}
8881

89-
fn check_state(rt: &MockRuntime) {
90-
let test_address = Address::new_id(1000);
91-
let (_, acc) = check_state_invariants(&rt.get_state(), &test_address);
92-
acc.assert_empty();
93-
}
94-
95-
macro_rules! account_constructor_tests {
96-
($($name:ident: $value:expr,)*) => {
97-
$(
98-
#[test]
99-
fn $name() {
100-
let (addr, exit_code) = $value;
101-
102-
let mut rt = MockRuntime {
103-
receiver: fvm_shared::address::Address::new_id(100),
104-
caller: SYSTEM_ACTOR_ADDR.clone(),
105-
caller_type: SYSTEM_ACTOR_CODE_ID.clone(),
106-
..Default::default()
107-
};
108-
rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]);
109-
110-
if exit_code.is_success() {
111-
rt.call::<AccountActor>(1, &RawBytes::serialize(addr).unwrap()).unwrap();
112-
113-
let state: State = rt.get_state();
114-
assert_eq!(state.address, addr);
115-
rt.expect_validate_caller_any();
116-
117-
let pk: Address = rt
118-
.call::<AccountActor>(2, &RawBytes::default())
119-
.unwrap()
120-
.deserialize()
121-
.unwrap();
122-
assert_eq!(pk, addr);
123-
124-
check_state(&rt);
125-
} else {
126-
expect_abort(
127-
exit_code,
128-
rt.call::<AccountActor>(1,&RawBytes::serialize(addr).unwrap())
129-
)
130-
}
131-
rt.verify();
132-
}
133-
)*
134-
}
135-
}
136-
137-
account_constructor_tests! {
138-
happy_construct_secp256k1_address: (
139-
Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(),
140-
ExitCode::OK
141-
),
142-
happy_construct_bls_address: (
143-
Address::new_bls(&[1; fvm_shared::address::BLS_PUB_LEN]).unwrap(),
144-
ExitCode::OK
145-
),
146-
fail_construct_id_address: (
147-
Address::new_id(1),
148-
ExitCode::USR_ILLEGAL_ARGUMENT
149-
),
150-
fail_construct_actor_address: (
151-
Address::new_actor(&[1, 2, 3]),
152-
ExitCode::USR_ILLEGAL_ARGUMENT
153-
),
154-
}
155-
15682
#[test]
15783
fn authenticate_message() {
158-
let mut rt = MockRuntime {
159-
receiver: Address::new_id(100),
160-
caller: SYSTEM_ACTOR_ADDR,
161-
caller_type: *SYSTEM_ACTOR_CODE_ID,
162-
..Default::default()
163-
};
84+
let mut rt = MockRuntime { receiver: Address::new_id(100), ..Default::default() };
85+
rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR);
16486

16587
let addr = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap();
16688
rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]);
167-
168-
rt.call::<AccountActor>(1, &RawBytes::serialize(addr).unwrap()).unwrap();
89+
rt.call::<AccountActor>(Method::Constructor as MethodNum, &RawBytes::serialize(addr).unwrap())
90+
.unwrap();
16991

17092
let state: State = rt.get_state();
17193
assert_eq!(state.address, addr);
@@ -174,26 +96,56 @@ fn authenticate_message() {
17496
RawBytes::serialize(AuthenticateMessageParams { signature: vec![], message: vec![] })
17597
.unwrap();
17698

99+
// Valid signature
177100
rt.expect_validate_caller_any();
178101
rt.expect_verify_signature(ExpectedVerifySig {
179102
sig: Signature::new_secp256k1(vec![]),
180103
signer: addr,
181104
plaintext: vec![],
182105
result: Ok(()),
183106
});
184-
assert_eq!(RawBytes::default(), rt.call::<AccountActor>(3, &params).unwrap());
107+
assert_eq!(
108+
RawBytes::default(),
109+
rt.call::<AccountActor>(Method::AuthenticateMessage as MethodNum, &params).unwrap()
110+
);
111+
rt.verify();
185112

113+
// Invalid signature
186114
rt.expect_validate_caller_any();
187115
rt.expect_verify_signature(ExpectedVerifySig {
188116
sig: Signature::new_secp256k1(vec![]),
189117
signer: addr,
190118
plaintext: vec![],
191119
result: Err(anyhow!("bad signature")),
192120
});
193-
assert_eq!(
121+
expect_abort_contains_message(
194122
ExitCode::USR_ILLEGAL_ARGUMENT,
195-
rt.call::<AccountActor>(3, &params).unwrap_err().exit_code()
123+
"bad signature",
124+
rt.call::<AccountActor>(Method::AuthenticateMessage as MethodNum, &params),
196125
);
197-
198126
rt.verify();
127+
128+
// Invalid caller of internal method number
129+
rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000));
130+
expect_abort_contains_message(
131+
ExitCode::USR_FORBIDDEN,
132+
"must be built-in",
133+
rt.call::<AccountActor>(Method::AuthenticateMessage as MethodNum, &params),
134+
);
135+
136+
// Ok to call exported method number
137+
rt.expect_validate_caller_any();
138+
rt.expect_verify_signature(ExpectedVerifySig {
139+
sig: Signature::new_secp256k1(vec![]),
140+
signer: addr,
141+
plaintext: vec![],
142+
result: Ok(()),
143+
});
144+
rt.call::<AccountActor>(Method::AuthenticateMessageExported as MethodNum, &params).unwrap();
145+
}
146+
147+
fn check_state(rt: &MockRuntime) {
148+
let test_address = Address::new_id(1000);
149+
let (_, acc) = check_state_invariants(&rt.get_state(), &test_address);
150+
acc.assert_empty();
199151
}

runtime/src/builtin/shared.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
use crate::{actor_error, ActorContext, ActorError};
55
use fvm_shared::address::Address;
6-
use fvm_shared::ActorID;
76
use fvm_shared::METHOD_SEND;
7+
use fvm_shared::{ActorID, MethodNum};
88

99
use crate::runtime::builtins::Type;
1010
use crate::runtime::Runtime;
@@ -38,3 +38,37 @@ pub fn resolve_to_actor_id(
3838

3939
Err(actor_error!(illegal_argument, "failed to resolve or initialize address {}", address))
4040
}
41+
42+
// The lowest FRC-42 method number.
43+
pub const FIRST_EXPORTED_METHOD_NUMBER: MethodNum = 1 << 24;
44+
45+
// Checks whether the caller is allowed to invoke some method number.
46+
// All method numbers below the FRC-42 range are restricted to built-in actors
47+
// (including the account and multisig actors).
48+
// Methods may subsequently enforce tighter restrictions.
49+
pub fn restrict_internal_api<RT>(rt: &mut RT, method: MethodNum) -> Result<(), ActorError>
50+
where
51+
RT: Runtime,
52+
{
53+
if method >= FIRST_EXPORTED_METHOD_NUMBER {
54+
return Ok(());
55+
}
56+
let caller = rt.message().caller();
57+
let code_cid = rt.get_actor_code_cid(&caller.id().unwrap());
58+
match code_cid {
59+
None => {
60+
return Err(
61+
actor_error!(forbidden; "no code for caller {} of method {}", caller, method),
62+
)
63+
}
64+
Some(code_cid) => {
65+
let builtin_type = rt.resolve_builtin_actor_type(&code_cid);
66+
if builtin_type.is_none() {
67+
return Err(
68+
actor_error!(forbidden; "caller {} of method {} must be built-in", caller, method),
69+
);
70+
}
71+
}
72+
}
73+
Ok(())
74+
}

runtime/src/test_utils.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,18 @@ use crate::runtime::{
4242
use crate::{actor_error, ActorError};
4343

4444
lazy_static::lazy_static! {
45-
pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/system");
46-
pub static ref INIT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/init");
47-
pub static ref CRON_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/cron");
48-
pub static ref ACCOUNT_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/account");
49-
pub static ref POWER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storagepower");
50-
pub static ref MINER_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storageminer");
51-
pub static ref MARKET_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/storagemarket");
52-
pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/paymentchannel");
53-
pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/multisig");
54-
pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/reward");
55-
pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/verifiedregistry");
56-
pub static ref DATACAP_TOKEN_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/datacap");
45+
pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/system");
46+
pub static ref INIT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/init");
47+
pub static ref CRON_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/cron");
48+
pub static ref ACCOUNT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/account");
49+
pub static ref POWER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storagepower");
50+
pub static ref MINER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storageminer");
51+
pub static ref MARKET_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/storagemarket");
52+
pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/paymentchannel");
53+
pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/multisig");
54+
pub static ref REWARD_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/reward");
55+
pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/verifiedregistry");
56+
pub static ref DATACAP_TOKEN_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/datacap");
5757
pub static ref ACTOR_TYPES: BTreeMap<Cid, Type> = {
5858
let mut map = BTreeMap::new();
5959
map.insert(*SYSTEM_ACTOR_CODE_ID, Type::System);
@@ -99,7 +99,7 @@ lazy_static::lazy_static! {
9999
const IPLD_RAW: u64 = 0x55;
100100

101101
/// Returns an identity CID for bz.
102-
pub fn make_builtin(bz: &[u8]) -> Cid {
102+
pub fn make_identity_cid(bz: &[u8]) -> Cid {
103103
Cid::new_v1(IPLD_RAW, OtherMultihash::wrap(0, bz).expect("name too long"))
104104
}
105105

test_vm/tests/test_vm_test.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use fil_actor_account::State as AccountState;
2-
use fil_actors_runtime::test_utils::{make_builtin, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID};
2+
use fil_actors_runtime::test_utils::{
3+
make_identity_cid, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID,
4+
};
35
use fvm_ipld_blockstore::MemoryBlockstore;
46
use fvm_ipld_encoding::RawBytes;
57
use fvm_shared::address::Address;
@@ -18,14 +20,19 @@ fn state_control() {
1820
let addr2 = Address::new_id(2222);
1921

2022
// set actor
21-
let a1 =
22-
actor(*ACCOUNT_ACTOR_CODE_ID, make_builtin(b"a1-head"), 42, TokenAmount::from_atto(10u8));
23+
let a1 = actor(
24+
*ACCOUNT_ACTOR_CODE_ID,
25+
make_identity_cid(b"a1-head"),
26+
42,
27+
TokenAmount::from_atto(10u8),
28+
);
2329
v.set_actor(addr1, a1.clone());
2430
let out = v.get_actor(addr1).unwrap();
2531
assert_eq!(out, a1);
2632
let check = v.checkpoint();
2733

28-
let a2 = actor(*PAYCH_ACTOR_CODE_ID, make_builtin(b"a2-head"), 88, TokenAmount::from_atto(1u8));
34+
let a2 =
35+
actor(*PAYCH_ACTOR_CODE_ID, make_identity_cid(b"a2-head"), 88, TokenAmount::from_atto(1u8));
2936
v.set_actor(addr2, a2.clone());
3037
assert_eq!(v.get_actor(addr2).unwrap(), a2);
3138
// rollback removes a2 but not a1

0 commit comments

Comments
 (0)