Skip to content

Commit

Permalink
feat: Implement FIP-0044 (#502)
Browse files Browse the repository at this point in the history
* feat: add authenticate message to account actor

* hook up market actor to call new authenticate_message method

* fixup and add tests
  • Loading branch information
arajasek authored Aug 31, 2022
1 parent 159a585 commit f5855ac
Show file tree
Hide file tree
Showing 14 changed files with 573 additions and 144 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions actors/account/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ num-traits = "0.2.14"
num-derive = "0.3.3"
fvm_ipld_blockstore = "0.1.1"
fvm_ipld_encoding = "0.2.2"
anyhow = "1.0.56"

[dev-dependencies]
fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] }
Expand Down
48 changes: 46 additions & 2 deletions actors/account/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::{Address, Protocol};
use fvm_shared::crypto::signature::SignatureType::{Secp256k1, BLS};
use fvm_shared::crypto::signature::{Signature, SignatureType};
use fvm_shared::error::ExitCode;
use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;

use crate::types::AuthenticateMessageParams;
use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR;
use fil_actors_runtime::cbor;
use fil_actors_runtime::runtime::{ActorCode, Runtime};
use fil_actors_runtime::{actor_error, ActorError};
use fil_actors_runtime::{cbor, ActorDowncast};

pub use self::state::State;

mod state;
pub mod testing;
pub mod types;

#[cfg(feature = "fil-actor")]
fil_actors_runtime::wasm_trampoline!(Actor);
Expand All @@ -29,10 +34,12 @@ fil_actors_runtime::wasm_trampoline!(Actor);
pub enum Method {
Constructor = METHOD_CONSTRUCTOR,
PubkeyAddress = 2,
AuthenticateMessage = 3,
}

/// Account Actor
pub struct Actor;

impl Actor {
/// Constructor for Account actor
pub fn constructor<BS, RT>(rt: &mut RT, address: Address) -> Result<(), ActorError>
Expand All @@ -52,7 +59,7 @@ impl Actor {
Ok(())
}

// Fetches the pubkey-type address from this actor.
/// Fetches the pubkey-type address from this actor.
pub fn pubkey_address<BS, RT>(rt: &mut RT) -> Result<Address, ActorError>
where
BS: Blockstore,
Expand All @@ -62,6 +69,39 @@ impl Actor {
let st: State = rt.state()?;
Ok(st.address)
}

/// Authenticates whether the provided signature is valid for the provided message.
/// Should be called with the raw bytes of a signature, NOT a serialized Signature object that includes a SignatureType.
/// Errors with USR_ILLEGAL_ARGUMENT if the authentication is invalid.
pub fn authenticate_message<BS, RT>(
rt: &mut RT,
params: AuthenticateMessageParams,
) -> Result<(), ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let st: State = rt.state()?;
let address = st.address;
let sig_type: SignatureType = match address.protocol() {
Protocol::Secp256k1 => Secp256k1,
Protocol::BLS => BLS,
protocol => {
return Err(actor_error!(illegal_state;
"account address must use BLS or SECP protocol, got {}", protocol));
}
};
let sig = Signature { sig_type, bytes: params.signature };
rt.verify_signature(&sig, &address, &params.message).map_err(|e| {
e.downcast_default(
ExitCode::USR_ILLEGAL_ARGUMENT,
"failed to authenticate message, signature invalid",
)
})?;

Ok(())
}
}

impl ActorCode for Actor {
Expand All @@ -83,6 +123,10 @@ impl ActorCode for Actor {
let addr = Self::pubkey_address(rt)?;
Ok(RawBytes::serialize(addr)?)
}
Some(Method::AuthenticateMessage) => {
Self::authenticate_message(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::default())
}
None => Err(actor_error!(unhandled_message; "Invalid method")),
}
}
Expand Down
12 changes: 12 additions & 0 deletions actors/account/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use fvm_ipld_encoding::tuple::*;
use fvm_ipld_encoding::{serde_bytes, Cbor};

#[derive(Debug, Serialize_tuple, Deserialize_tuple)]
pub struct AuthenticateMessageParams {
#[serde(with = "serde_bytes")]
pub signature: Vec<u8>,
#[serde(with = "serde_bytes")]
pub message: Vec<u8>,
}

impl Cbor for AuthenticateMessageParams {}
52 changes: 50 additions & 2 deletions actors/account/tests/account_actor_test.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use anyhow::anyhow;
use fil_actor_account::types::AuthenticateMessageParams;
use fil_actor_account::{testing::check_state_invariants, Actor as AccountActor, State};
use fil_actors_runtime::builtin::SYSTEM_ACTOR_ADDR;
use fil_actors_runtime::test_utils::*;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
use fvm_shared::crypto::signature::Signature;
use fvm_shared::error::ExitCode;

fn check_state(rt: &MockRuntime) {
Expand All @@ -14,7 +17,7 @@ fn check_state(rt: &MockRuntime) {
acc.assert_empty();
}

macro_rules! account_tests {
macro_rules! account_constructor_tests {
($($name:ident: $value:expr,)*) => {
$(
#[test]
Expand Down Expand Up @@ -56,7 +59,7 @@ macro_rules! account_tests {
}
}

account_tests! {
account_constructor_tests! {
happy_construct_secp256k1_address: (
Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap(),
ExitCode::OK
Expand All @@ -74,3 +77,48 @@ account_tests! {
ExitCode::USR_ILLEGAL_ARGUMENT
),
}

#[test]
fn authenticate_message() {
let mut rt = MockRuntime {
receiver: Address::new_id(100),
caller: *SYSTEM_ACTOR_ADDR,
caller_type: *SYSTEM_ACTOR_CODE_ID,
..Default::default()
};

let addr = Address::new_secp256k1(&[2; fvm_shared::address::SECP_PUB_LEN]).unwrap();
rt.expect_validate_caller_addr(vec![*SYSTEM_ACTOR_ADDR]);

rt.call::<AccountActor>(1, &RawBytes::serialize(addr).unwrap()).unwrap();

let state: State = rt.get_state();
assert_eq!(state.address, addr);

let params =
RawBytes::serialize(AuthenticateMessageParams { signature: vec![], message: vec![] })
.unwrap();

rt.expect_validate_caller_any();
rt.expect_verify_signature(ExpectedVerifySig {
sig: Signature::new_secp256k1(vec![]),
signer: addr,
plaintext: vec![],
result: Ok(()),
});
assert_eq!(RawBytes::default(), rt.call::<AccountActor>(3, &params).unwrap());

rt.expect_validate_caller_any();
rt.expect_verify_signature(ExpectedVerifySig {
sig: Signature::new_secp256k1(vec![]),
signer: addr,
plaintext: vec![],
result: Err(anyhow!("bad signature")),
});
assert_eq!(
ExitCode::USR_ILLEGAL_ARGUMENT,
rt.call::<AccountActor>(3, &params).unwrap_err().exit_code()
);

rt.verify();
}
17 changes: 17 additions & 0 deletions actors/market/src/ext.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use fvm_ipld_encoding::serde_bytes;
use fvm_ipld_encoding::tuple::*;
use fvm_shared::address::Address;
use fvm_shared::bigint::bigint_ser;
Expand All @@ -6,6 +7,20 @@ use fvm_shared::econ::TokenAmount;
use fvm_shared::sector::StoragePower;
use fvm_shared::smooth::FilterEstimate;

pub mod account {
use super::*;

pub const AUTHENTICATE_MESSAGE_METHOD: u64 = 3;

#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct AuthenticateMessageParams {
#[serde(with = "serde_bytes")]
pub signature: Vec<u8>,
#[serde(with = "serde_bytes")]
pub message: Vec<u8>,
}
}

pub mod miner {
use super::*;

Expand All @@ -25,6 +40,7 @@ pub mod verifreg {
// based on fil_actor_verifreg
pub const USE_BYTES_METHOD: u64 = 5;
pub const RESTORE_BYTES_METHOD: u64 = 6;

pub type UseBytesParams = BytesParams;
pub type RestoreBytesParams = BytesParams;

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

pub mod power {
use super::*;

pub const CURRENT_TOTAL_POWER_METHOD: u64 = 9;

#[derive(Serialize_tuple, Deserialize_tuple)]
Expand Down
18 changes: 13 additions & 5 deletions actors/market/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1282,12 +1282,20 @@ where
BS: Blockstore,
RT: Runtime<BS>,
{
let signature_bytes = proposal.client_signature.bytes.clone();
// Generate unsigned bytes
let sv_bz = serialize_vec(&proposal.proposal, "deal proposal")?;
rt.verify_signature(&proposal.client_signature, &proposal.proposal.client, &sv_bz).map_err(
|e| e.downcast_default(ExitCode::USR_ILLEGAL_ARGUMENT, "signature proposal invalid"),
)?;

let proposal_bytes = serialize_vec(&proposal.proposal, "deal proposal")?;

rt.send(
&proposal.proposal.client,
ext::account::AUTHENTICATE_MESSAGE_METHOD,
RawBytes::serialize(ext::account::AuthenticateMessageParams {
signature: signature_bytes,
message: proposal_bytes,
})?,
TokenAmount::zero(),
)
.map_err(|e| e.wrap("proposal authentication failed"))?;
Ok(())
}

Expand Down
27 changes: 19 additions & 8 deletions actors/market/tests/cron_tick_timedout_deals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ use fvm_shared::error::ExitCode;
use fvm_shared::sector::StoragePower;
use fvm_shared::METHOD_SEND;

use fil_actor_market::ext::account::{AuthenticateMessageParams, AUTHENTICATE_MESSAGE_METHOD};
use num_traits::Zero;

mod harness;

use harness::*;

const START_EPOCH: ChainEpoch = 50;
Expand Down Expand Up @@ -79,20 +81,29 @@ fn publishing_timed_out_deal_again_should_work_after_cron_tick_as_it_should_no_l
END_EPOCH,
);
let buf = RawBytes::serialize(deal_proposal2.clone()).expect("failed to marshal deal proposal");
let sig = Signature::new_bls("does not matter".as_bytes().to_vec());
let sig = Signature::new_bls(buf.to_vec());
let client_deal_proposal =
ClientDealProposal { proposal: deal_proposal2.clone(), client_signature: sig.clone() };
ClientDealProposal { proposal: deal_proposal2.clone(), client_signature: sig };
let params = PublishStorageDealsParams { deals: vec![client_deal_proposal] };
rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]);
expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR);
expect_query_network_info(&mut rt);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR);
rt.expect_verify_signature(ExpectedVerifySig {
sig,
signer: deal_proposal2.client,
plaintext: buf.to_vec(),
result: Ok(()),
});
let auth_param = RawBytes::serialize(AuthenticateMessageParams {
signature: buf.to_vec(),
message: buf.to_vec(),
})
.unwrap();

rt.expect_send(
deal_proposal2.client,
AUTHENTICATE_MESSAGE_METHOD,
auth_param,
TokenAmount::from(0u8),
RawBytes::default(),
ExitCode::OK,
);

expect_abort(
ExitCode::USR_ILLEGAL_ARGUMENT,
rt.call::<MarketActor>(
Expand Down
Loading

0 comments on commit f5855ac

Please sign in to comment.