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
3 changes: 0 additions & 3 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,9 +603,6 @@ Run a GraphQL service that exposes a faucet where users can claim tokens. This g
Default value: `8080`
* `--amount <AMOUNT>` — The number of tokens to send to each new chain
* `--limit-rate-until <LIMIT_RATE_UNTIL>` — The end timestamp: The faucet will rate-limit the token supply so it runs out of money no earlier than this
* `--max-chain-length <MAX_CHAIN_LENGTH>` — The maximum number of blocks in the faucet chain, before a new one is created

Default value: `500`
* `--listener-skip-process-inbox` — Do not create blocks automatically to receive incoming messages. Instead, wait for an explicit mutation `processInbox`
* `--listener-delay-before-ms <DELAY_BEFORE_MS>` — Wait before processing any notification (useful for testing)

Expand Down
1 change: 0 additions & 1 deletion Cargo.lock

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

1 change: 0 additions & 1 deletion linera-faucet/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ futures.workspace = true
linera-base.workspace = true
linera-client.workspace = true
linera-core.workspace = true
linera-execution.workspace = true
linera-storage.workspace = true
linera-version.workspace = true
serde.workspace = true
Expand Down
137 changes: 20 additions & 117 deletions linera-faucet/server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use axum::{Extension, Router};
use futures::lock::Mutex;
use linera_base::{
crypto::{CryptoHash, ValidatorPublicKey},
data_types::{Amount, ApplicationPermissions, ArithmeticError, BlockHeight, Timestamp},
data_types::{Amount, ApplicationPermissions, Timestamp},
identifiers::{AccountOwner, ChainId, MessageId},
ownership::ChainOwnership,
};
Expand All @@ -20,7 +20,6 @@ use linera_client::{
config::GenesisConfig,
};
use linera_core::data_types::ClientOutcome;
use linera_execution::SystemMessage;
use linera_storage::{Clock as _, Storage};
use serde::Deserialize;
use tower_http::cors::CorsLayer;
Expand All @@ -43,15 +42,14 @@ mod tests;
pub struct QueryRoot<C> {
context: Arc<Mutex<C>>,
genesis_config: Arc<GenesisConfig>,
chain_id: Arc<Mutex<ChainId>>,
chain_id: ChainId,
}

/// The root GraphQL mutation type.
pub struct MutationRoot<C> {
chain_id: Arc<Mutex<ChainId>>,
chain_id: ChainId,
context: Arc<Mutex<C>>,
amount: Amount,
end_block_height: BlockHeight,
end_timestamp: Timestamp,
start_timestamp: Timestamp,
start_balance: Amount,
Expand Down Expand Up @@ -91,8 +89,7 @@ where

/// Returns the current committee's validators.
async fn current_validators(&self) -> Result<Vec<Validator>, Error> {
let chain_id = *self.chain_id.lock().await;
let client = self.context.lock().await.make_chain_client(chain_id)?;
let client = self.context.lock().await.make_chain_client(self.chain_id)?;
let committee = client.local_committee().await?;
Ok(committee
.validators()
Expand Down Expand Up @@ -121,8 +118,7 @@ where
C: ClientContext,
{
async fn do_claim(&self, owner: AccountOwner) -> Result<ClaimOutcome, Error> {
let chain_id = *self.chain_id.lock().await;
let client = self.context.lock().await.make_chain_client(chain_id)?;
let client = self.context.lock().await.make_chain_client(self.chain_id)?;

if self.start_timestamp < self.end_timestamp {
let local_time = client.storage_client().clock().current_time();
Expand Down Expand Up @@ -153,32 +149,16 @@ where
.open_chain(ownership, ApplicationPermissions::default(), self.amount)
.await;
self.context.lock().await.update_wallet(&client).await?;
let (message_id, certificate) = result?.try_unwrap()?;

if client.next_block_height() >= self.end_block_height {
let key_pair = client.key_pair().await?;
let balance = client.local_balance().await?.try_sub(Amount::ONE)?;
let ownership = client.chain_state_view().await?.ownership().clone();
let (message_id, certificate) = client
.open_chain(ownership, ApplicationPermissions::default(), balance)
.await?
.try_unwrap()?;
// TODO(#1795): Move the remaining tokens to the new chain.
client.close_chain().await?.try_unwrap()?;
let chain_id = ChainId::child(message_id);
info!("Switching to a new faucet chain {chain_id:8}; remaining balance: {balance}");
self.context
.lock()
.await
.update_wallet_for_new_chain(
chain_id,
Some(key_pair),
certificate.block().header.timestamp,
)
.await?;
*self.chain_id.lock().await = chain_id;
}

let (message_id, certificate) = match result? {
ClientOutcome::Committed(result) => result,
ClientOutcome::WaitForTimeout(timeout) => {
return Err(Error::new(format!(
"This faucet is using a multi-owner chain and is not the leader right now. \
Try again at {}",
timeout.timestamp,
)));
}
};
let chain_id = ChainId::child(message_id);
Ok(ClaimOutcome {
message_id,
Expand All @@ -205,15 +185,14 @@ pub struct FaucetService<C>
where
C: ClientContext,
{
chain_id: Arc<Mutex<ChainId>>,
chain_id: ChainId,
context: Arc<Mutex<C>>,
genesis_config: Arc<GenesisConfig>,
config: ChainListenerConfig,
storage: C::Storage,
port: NonZeroU16,
amount: Amount,
end_timestamp: Timestamp,
end_block_height: BlockHeight,
start_timestamp: Timestamp,
start_balance: Amount,
}
Expand All @@ -224,14 +203,13 @@ where
{
fn clone(&self) -> Self {
Self {
chain_id: self.chain_id.clone(),
chain_id: self.chain_id,
context: Arc::clone(&self.context),
genesis_config: Arc::clone(&self.genesis_config),
config: self.config.clone(),
storage: self.storage.clone(),
port: self.port,
amount: self.amount,
end_block_height: self.end_block_height,
end_timestamp: self.end_timestamp,
start_timestamp: self.start_timestamp,
start_balance: self.start_balance,
Expand All @@ -250,7 +228,6 @@ where
chain_id: ChainId,
context: C,
amount: Amount,
end_block_height: BlockHeight,
end_timestamp: Timestamp,
genesis_config: Arc<GenesisConfig>,
config: ChainListenerConfig,
Expand All @@ -262,14 +239,13 @@ where
client.process_inbox().await?;
let start_balance = client.local_balance().await?;
Ok(Self {
chain_id: Arc::new(Mutex::new(chain_id)),
chain_id,
context,
genesis_config,
config,
storage,
port,
amount,
end_block_height,
end_timestamp,
start_timestamp,
start_balance,
Expand All @@ -278,27 +254,24 @@ where

pub fn schema(&self) -> Schema<QueryRoot<C>, MutationRoot<C>, EmptySubscription> {
let mutation_root = MutationRoot {
chain_id: self.chain_id.clone(),
chain_id: self.chain_id,
context: Arc::clone(&self.context),
amount: self.amount,
end_block_height: self.end_block_height,
end_timestamp: self.end_timestamp,
start_timestamp: self.start_timestamp,
start_balance: self.start_balance,
};
let query_root = QueryRoot {
genesis_config: Arc::clone(&self.genesis_config),
context: Arc::clone(&self.context),
chain_id: self.chain_id.clone(),
chain_id: self.chain_id,
};
Schema::build(query_root, mutation_root, EmptySubscription).finish()
}

/// Runs the faucet.
#[tracing::instrument(name = "FaucetService::run", skip_all, fields(port = self.port, chain_id = ?self.chain_id))]
pub async fn run(self) -> anyhow::Result<()> {
self.find_current_chain().await?;

let port = self.port.get();
let index_handler = axum::routing::get(graphiql).post(Self::index_handler);

Expand Down Expand Up @@ -329,74 +302,4 @@ where
let schema = service.0.schema();
schema.execute(request.into_inner()).await.into()
}

/// Finds the current chain ID, even if the configured chain has been closed and the tokens
/// moved to a new one.
async fn find_current_chain(&self) -> anyhow::Result<()> {
let mut chain_id = *self.chain_id.lock().await;
let mut client = self.context.lock().await.make_chain_client(chain_id)?;
client.synchronize_from_validators().await?;
loop {
let chain = client.chain_state_view().await?;
if !chain.execution_state.system.closed.get() {
break; // This is the current chain; it has not been closed yet.
}
// The faucet closes each chain in the last block; the next-to-last block opens the
// new chain.
let index = chain
.confirmed_log
.count()
.checked_sub(2)
.ok_or(ArithmeticError::Underflow)?;
let hash = chain
.confirmed_log
.get(index)
.await?
.ok_or_else(|| anyhow::anyhow!("Missing confirmed_log entry"))?;
let certificate = client.storage_client().read_certificate(hash).await?;
chain_id = certificate
.block()
.messages()
.iter()
.flatten()
.find_map(|message| {
if let linera_execution::Message::System(SystemMessage::OpenChain(_)) =
&message.message
{
Some(message.destination.recipient()?)
} else {
None
}
})
.ok_or_else(|| anyhow::anyhow!("No OpenChain message found"))?;
client = self.context.lock().await.make_chain_client(chain_id)?;
client.synchronize_from_validators().await?;
}
*self.chain_id.lock().await = chain_id;
Ok(())
}
}

trait ClientOutcomeExt {
type Output;

/// Returns the committed result or an error if we are not the leader.
///
/// It is recommended to use single-owner chains for the faucet to avoid this error.
fn try_unwrap(self) -> Result<Self::Output, Error>;
}

impl<T> ClientOutcomeExt for ClientOutcome<T> {
type Output = T;

fn try_unwrap(self) -> Result<Self::Output, Error> {
match self {
ClientOutcome::Committed(result) => Ok(result),
ClientOutcome::WaitForTimeout(timeout) => Err(Error::new(format!(
"This faucet is using a multi-owner chain and is not the leader right now. \
Try again at {}",
timeout.timestamp,
))),
}
}
}
5 changes: 2 additions & 3 deletions linera-faucet/server/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use async_trait::async_trait;
use futures::lock::Mutex;
use linera_base::{
crypto::{AccountPublicKey, AccountSecretKey},
data_types::{Amount, BlockHeight, Timestamp},
data_types::{Amount, Timestamp},
identifiers::ChainId,
};
use linera_client::{chain_listener, wallet::Wallet};
Expand Down Expand Up @@ -83,10 +83,9 @@ async fn test_faucet_rate_limiting() {
};
let context = Arc::new(Mutex::new(context));
let root = MutationRoot {
chain_id: Arc::new(Mutex::new(chain_id)),
chain_id,
context: context.clone(),
amount: Amount::from_tokens(1),
end_block_height: BlockHeight::from(10),
end_timestamp: Timestamp::from(6000),
start_timestamp: Timestamp::from(0),
start_balance: Amount::from_tokens(6),
Expand Down
13 changes: 3 additions & 10 deletions linera-service/src/cli_wrappers/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,22 +517,15 @@ impl ClientWrapper {
port: impl Into<Option<u16>>,
chain_id: ChainId,
amount: Amount,
max_chain_length: Option<u64>,
) -> Result<FaucetService> {
let port = port.into().unwrap_or(8080);
let mut command = self.command().await?;
command
let child = command
.arg("faucet")
.arg(chain_id.to_string())
.args(["--port".to_string(), port.to_string()])
.args(["--amount".to_string(), amount.to_string()]);
if let Some(max_chain_length) = max_chain_length {
command.args([
"--max-chain-length".to_string(),
max_chain_length.to_string(),
]);
}
let child = command.spawn_into()?;
.args(["--amount".to_string(), amount.to_string()])
.spawn_into()?;
let client = reqwest_client();
for i in 0..10 {
linera_base::time::timer::sleep(Duration::from_secs(i)).await;
Expand Down
4 changes: 0 additions & 4 deletions linera-service/src/linera/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,10 +614,6 @@ pub enum ClientCommand {
#[arg(long)]
limit_rate_until: Option<DateTime<Utc>>,

/// The maximum number of blocks in the faucet chain, before a new one is created.
#[arg(long, default_value = "500")]
max_chain_length: u64,

/// Configuration for the faucet chain listener.
#[command(flatten)]
config: ChainListenerConfig,
Expand Down
4 changes: 1 addition & 3 deletions linera-service/src/linera/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use command::{ClientCommand, DatabaseToolCommand, NetCommand, ProjectCommand, Wa
use futures::{lock::Mutex, FutureExt as _, StreamExt};
use linera_base::{
crypto::{AccountSecretKey, CryptoHash, CryptoRng, Ed25519SecretKey},
data_types::{ApplicationPermissions, BlockHeight, Timestamp},
data_types::{ApplicationPermissions, Timestamp},
identifiers::{AccountOwner, ChainDescription, ChainId},
ownership::ChainOwnership,
};
Expand Down Expand Up @@ -849,7 +849,6 @@ impl Runnable for Job {
port,
amount,
limit_rate_until,
max_chain_length,
config,
} => {
let chain_id = chain_id.unwrap_or_else(|| context.default_chain());
Expand All @@ -867,7 +866,6 @@ impl Runnable for Job {
chain_id,
context,
amount,
BlockHeight(max_chain_length),
end_timestamp,
genesis_config,
config,
Expand Down
2 changes: 1 addition & 1 deletion linera-service/src/linera/net_up_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ async fn print_messages_and_create_faucet(
ChainId::root(1)
};
let service = client
.run_faucet(Some(faucet_port.into()), faucet_chain, faucet_amount, None)
.run_faucet(Some(faucet_port.into()), faucet_chain, faucet_amount)
.await?;
Some(service)
} else {
Expand Down
Loading
Loading