Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auctioneer): add auctioneer binary #1556

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
600 changes: 317 additions & 283 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
exclude = ["tools/protobuf-compiler", "tools/solidity-compiler"]

members = [
"crates/astria-auctioneer",
"crates/astria-bridge-contracts",
"crates/astria-bridge-withdrawer",
"crates/astria-build-info",
Expand All @@ -26,6 +27,7 @@ members = [
# Specify default members so that cargo invocations in github actions will
# not act on lints
default-members = [
"crates/astria-auctioneer",
"crates/astria-bridge-contracts",
"crates/astria-bridge-withdrawer",
"crates/astria-build-info",
Expand Down
65 changes: 65 additions & 0 deletions crates/astria-auctioneer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[package]
name = "astria-auctioneer"
version = "0.0.1"
edition = "2021"
rust-version = "1.76"
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/astriaorg/astria"
homepage = "https://astria.org"

[[bin]]
name = "astria-auctioneer"

[dependencies]
astria-build-info = { path = "../astria-build-info", features = ["runtime"] }
astria-core = { path = "../astria-core", features = ["serde", "client"] }
astria-eyre = { path = "../astria-eyre" }
config = { package = "astria-config", path = "../astria-config" }
sequencer_client = { package = "astria-sequencer-client", path = "../astria-sequencer-client" }
telemetry = { package = "astria-telemetry", path = "../astria-telemetry", features = [
"display",
] }

async-trait = { workspace = true }
axum = { workspace = true }
bytes = { workspace = true }
futures = { workspace = true }
hex = { workspace = true }
humantime = { workspace = true }
itertools = { workspace = true }
pbjson-types = { workspace = true }
pin-project-lite = { workspace = true }
prost = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = [
"macros",
"rt-multi-thread",
"sync",
"time",
"signal",
] }
tokio-util = { workspace = true, features = ["rt"] }
tracing = { workspace = true, features = ["attributes"] }
tryhard = { workspace = true }
tonic = { workspace = true }
tokio-stream = { workspace = true, features = ["net"] }

[dev-dependencies]
astria-core = { path = "../astria-core", features = ["client"] }
config = { package = "astria-config", path = "../astria-config", features = [
"tests",
] }
insta = { workspace = true, features = ["json"] }
tempfile = { workspace = true }
test_utils = { package = "astria-test-utils", path = "../astria-test-utils", features = [
"geth",
] }
tokio-test = { workspace = true }
wiremock = { workspace = true }

[build-dependencies]
astria-build-info = { path = "../astria-build-info", features = ["build"] }
35 changes: 35 additions & 0 deletions crates/astria-auctioneer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Astria Auctioneer

TODO: Add a description of the binary.

## Running The Auctioneer

### Dependencies

We use [just](https://just.systems/man/en/chapter_4.html) for convenient project
specific commands.

### Configuration

The Auctioneer is configured via environment variables. An example configuration
can be seen in `local.env.example`.

To copy a configuration to your `.env` file run:

```sh

# Can specify an environment
just copy-env <ENVIRONMENT>

# By default will copy `local.env.example`
just copy-env
```

### Running locally

After creating a `.env` file either manually or by copying as above, `just` will
load it and run locally:

```bash
just run
```
4 changes: 4 additions & 0 deletions crates/astria-auctioneer/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
astria_build_info::emit("auctioneer-v")?;
Ok(())
}
12 changes: 12 additions & 0 deletions crates/astria-auctioneer/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
default:
@just --list

set dotenv-load
set fallback

default_env := 'local'
copy-env type=default_env:
cp {{ type }}.env.example .env

run:
cargo run
75 changes: 75 additions & 0 deletions crates/astria-auctioneer/local.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Configuration options of Astria Auctioneer.

# Address of the gRPC server for the sequencer chain
ASTRIA_AUCTIONEER_SEQUENCER_GRPC_ENDPOINT="http://127.0.0.1:8080"

# Address of the ABCI server for the sequencer chain
ASTRIA_AUCTIONEER_SEQUENCER_ABCI_ENDPOINT="http://127.0.0.1:26657"

# Chain ID of the sequencer chain which transactions are submitted to.
ASTRIA_AUCTIONEER_SEQUENCER_CHAIN_ID="astria-dev-1"

# The path to the file storing the private key for the sequencer account used for signing
# transactions. The file should contain a hex-encoded Ed25519 secret key.
ASTRIA_AUCTIONEER_SEQUENCER_PRIVATE_KEY_PATH=/path/to/priv_sequencer_key.json

# The fee asset denomination that will be used in the submitted sequencer transactions.
ASTRIA_AUCTIONEER_FEE_ASSET_DENOMINATION="nria"

# The prefix that will be used to construct bech32m sequencer addresses.
ASTRIA_AUCTIONEER_SEQUENCER_ADDRESS_PREFIX=astria

# Address of the gRPC server for the rollup's Optimistic Execution and Bundle services
ASTRIA_AUCTIONEER_ROLLUP_GRPC_ENDPOINT="http://127.0.0.1:50051"

# The rollup ID to post the auction result to
ASTRIA_AUCTIONEER_ROLLUP_ID="astriachain"

# The amount of time in miliseconds to wait between opening the auction and closing it to
# submit the result to the sequencer.
ASTRIA_AUCTIONEER_LATENCY_MARGIN_MS=1000

# Log level. One of debug, info, warn, or error
ASTRIA_AUCTIONEER_LOG="astria_auctioneer=info"

# If true disables tty detection and forces writing telemetry to stdout.
# If false span data is written to stdout only if it is connected to a tty.
ASTRIA_AUCTIONEER_FORCE_STDOUT=false

# If true uses an exceedingly pretty human readable format to write to stdout.
# If false uses JSON formatted OTEL traces.
# This does nothing unless stdout is connected to a tty or
# `ASTRIA_AUCTIONEER_FORCE_STDOUT` is set to `true`.
ASTRIA_AUCTIONEER_PRETTY_PRINT=false

# If set to any non-empty value removes ANSI escape characters from the pretty
# printed output. Note that this does nothing unless `ASTRIA_AUCTIONEER_PRETTY_PRINT`
# is set to `true`.
NO_COLOR=

# Set to true to enable prometheus metrics.
ASTRIA_AUCTIONEER_NO_METRICS=true

# The address at which the prometheus HTTP listener will bind if enabled.
ASTRIA_AUCTIONEER_METRICS_HTTP_LISTENER_ADDR="127.0.0.1:9000"

# If true disables writing to the opentelemetry OTLP endpoint.
ASTRIA_AUCTIONEER_NO_OTEL=false

# The OTEL specific config options follow the OpenTelemetry Protocol Exporter v1
# specification as defined here:
# https://github.com/open-telemetry/opentelemetry-specification/blob/e94af89e3d0c01de30127a0f423e912f6cda7bed/specification/protocol/exporter.md

# Sets the general OTLP endpoint.
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
# Sets the OTLP endpoint for trace data. This takes precedence over `OTEL_EXPORTER_OTLP_ENDPOINT` if set.
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://localhost:4317/v1/traces"
# The duration in seconds that the OTEL exporter will wait for each batch export.
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT=10
# The compression format to use for exporting. Only `"gzip"` is supported.
# Don't set the env var if no compression is required.
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION="gzip"
# The HTTP headers that will be set when sending gRPC requests.
OTEL_EXPORTER_OTLP_HEADERS="key1=value1,key2=value2"
# The HTTP headers that will be set when sending gRPC requests. This takes precedence over `OTEL_EXPORTER_OTLP_HEADERS` if set.
OTEL_EXPORTER_OTLP_TRACE_HEADERS="key1=value1,key2=value2"
32 changes: 32 additions & 0 deletions crates/astria-auctioneer/src/auction/allocation_rule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! The allocation rule is the mechanism by which the auction processes incoming bids and determines
//! the winner.
use super::Bundle;

pub(super) struct FirstPrice {
highest_bid: Option<Bundle>,
}

impl FirstPrice {
pub(super) fn new() -> Self {
Self {
highest_bid: None,
}
}

/// Submit a bundle with a bid.
///
/// Returns `true` if the bid is accepted as the highest bid.
pub(crate) fn bid(&mut self, bundle: Bundle) -> bool {
if bundle.bid() > self.highest_bid.as_ref().map_or(0, Bundle::bid) {
self.highest_bid = Some(bundle);
true
} else {
false
}
}

/// Returns the winner of the auction, if one exists.
pub(crate) fn winner(self) -> Option<Bundle> {
self.highest_bid
}
}
86 changes: 86 additions & 0 deletions crates/astria-auctioneer/src/auction/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::time::Duration;

use astria_core::{
generated::sequencerblock::v1::sequencer_service_client::SequencerServiceClient,
primitive::v1::{
asset,
RollupId,
},
};
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;

use super::{
Auction,
Handle,
Id,
SequencerKey,
};
use crate::Metrics;

pub(crate) struct Builder {
pub(crate) metrics: &'static Metrics,
pub(crate) shutdown_token: CancellationToken,

/// The endpoint for the sequencer gRPC service used to get pending nonces
pub(crate) sequencer_grpc_client: SequencerServiceClient<tonic::transport::Channel>,
/// The endpoint for the sequencer ABCI service used to submit transactions
pub(crate) sequencer_abci_client: sequencer_client::HttpClient,
/// The amount of time to wait after a commit before closing the auction for bids and
/// submitting the resulting transaction
pub(crate) latency_margin: Duration,
/// The ID of the auction to be run
pub(crate) auction_id: Id,
/// The key used to sign sequencer transactions
pub(crate) sequencer_key: SequencerKey,
/// The denomination of the fee asset used in the sequencer transactions
pub(crate) fee_asset_denomination: asset::Denom,
/// The chain ID used for sequencer transactions
pub(crate) sequencer_chain_id: String,
/// The rollup ID used for `RollupDataSubmission` with the auction result
pub(crate) rollup_id: RollupId,
}

impl Builder {
pub(crate) fn build(self) -> (Handle, Auction) {
let Self {
metrics,
shutdown_token,
sequencer_grpc_client,
sequencer_abci_client,
latency_margin,
auction_id,
fee_asset_denomination,
rollup_id,
sequencer_key,
sequencer_chain_id,
} = self;

// TODO: get the capacities from config or something instead of using a magic number
let (commands_tx, commands_rx) = mpsc::channel(16);
let (new_bundles_tx, new_bundles_rx) = mpsc::channel(16);

let auction = Auction {
metrics,
shutdown_token,
sequencer_grpc_client,
sequencer_abci_client,
commands_rx,
new_bundles_rx,
latency_margin,
id: auction_id,
sequencer_key,
fee_asset_denomination,
sequencer_chain_id,
rollup_id,
};

(
Handle {
commands_tx,
new_bundles_tx,
},
auction,
)
}
}
Loading
Loading