Skip to content

Commit f5855ac

Browse files
authored
feat: Implement FIP-0044 (#502)
* feat: add authenticate message to account actor * hook up market actor to call new authenticate_message method * fixup and add tests
1 parent 159a585 commit f5855ac

14 files changed

+573
-144
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

actors/account/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ num-traits = "0.2.14"
2020
num-derive = "0.3.3"
2121
fvm_ipld_blockstore = "0.1.1"
2222
fvm_ipld_encoding = "0.2.2"
23+
anyhow = "1.0.56"
2324

2425
[dev-dependencies]
2526
fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] }

actors/account/src/lib.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,24 @@
44
use fvm_ipld_blockstore::Blockstore;
55
use fvm_ipld_encoding::RawBytes;
66
use fvm_shared::address::{Address, Protocol};
7+
use fvm_shared::crypto::signature::SignatureType::{Secp256k1, BLS};
8+
use fvm_shared::crypto::signature::{Signature, SignatureType};
9+
use fvm_shared::error::ExitCode;
710
use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR};
811
use num_derive::FromPrimitive;
912
use num_traits::FromPrimitive;
1013

14+
use crate::types::AuthenticateMessageParams;
1115
use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR;
12-
use fil_actors_runtime::cbor;
1316
use fil_actors_runtime::runtime::{ActorCode, Runtime};
1417
use fil_actors_runtime::{actor_error, ActorError};
18+
use fil_actors_runtime::{cbor, ActorDowncast};
1519

1620
pub use self::state::State;
1721

1822
mod state;
1923
pub mod testing;
24+
pub mod types;
2025

2126
#[cfg(feature = "fil-actor")]
2227
fil_actors_runtime::wasm_trampoline!(Actor);
@@ -29,10 +34,12 @@ fil_actors_runtime::wasm_trampoline!(Actor);
2934
pub enum Method {
3035
Constructor = METHOD_CONSTRUCTOR,
3136
PubkeyAddress = 2,
37+
AuthenticateMessage = 3,
3238
}
3339

3440
/// Account Actor
3541
pub struct Actor;
42+
3643
impl Actor {
3744
/// Constructor for Account actor
3845
pub fn constructor<BS, RT>(rt: &mut RT, address: Address) -> Result<(), ActorError>
@@ -52,7 +59,7 @@ impl Actor {
5259
Ok(())
5360
}
5461

55-
// Fetches the pubkey-type address from this actor.
62+
/// Fetches the pubkey-type address from this actor.
5663
pub fn pubkey_address<BS, RT>(rt: &mut RT) -> Result<Address, ActorError>
5764
where
5865
BS: Blockstore,
@@ -62,6 +69,39 @@ impl Actor {
6269
let st: State = rt.state()?;
6370
Ok(st.address)
6471
}
72+
73+
/// Authenticates whether the provided signature is valid for the provided message.
74+
/// Should be called with the raw bytes of a signature, NOT a serialized Signature object that includes a SignatureType.
75+
/// Errors with USR_ILLEGAL_ARGUMENT if the authentication is invalid.
76+
pub fn authenticate_message<BS, RT>(
77+
rt: &mut RT,
78+
params: AuthenticateMessageParams,
79+
) -> Result<(), ActorError>
80+
where
81+
BS: Blockstore,
82+
RT: Runtime<BS>,
83+
{
84+
rt.validate_immediate_caller_accept_any()?;
85+
let st: State = rt.state()?;
86+
let address = st.address;
87+
let sig_type: SignatureType = match address.protocol() {
88+
Protocol::Secp256k1 => Secp256k1,
89+
Protocol::BLS => BLS,
90+
protocol => {
91+
return Err(actor_error!(illegal_state;
92+
"account address must use BLS or SECP protocol, got {}", protocol));
93+
}
94+
};
95+
let sig = Signature { sig_type, bytes: params.signature };
96+
rt.verify_signature(&sig, &address, &params.message).map_err(|e| {
97+
e.downcast_default(
98+
ExitCode::USR_ILLEGAL_ARGUMENT,
99+
"failed to authenticate message, signature invalid",
100+
)
101+
})?;
102+
103+
Ok(())
104+
}
65105
}
66106

67107
impl ActorCode for Actor {
@@ -83,6 +123,10 @@ impl ActorCode for Actor {
83123
let addr = Self::pubkey_address(rt)?;
84124
Ok(RawBytes::serialize(addr)?)
85125
}
126+
Some(Method::AuthenticateMessage) => {
127+
Self::authenticate_message(rt, cbor::deserialize_params(params)?)?;
128+
Ok(RawBytes::default())
129+
}
86130
None => Err(actor_error!(unhandled_message; "Invalid method")),
87131
}
88132
}

actors/account/src/types.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use fvm_ipld_encoding::tuple::*;
2+
use fvm_ipld_encoding::{serde_bytes, Cbor};
3+
4+
#[derive(Debug, Serialize_tuple, Deserialize_tuple)]
5+
pub struct AuthenticateMessageParams {
6+
#[serde(with = "serde_bytes")]
7+
pub signature: Vec<u8>,
8+
#[serde(with = "serde_bytes")]
9+
pub message: Vec<u8>,
10+
}
11+
12+
impl Cbor for AuthenticateMessageParams {}

actors/account/tests/account_actor_test.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// Copyright 2019-2022 ChainSafe Systems
22
// SPDX-License-Identifier: Apache-2.0, MIT
33

4+
use anyhow::anyhow;
5+
use fil_actor_account::types::AuthenticateMessageParams;
46
use fil_actor_account::{testing::check_state_invariants, Actor as AccountActor, State};
57
use fil_actors_runtime::builtin::SYSTEM_ACTOR_ADDR;
68
use fil_actors_runtime::test_utils::*;
79
use fvm_ipld_encoding::RawBytes;
810
use fvm_shared::address::Address;
11+
use fvm_shared::crypto::signature::Signature;
912
use fvm_shared::error::ExitCode;
1013

1114
fn check_state(rt: &MockRuntime) {
@@ -14,7 +17,7 @@ fn check_state(rt: &MockRuntime) {
1417
acc.assert_empty();
1518
}
1619

17-
macro_rules! account_tests {
20+
macro_rules! account_constructor_tests {
1821
($($name:ident: $value:expr,)*) => {
1922
$(
2023
#[test]
@@ -56,7 +59,7 @@ macro_rules! account_tests {
5659
}
5760
}
5861

59-
account_tests! {
62+
account_constructor_tests! {
6063
happy_construct_secp256k1_address: (
6164
Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(),
6265
ExitCode::OK
@@ -74,3 +77,48 @@ account_tests! {
7477
ExitCode::USR_ILLEGAL_ARGUMENT
7578
),
7679
}
80+
81+
#[test]
82+
fn authenticate_message() {
83+
let mut rt = MockRuntime {
84+
receiver: Address::new_id(100),
85+
caller: *SYSTEM_ACTOR_ADDR,
86+
caller_type: *SYSTEM_ACTOR_CODE_ID,
87+
..Default::default()
88+
};
89+
90+
let addr = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap();
91+
rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]);
92+
93+
rt.call::<AccountActor>(1, &RawBytes::serialize(addr).unwrap()).unwrap();
94+
95+
let state: State = rt.get_state();
96+
assert_eq!(state.address, addr);
97+
98+
let params =
99+
RawBytes::serialize(AuthenticateMessageParams { signature: vec![], message: vec![] })
100+
.unwrap();
101+
102+
rt.expect_validate_caller_any();
103+
rt.expect_verify_signature(ExpectedVerifySig {
104+
sig: Signature::new_secp256k1(vec![]),
105+
signer: addr,
106+
plaintext: vec![],
107+
result: Ok(()),
108+
});
109+
assert_eq!(RawBytes::default(), rt.call::<AccountActor>(3, &params).unwrap());
110+
111+
rt.expect_validate_caller_any();
112+
rt.expect_verify_signature(ExpectedVerifySig {
113+
sig: Signature::new_secp256k1(vec![]),
114+
signer: addr,
115+
plaintext: vec![],
116+
result: Err(anyhow!("bad signature")),
117+
});
118+
assert_eq!(
119+
ExitCode::USR_ILLEGAL_ARGUMENT,
120+
rt.call::<AccountActor>(3, &params).unwrap_err().exit_code()
121+
);
122+
123+
rt.verify();
124+
}

actors/market/src/ext.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use fvm_ipld_encoding::serde_bytes;
12
use fvm_ipld_encoding::tuple::*;
23
use fvm_shared::address::Address;
34
use fvm_shared::bigint::bigint_ser;
@@ -6,6 +7,20 @@ use fvm_shared::econ::TokenAmount;
67
use fvm_shared::sector::StoragePower;
78
use fvm_shared::smooth::FilterEstimate;
89

10+
pub mod account {
11+
use super::*;
12+
13+
pub const AUTHENTICATE_MESSAGE_METHOD: u64 = 3;
14+
15+
#[derive(Serialize_tuple, Deserialize_tuple)]
16+
pub struct AuthenticateMessageParams {
17+
#[serde(with = "serde_bytes")]
18+
pub signature: Vec<u8>,
19+
#[serde(with = "serde_bytes")]
20+
pub message: Vec<u8>,
21+
}
22+
}
23+
924
pub mod miner {
1025
use super::*;
1126

@@ -25,6 +40,7 @@ pub mod verifreg {
2540
// based on fil_actor_verifreg
2641
pub const USE_BYTES_METHOD: u64 = 5;
2742
pub const RESTORE_BYTES_METHOD: u64 = 6;
43+
2844
pub type UseBytesParams = BytesParams;
2945
pub type RestoreBytesParams = BytesParams;
3046

@@ -44,6 +60,7 @@ pub mod reward {
4460

4561
pub mod power {
4662
use super::*;
63+
4764
pub const CURRENT_TOTAL_POWER_METHOD: u64 = 9;
4865

4966
#[derive(Serialize_tuple, Deserialize_tuple)]

actors/market/src/lib.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,12 +1282,20 @@ where
12821282
BS: Blockstore,
12831283
RT: Runtime<BS>,
12841284
{
1285+
let signature_bytes = proposal.client_signature.bytes.clone();
12851286
// Generate unsigned bytes
1286-
let sv_bz = serialize_vec(&proposal.proposal, "deal proposal")?;
1287-
rt.verify_signature(&proposal.client_signature, &proposal.proposal.client, &sv_bz).map_err(
1288-
|e| e.downcast_default(ExitCode::USR_ILLEGAL_ARGUMENT, "signature proposal invalid"),
1289-
)?;
1290-
1287+
let proposal_bytes = serialize_vec(&proposal.proposal, "deal proposal")?;
1288+
1289+
rt.send(
1290+
&proposal.proposal.client,
1291+
ext::account::AUTHENTICATE_MESSAGE_METHOD,
1292+
RawBytes::serialize(ext::account::AuthenticateMessageParams {
1293+
signature: signature_bytes,
1294+
message: proposal_bytes,
1295+
})?,
1296+
TokenAmount::zero(),
1297+
)
1298+
.map_err(|e| e.wrap("proposal authentication failed"))?;
12911299
Ok(())
12921300
}
12931301

actors/market/tests/cron_tick_timedout_deals.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ use fvm_shared::error::ExitCode;
1616
use fvm_shared::sector::StoragePower;
1717
use fvm_shared::METHOD_SEND;
1818

19+
use fil_actor_market::ext::account::{AuthenticateMessageParams, AUTHENTICATE_MESSAGE_METHOD};
1920
use num_traits::Zero;
2021

2122
mod harness;
23+
2224
use harness::*;
2325

2426
const START_EPOCH: ChainEpoch = 50;
@@ -79,20 +81,29 @@ fn publishing_timed_out_deal_again_should_work_after_cron_tick_as_it_should_no_l
7981
END_EPOCH,
8082
);
8183
let buf = RawBytes::serialize(deal_proposal2.clone()).expect("failed to marshal deal proposal");
82-
let sig = Signature::new_bls("does not matter".as_bytes().to_vec());
84+
let sig = Signature::new_bls(buf.to_vec());
8385
let client_deal_proposal =
84-
ClientDealProposal { proposal: deal_proposal2.clone(), client_signature: sig.clone() };
86+
ClientDealProposal { proposal: deal_proposal2.clone(), client_signature: sig };
8587
let params = PublishStorageDealsParams { deals: vec![client_deal_proposal] };
8688
rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]);
8789
expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR);
8890
expect_query_network_info(&mut rt);
8991
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR);
90-
rt.expect_verify_signature(ExpectedVerifySig {
91-
sig,
92-
signer: deal_proposal2.client,
93-
plaintext: buf.to_vec(),
94-
result: Ok(()),
95-
});
92+
let auth_param = RawBytes::serialize(AuthenticateMessageParams {
93+
signature: buf.to_vec(),
94+
message: buf.to_vec(),
95+
})
96+
.unwrap();
97+
98+
rt.expect_send(
99+
deal_proposal2.client,
100+
AUTHENTICATE_MESSAGE_METHOD,
101+
auth_param,
102+
TokenAmount::from(0u8),
103+
RawBytes::default(),
104+
ExitCode::OK,
105+
);
106+
96107
expect_abort(
97108
ExitCode::USR_ILLEGAL_ARGUMENT,
98109
rt.call::<MarketActor>(

0 commit comments

Comments
 (0)