Skip to content

Commit

Permalink
minimal fees (#4664)
Browse files Browse the repository at this point in the history
* add minimal fees to config

* parse config

* update operation pool to not include op with fee < config.fee

* return minimal_fees in node get_status (rpc api)

* update massa-client to reject op with fee < minimal_fees

* update proto-rs sc-runtime rev

* update massa-api

* grpc get_status

* update massa grpc

* clippy

* massa-api unit test

* fix test

* PR comment

* fix config.toml

* massa-sc-runtime target main
  • Loading branch information
modship committed Apr 4, 2024
1 parent 5897221 commit 8b23f0a
Show file tree
Hide file tree
Showing 19 changed files with 291 additions and 23 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ opt-level = 3 # Speed-up the CI
# This profile is used for prebuilt binaries.
[profile.release_prebuilt]
inherits = "release"
codegen-units = 1 # Do not split into multiple codegen units, improve performance, may compile slower
lto = true # Enables Link Time Optimization, enabling more aggressive optimizations across the entire codebase
codegen-units = 1 # Do not split into multiple codegen units, improve performance, may compile slower
lto = true # Enables Link Time Optimization, enabling more aggressive optimizations across the entire codebase

# # Features
#
Expand Down Expand Up @@ -106,8 +106,8 @@ massa_versioning = { path = "./massa-versioning" }
massa_wallet = { path = "./massa-wallet" }

# Massa projects dependencies
massa-proto-rs = { git = "https://github.com/massalabs/massa-proto-rs", "rev" = "25638b3b7d387afbca81afcb02bae1af03697f18" }
massa-sc-runtime = { git = "https://github.com/massalabs/massa-sc-runtime", "rev" = "32102813c05559c17d8ac1e1ee673802c2ba2ea8" }
massa-proto-rs = { git = "https://github.com/massalabs/massa-proto-rs", "rev" = "426fd325a55dfcc4033920bed2de075a7e7ad4b7" }
massa-sc-runtime = { git = "https://github.com/massalabs/massa-sc-runtime", "rev" = "e95066a6d3a963ff0125616f404a84e5abb63d63" }
peernet = { git = "https://github.com/massalabs/PeerNet", "rev" = "04b05ddd320fbe76cc858115af7b5fc28bdb8310" }

# Common dependencies
Expand Down Expand Up @@ -201,4 +201,4 @@ tracing-subscriber = "0.3"
unsigned-varint = "0.8"
variant_count = "1.1"
walkdir = "2.3"
zeroize = { version = "1.7", features = ["derive"] }
zeroize = { version = "1.7", features = ["derive"] }
3 changes: 3 additions & 0 deletions massa-api-exports/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) 2022 MASSA LABS <info@massa.net>

use massa_models::amount::Amount;
use massa_signature::KeyPair;
use massa_time::MassaTime;
use std::net::SocketAddr;
Expand Down Expand Up @@ -81,4 +82,6 @@ pub struct APIConfig {
pub chain_id: u64,
/// Delta to compute upper bounds when fetching deferred credits
pub deferred_credits_delta: MassaTime,
/// minimal fees to include an operation in a block
pub minimal_fees: Amount,
}
3 changes: 3 additions & 0 deletions massa-api-exports/src/node.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) 2022 MASSA LABS <info@massa.net>

use massa_models::amount::Amount;
use massa_models::node::NodeId;
use massa_models::stats::{ConsensusStats, ExecutionStats, NetworkStats};
use massa_models::{config::CompactConfig, slot::Slot, version::Version};
Expand Down Expand Up @@ -43,6 +44,8 @@ pub struct NodeStatus {
pub config: CompactConfig,
/// chain id
pub chain_id: u64,
/// minimal fees to include an operation in a block
pub minimal_fees: Amount,
}

impl std::fmt::Display for NodeStatus {
Expand Down
56 changes: 56 additions & 0 deletions massa-api/src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,27 @@ impl MassaRpcServer for API<Public> {
fee,
};

// check if fee is enough
if fee
.unwrap_or_default()
.checked_sub(self.0.api_settings.minimal_fees)
.is_none()
{
let result = ExecuteReadOnlyResponse {
executed_at: Slot::new(0, 0),
result: ReadOnlyResult::Error(format!(
"fee is too low provided: {} , minimal_fees required: {}",
fee.unwrap_or_default(),
self.0.api_settings.minimal_fees
)),
gas_cost: 0,
output_events: Default::default(),
state_changes: Default::default(),
};
res.push(result);
continue;
}

// run
let result = self.0.execution_controller.execute_readonly_request(req);

Expand Down Expand Up @@ -365,6 +386,26 @@ impl MassaRpcServer for API<Public> {
fee,
};

if fee
.unwrap_or_default()
.checked_sub(self.0.api_settings.minimal_fees)
.is_none()
{
let result = ExecuteReadOnlyResponse {
executed_at: Slot::new(0, 0),
result: ReadOnlyResult::Error(format!(
"fee is too low provided: {} , minimal_fees required: {}",
fee.unwrap_or_default(),
self.0.api_settings.minimal_fees
)),
gas_cost: 0,
output_events: Default::default(),
state_changes: Default::default(),
};
res.push(result);
continue;
}

// run
let result = self.0.execution_controller.execute_readonly_request(req);

Expand Down Expand Up @@ -520,6 +561,7 @@ impl MassaRpcServer for API<Public> {
config,
current_cycle,
chain_id: self.0.api_settings.chain_id,
minimal_fees: self.0.api_settings.minimal_fees,
})
}

Expand Down Expand Up @@ -1145,6 +1187,19 @@ impl MassaRpcServer for API<Public> {
.map(|op_input| check_input_operation(op_input, api_cfg, last_slot))
.map(|op| match op {
Ok(operation) => {
if operation
.content
.fee
.checked_sub(api_cfg.minimal_fees)
.is_none()
{
return Err(ApiError::BadRequest(format!(
"fee is too low provided: {} , minimal_fees required: {}",
operation.content.fee, self.0.api_settings.minimal_fees
))
.into());
}

let _verify_signature = match operation.verify_signature() {
Ok(()) => (),
Err(e) => return Err(ApiError::ModelsError(e).into()),
Expand All @@ -1154,6 +1209,7 @@ impl MassaRpcServer for API<Public> {
Err(e) => Err(e),
})
.collect::<RpcResult<Vec<SecureShareOperation>>>()?;

to_send.store_operations(verified_ops.clone());
let ids: Vec<OperationId> = verified_ops.iter().map(|op| op.id).collect();
cmd_sender.add_operations(to_send.clone());
Expand Down
3 changes: 3 additions & 0 deletions massa-api/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{collections::HashMap, net::SocketAddr};
use massa_api_exports::config::APIConfig;
use massa_consensus_exports::{ConsensusBroadcasts, MockConsensusController};
use massa_execution_exports::{GasCosts, MockExecutionController};
use massa_models::amount::Amount;
use massa_models::config::CHAINID;
use massa_models::{
config::{
Expand Down Expand Up @@ -68,6 +69,7 @@ pub(crate) fn get_apiv2_server(addr: &SocketAddr) -> (API<ApiV2>, APIConfig) {
last_start_period: 0,
chain_id: *CHAINID,
deferred_credits_delta: MassaTime::from_millis(24 * 3600 * 2),
minimal_fees: Amount::zero(),
};

// let shared_storage: massa_storage::Storage = massa_storage::Storage::create_root();
Expand Down Expand Up @@ -143,6 +145,7 @@ pub(crate) fn start_public_api(addr: SocketAddr) -> (API<Public>, APIConfig) {
last_start_period: 0,
chain_id: *CHAINID,
deferred_credits_delta: MassaTime::from_millis(24 * 3600 * 2),
minimal_fees: Amount::zero(),
};

let shared_storage: massa_storage::Storage = massa_storage::Storage::create_root();
Expand Down
63 changes: 63 additions & 0 deletions massa-api/src/tests/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,69 @@ async fn get_graph_interval() {
api_public_handle.stop().await;
}

#[tokio::test]
async fn send_operations_low_fee() {
let addr: SocketAddr = "[::]:5049".parse().unwrap();
let (mut api_public, mut config) = start_public_api(addr);

config.minimal_fees = Amount::from_str("0.01").unwrap();
api_public.0.api_settings.minimal_fees = Amount::from_str("0.01").unwrap();

let mut pool_ctrl = MockPoolController::new();
pool_ctrl.expect_clone_box().returning(|| {
let mut pool_ctrl = MockPoolController::new();
pool_ctrl.expect_add_operations().returning(|_a| ());
Box::new(pool_ctrl)
});

let mut protocol_ctrl = MockProtocolController::new();
protocol_ctrl.expect_clone_box().returning(|| {
let mut protocol_ctrl = MockProtocolController::new();
protocol_ctrl
.expect_propagate_operations()
.returning(|_a| Ok(()));
Box::new(protocol_ctrl)
});

api_public.0.protocol_controller = Box::new(protocol_ctrl);
api_public.0.pool_command_sender = Box::new(pool_ctrl);

let api_public_handle = api_public
.serve(&addr, &config)
.await
.expect("failed to start PUBLIC API");

let client = HttpClientBuilder::default()
.build(format!(
"http://localhost:{}",
addr.to_string().split(':').last().unwrap()
))
.unwrap();
let keypair = KeyPair::generate(0).unwrap();

// send transaction
let operation = create_operation_with_expire_period(&keypair, 500000);

let input: OperationInput = OperationInput {
creator_public_key: keypair.get_public_key(),
signature: operation.signature,
serialized_content: operation.serialized_data,
};

let response: Result<Vec<OperationId>, Error> = client
.request("send_operations", rpc_params![vec![input]])
.await;

let err = response.unwrap_err();

// op has low fee and should not be executed
assert!(err
.to_string()
.contains("Bad request: fee is too low provided: 0"));

api_public_handle.stop().await;
}

#[tokio::test]
async fn send_operations() {
let addr: SocketAddr = "[::]:5014".parse().unwrap();
Expand Down
23 changes: 17 additions & 6 deletions massa-client/src/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,16 +1360,27 @@ async fn send_operation(
addr: Address,
json: bool,
) -> Result<Box<dyn Output>> {
let cfg = match client.public.get_status().await {
let status = match client.public.get_status().await {
Ok(node_status) => node_status,
Err(e) => rpc_error!(e),
};

// check if the fee is higher than the minimal fees of the node
if fee.checked_sub(status.minimal_fees).is_none() {
bail!(format!(
"fee is too low provided: {} , minimal_fees required: {}",
fee, status.minimal_fees
));
}
.config;

let slot = get_current_latest_block_slot(cfg.thread_count, cfg.t0, cfg.genesis_timestamp)?
.unwrap_or_else(|| Slot::new(0, 0));
let mut expire_period = slot.period + cfg.operation_validity_periods;
if slot.thread >= addr.get_thread(cfg.thread_count) {
let slot = get_current_latest_block_slot(
status.config.thread_count,
status.config.t0,
status.config.genesis_timestamp,
)?
.unwrap_or_else(|| Slot::new(0, 0));
let mut expire_period = slot.period + status.config.operation_validity_periods;
if slot.thread >= addr.get_thread(status.config.thread_count) {
expire_period += 1;
};

Expand Down
3 changes: 3 additions & 0 deletions massa-grpc/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) 2023 MASSA LABS <info@massa.net>

use massa_models::amount::Amount;
use massa_signature::KeyPair;
use massa_time::MassaTime;
use serde::Deserialize;
Expand Down Expand Up @@ -133,6 +134,8 @@ pub struct GrpcConfig {
pub client_private_key_path: PathBuf,
/// chain id
pub chain_id: u64,
/// minimal fees
pub minimal_fees: Amount,
}

/// gRPC API configuration.
Expand Down
14 changes: 14 additions & 0 deletions massa-grpc/src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,19 @@ pub(crate) fn execute_read_only_call(
.transpose()?,
};

if read_only_call
.fee
.unwrap_or_default()
.checked_sub(grpc.grpc_config.minimal_fees)
.is_none()
{
return Err(GrpcError::InvalidArgument(format!(
"fee is too low provided: {} , minimal_fees required: {}",
read_only_call.fee.unwrap_or_default(),
grpc.grpc_config.minimal_fees
)));
}

let output = grpc
.execution_controller
.execute_readonly_request(read_only_call)?;
Expand Down Expand Up @@ -945,6 +958,7 @@ pub(crate) fn get_status(
final_state_fingerprint: state.final_state_fingerprint.to_string(),
config: Some(config.into()),
chain_id: grpc.grpc_config.chain_id,
minimal_fees: Some(grpc.grpc_config.minimal_fees.into()),
};

Ok(grpc_api::GetStatusResponse {
Expand Down
6 changes: 6 additions & 0 deletions massa-grpc/src/stream/send_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ pub(crate) async fn send_operations(
return Err(GrpcError::InvalidArgument("Operation expire_period is lower than the current period of this node. Your operation will never be included in a block.".into()));
}
}


if res_operation.content.fee.checked_sub(config.minimal_fees).is_none() {
return Err(GrpcError::InvalidArgument("Operation fee is lower than the minimal fee. Your operation will never be included in a block.".into()));
}

if rest.is_empty() {
res_operation.verify_signature()
.map(|_| (res_operation.id.to_string(), res_operation))
Expand Down
2 changes: 2 additions & 0 deletions massa-grpc/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::config::{GrpcConfig, ServiceName};
use crate::server::MassaPublicGrpc;
use massa_consensus_exports::{ConsensusBroadcasts, MockConsensusController};
use massa_execution_exports::{ExecutionChannels, MockExecutionController};
use massa_models::amount::Amount;
use massa_models::{
config::{
ENDORSEMENT_COUNT, MAX_DATASTORE_VALUE_LENGTH, MAX_DENUNCIATIONS_PER_BLOCK_HEADER,
Expand Down Expand Up @@ -109,6 +110,7 @@ pub(crate) fn grpc_public_service(addr: &SocketAddr) -> MassaPublicGrpc {
client_private_key_path: PathBuf::default(),
max_query_items_per_request: 50,
chain_id: *CHAINID,
minimal_fees: Amount::zero(),
};

let mip_stats_config = MipStatsConfig {
Expand Down
Loading

0 comments on commit 8b23f0a

Please sign in to comment.