Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

### Added

- [#6524](https://github.com/ChainSafe/forest/pull/6524): Implemented `Filecoin.EthSendRawTransactionUntrusted` for API v2.

- [#6513](https://github.com/ChainSafe/forest/pull/6513): Enabled `Filecoin.EthNewFilter` for API v2.

### Changed
Expand Down
11 changes: 9 additions & 2 deletions src/message_pool/msgpool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use utils::{get_base_fee_lower_bound, recover_sig};
use super::errors::Error;
use crate::message_pool::{
msg_chain::{Chains, create_message_chains},
msg_pool::{MsgSet, add_helper, remove},
msg_pool::{MsgSet, TrustPolicy, add_helper, remove},
provider::Provider,
};

Expand Down Expand Up @@ -277,7 +277,14 @@ where
for (_, hm) in rmsgs {
for (_, msg) in hm {
let sequence = get_state_sequence(api, &msg.from(), &cur_tipset.read().clone())?;
if let Err(e) = add_helper(api, bls_sig_cache, pending, msg, sequence) {
if let Err(e) = add_helper(
api,
bls_sig_cache,
pending,
msg,
sequence,
TrustPolicy::Trusted,
) {
error!("Failed to read message from reorg to mpool: {}", e);
}
}
Expand Down
66 changes: 49 additions & 17 deletions src/message_pool/msgpool/msg_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ pub const MAX_UNTRUSTED_ACTOR_PENDING_MESSAGES: u64 = 10;
/// large messages from being added to the message pool.
const MAX_MESSAGE_SIZE: usize = 64 << 10; // 64 KiB

/// Trust policy for whether a message is from a trusted or untrusted source.
/// Untrusted sources are subject to stricter limits.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TrustPolicy {
Trusted,
Untrusted,
}

/// Simple structure that contains a hash-map of messages where k: a message
/// from address, v: a message which corresponds to that address.
#[derive(Clone, Default, Debug)]
Expand Down Expand Up @@ -89,7 +97,6 @@ impl MsgSet {
/// Add a signed message to the `MsgSet`. Increase `next_sequence` if the
/// message has a sequence greater than any existing message sequence.
/// Use this method when pushing a message coming from untrusted sources.
#[allow(dead_code)]
pub fn add_untrusted<T>(&mut self, api: &T, m: SignedMessage) -> Result<(), Error>
where
T: Provider,
Expand Down Expand Up @@ -220,11 +227,15 @@ where

/// Push a signed message to the `MessagePool`. Additionally performs basic
/// checks on the validity of a message.
pub async fn push(&self, msg: SignedMessage) -> Result<Cid, Error> {
pub async fn push_internal(
&self,
msg: SignedMessage,
trust_policy: TrustPolicy,
) -> Result<Cid, Error> {
self.check_message(&msg)?;
let cid = msg.cid();
let cur_ts = self.current_tipset();
let publish = self.add_tipset(msg.clone(), &cur_ts, true)?;
let publish = self.add_tipset(msg.clone(), &cur_ts, true, trust_policy)?;
let msg_ser = to_vec(&msg)?;
let network_name = self.chain_config.network.genesis_name();
self.add_local(msg)?;
Expand All @@ -240,6 +251,16 @@ where
Ok(cid)
}

/// Push a signed message to the `MessagePool` from an trusted source.
pub async fn push(&self, msg: SignedMessage) -> Result<Cid, Error> {
self.push_internal(msg, TrustPolicy::Trusted).await
}

/// Push a signed message to the `MessagePool` from an untrusted source.
pub async fn push_untrusted(&self, msg: SignedMessage) -> Result<Cid, Error> {
self.push_internal(msg, TrustPolicy::Untrusted).await
}

fn check_message(&self, msg: &SignedMessage) -> Result<(), Error> {
if to_vec(msg)?.len() > MAX_MESSAGE_SIZE {
return Err(Error::MessageTooBig);
Expand All @@ -259,7 +280,7 @@ where
pub fn add(&self, msg: SignedMessage) -> Result<(), Error> {
self.check_message(&msg)?;
let ts = self.current_tipset();
self.add_tipset(msg, &ts, false)?;
self.add_tipset(msg, &ts, false, TrustPolicy::Trusted)?;
Ok(())
}

Expand All @@ -284,7 +305,13 @@ where
/// Verify the `state_sequence` and balance for the sender of the message
/// given then call `add_locked` to finish adding the `signed_message`
/// to pending.
fn add_tipset(&self, msg: SignedMessage, cur_ts: &Tipset, local: bool) -> Result<bool, Error> {
fn add_tipset(
&self,
msg: SignedMessage,
cur_ts: &Tipset,
local: bool,
trust_policy: TrustPolicy,
) -> Result<bool, Error> {
let sequence = self.get_state_sequence(&msg.from(), cur_ts)?;

if sequence > msg.message().sequence {
Expand Down Expand Up @@ -317,15 +344,15 @@ where
if balance < msg_balance {
return Err(Error::NotEnoughFunds);
}
self.add_helper(msg)?;
self.add_helper(msg, trust_policy)?;
Ok(publish)
}

/// Finish verifying signed message before adding it to the pending `mset`
/// hash-map. If an entry in the hash-map does not yet exist, create a
/// new `mset` that will correspond to the from message and push it to
/// the pending hash-map.
fn add_helper(&self, msg: SignedMessage) -> Result<(), Error> {
fn add_helper(&self, msg: SignedMessage, trust_policy: TrustPolicy) -> Result<(), Error> {
let from = msg.from();
let cur_ts = self.current_tipset();
add_helper(
Expand All @@ -334,6 +361,7 @@ where
self.pending.as_ref(),
msg,
self.get_state_sequence(&from, &cur_ts)?,
trust_policy,
)
}

Expand Down Expand Up @@ -599,6 +627,7 @@ pub(in crate::message_pool) fn add_helper<T>(
pending: &SyncRwLock<HashMap<Address, MsgSet>>,
msg: SignedMessage,
sequence: u64,
trust_policy: TrustPolicy,
) -> Result<(), Error>
where
T: Provider,
Expand All @@ -611,15 +640,11 @@ where
api.put_message(&ChainMessage::Unsigned(msg.message().clone()))?;

let mut pending = pending.write();
let msett = pending.get_mut(&msg.from());
match msett {
Some(mset) => mset.add_trusted(api, msg)?,
None => {
let mut mset = MsgSet::new(sequence);
let from = msg.from();
mset.add_trusted(api, msg)?;
pending.insert(from, mset);
}
let from = msg.from();
let mset = pending.entry(from).or_insert_with(|| MsgSet::new(sequence));
match trust_policy {
TrustPolicy::Untrusted => mset.add_untrusted(api, msg)?,
TrustPolicy::Trusted => mset.add_trusted(api, msg)?,
}

Ok(())
Expand Down Expand Up @@ -701,7 +726,14 @@ mod tests {
};
let msg = SignedMessage::mock_bls_signed_message(message);
let sequence = msg.message().sequence;
let res = add_helper(&api, &bls_sig_cache, &pending, msg, sequence);
let res = add_helper(
&api,
&bls_sig_cache,
&pending,
msg,
sequence,
TrustPolicy::Trusted,
);
assert!(res.is_ok());
}

Expand Down
22 changes: 22 additions & 0 deletions src/rpc/methods/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3433,6 +3433,28 @@ impl RpcMethod<1> for EthSendRawTransaction {
}
}

pub enum EthSendRawTransactionUntrusted {}
impl RpcMethod<1> for EthSendRawTransactionUntrusted {
const NAME: &'static str = "Filecoin.EthSendRawTransactionUntrusted";
const NAME_ALIAS: Option<&'static str> = Some("eth_sendRawTransactionUntrusted");
const PARAM_NAMES: [&'static str; 1] = ["rawTx"];
const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
const PERMISSION: Permission = Permission::Read;

type Params = (EthBytes,);
type Ok = EthHash;

async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(raw_tx,): Self::Params,
) -> Result<Self::Ok, ServerError> {
let tx_args = parse_eth_transaction(&raw_tx.0)?;
let smsg = tx_args.get_signed_message(ctx.chain_config().eth_chain_id)?;
let cid = ctx.mpool.as_ref().push_untrusted(smsg).await?;
Ok(cid.into())
}
}

#[derive(Clone, Debug, PartialEq)]
pub struct CollectedEvent {
pub(crate) entries: Vec<EventEntry>,
Expand Down
3 changes: 2 additions & 1 deletion src/rpc/methods/mpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ impl RpcMethod<1> for MpoolPushUntrusted {
// Lotus implements a few extra sanity checks that we skip. We skip them
// because those checks aren't used for messages received from peers and
// therefore aren't safety critical.
MpoolPush::handle(ctx, (message,)).await
let cid = ctx.mpool.as_ref().push_untrusted(message).await?;
Ok(cid)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ macro_rules! for_each_rpc_method {
$callback!($crate::rpc::eth::EthTraceReplayBlockTransactionsV2);
$callback!($crate::rpc::eth::Web3ClientVersion);
$callback!($crate::rpc::eth::EthSendRawTransaction);
$callback!($crate::rpc::eth::EthSendRawTransactionUntrusted);

// gas vertical
$callback!($crate::rpc::gas::GasEstimateFeeCap);
Expand Down
24 changes: 24 additions & 0 deletions src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap

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

24 changes: 24 additions & 0 deletions src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap

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

24 changes: 24 additions & 0 deletions src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap

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

1 change: 1 addition & 0 deletions src/tool/subcommands/api_cmd/test_snapshots_ignored.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Filecoin.EthEstimateGas
Filecoin.EthGetFilterChanges
Filecoin.EthGetFilterLogs
Filecoin.EthSendRawTransaction
Filecoin.EthSendRawTransactionUntrusted
Filecoin.EthSubscribe
Filecoin.EthSyncing
Filecoin.EthUnsubscribe
Expand Down
Loading