Skip to content

Commit

Permalink
Creating telos-reth binary which uses TelosNode from the telos crate
Browse files Browse the repository at this point in the history
  • Loading branch information
poplexity committed Aug 7, 2024
1 parent e24e4c7 commit 88f7d8b
Show file tree
Hide file tree
Showing 12 changed files with 928 additions and 52 deletions.
452 changes: 401 additions & 51 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ members = [
"crates/storage/provider/",
"crates/storage/storage-api/",
"crates/tasks/",
"crates/telos/node",
"crates/telos/rpc",
"crates/tokio-util/",
"crates/tracing/",
"crates/transaction-pool/",
Expand Down Expand Up @@ -540,3 +542,8 @@ serial_test = "3"
similar-asserts = "1.5.0"
tempfile = "3.8"
test-fuzz = "5"

# telos
reth-node-telos = { path = "crates/telos/node" }
reth-telos-rpc = { path = "crates/telos/rpc" }
antelope-client = { git = "https://github.com/telosnetwork/antelope-rs", branch = "finish_table_rows_params" }
19 changes: 19 additions & 0 deletions bin/reth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ itertools.workspace = true
# p2p
discv5.workspace = true

# telos
reth-node-telos = { workspace = true, optional = true }
reth-telos-rpc = { workspace = true, optional = true }
antelope-client = { workspace = true, optional = true }

[target.'cfg(unix)'.dependencies]
tikv-jemallocator = { version = "0.5.0", optional = true }
libc = "0.2"
Expand Down Expand Up @@ -148,6 +153,15 @@ optimism = [
# no-op feature flag for switching between the `optimism` and default functionality in CI matrices
ethereum = []

telos = [
"dep:antelope-client",
"dep:reth-node-telos",
"dep:reth-telos-rpc",
# "reth-rpc/telos",
# "reth-network/telos",
# "reth-node-core/telos",
]

[[bin]]
name = "reth"
path = "src/main.rs"
Expand All @@ -160,3 +174,8 @@ path = "src/engine2.rs"
name = "op-reth"
path = "src/optimism.rs"
required-features = ["optimism"]

[[bin]]
name = "telos-reth"
path = "src/telos.rs"
required-features = ["telos"]
2 changes: 1 addition & 1 deletion bin/reth/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
#[cfg(all(feature = "optimism", not(test)))]
compile_error!("Cannot build the `reth` binary with the `optimism` feature flag enabled. Did you mean to build `op-reth`?");

#[cfg(not(feature = "optimism"))]
#[cfg(all(not(feature = "optimism"), not(feature = "telos")))]
fn main() {
use reth::cli::Cli;
use reth_node_ethereum::EthereumNode;
Expand Down
50 changes: 50 additions & 0 deletions bin/reth/src/telos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#![allow(missing_docs)]

use std::sync::Arc;
use clap::Parser;
use reth::cli::Cli;
use reth_node_telos::TelosArgs;
use reth_node_telos::TelosNode;
use reth_telos_rpc::TelosClient;

// We use jemalloc for performance reasons.
#[cfg(all(feature = "jemalloc", unix))]
#[global_allocator]
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;

#[cfg(all(feature = "optimism", not(test)))]
compile_error!("Cannot build the `telos-reth` binary with the `optimism` feature flag enabled. Did you mean to build `op-reth`?");

#[cfg(feature = "telos")]
fn main() {
use reth::cli::Cli;

reth_cli_util::sigsegv_handler::install();

// Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided.
if std::env::var_os("RUST_BACKTRACE").is_none() {
std::env::set_var("RUST_BACKTRACE", "1");
}

if let Err(err) = Cli::<TelosArgs>::parse().run(|builder, telos_args| async move {
let handle = builder
.node(TelosNode::new(telos_args.clone()))
.extend_rpc_modules(move |ctx| {
// register sequencer tx forwarder
if telos_args.telos_endpoint.is_some() {
ctx.registry.set_eth_raw_transaction_forwarder(Arc::new(TelosClient::new(
telos_args,
)));
}

Ok(())
})
.launch()
.await?;

handle.node_exit_future.await
}) {
eprintln!("Error: {err:?}");
std::process::exit(1);
}
}
36 changes: 36 additions & 0 deletions crates/telos/node/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "reth-node-telos"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
antelope-client.workspace = true
reth-primitives.workspace = true

reth-auto-seal-consensus.workspace = true
reth-basic-payload-builder.workspace = true
reth-beacon-consensus.workspace = true
reth-ethereum-engine-primitives.workspace = true
reth-evm-ethereum.workspace = true
reth-network.workspace = true
reth-node-api.workspace = true
reth-node-builder.workspace = true
reth-node-ethereum.workspace = true
reth-payload-builder.workspace = true
reth-provider.workspace = true
reth-rpc.workspace = true
reth-tracing.workspace = true
reth-transaction-pool.workspace = true

clap.workspace = true
serde = { workspace = true, features = ["derive"] }

[features]
telos = []
47 changes: 47 additions & 0 deletions crates/telos/node/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! clap [Args](clap::Args) for telos configuration
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Default, PartialEq, Eq, clap::Args)]
#[clap(next_help_heading = "Telos")]
pub struct TelosArgs {
/// TelosZero endpoint to use for API calls (send_transaction, get gas price from table)
#[arg(long = "telos.telos_endpoint", value_name = "HTTP_URL")]
pub telos_endpoint: Option<String>,

/// Signer account name
#[arg(long = "telos.signer_account")]
pub signer_account: Option<String>,

/// Signer permission name
#[arg(long = "telos.signer_permission")]
pub signer_permission: Option<String>,

/// Signer private key
#[arg(long = "telos.signer_key")]
pub signer_key: Option<String>,

/// Seconds to cache gas price
#[arg(long = "telos.gas_cache_seconds")]
pub gas_cache_seconds: Option<u32>,
}

#[cfg(test)]
mod tests {
use super::*;
use clap::{Args, Parser};

/// A helper type to parse Args more easily
#[derive(Parser)]
struct CommandParser<T: Args> {
#[clap(flatten)]
args: T,
}

#[test]
fn test_parse_database_args() {
let default_args = TelosArgs::default();
let args = CommandParser::<TelosArgs>::parse_from(["reth"]).args;
assert_eq!(args, default_args);
}
}
166 changes: 166 additions & 0 deletions crates/telos/node/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//! Standalone crate for Telos-specific Reth configuration and builder types.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/telosnetwork/telos-reth/issues/"
)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

#![cfg(feature = "telos")]

use std::fmt::{Debug, Formatter};
use antelope::api::client::{APIClient, DefaultProvider};
use antelope::api::v1::structs::GetTableRowsParams;
use antelope::chain::action::{Action, PermissionLevel};
use antelope::chain::checksum::{Checksum160, Checksum256};
use antelope::chain::name::Name;
use antelope::chain::private_key::PrivateKey;
use antelope::chain::transaction::{SignedTransaction, Transaction};
use antelope::serializer::{Decoder, Encoder, Packer};
use antelope::{name, StructPacker};
use reth_primitives::{TransactionSigned, U256};
use std::time::{Duration, Instant};

pub mod args;
pub mod node;

pub use crate::args::TelosArgs;
pub use crate::node::TelosNode;


/// Telos Network Config
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TelosNetworkConfig {
pub api_client: APIClient<DefaultProvider>,
pub signer_account: Name,
pub signer_permission: Name,
pub signer_key: PrivateKey,
pub gas_cache: GasPriceCache,
}

#[derive(StructPacker)]
pub struct RawActionData {
pub ram_payer: Name,
pub tx: Vec<u8>,
pub estimate_gas: bool,
pub sender: Option<Checksum160>,
}

#[derive(Clone)]
pub struct GasPriceCache {
api_client: Box<APIClient<DefaultProvider>>,
gas_cache_duration: Duration,
value: Option<(U256, Instant)>,
}

impl Debug for GasPriceCache {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "GasPriceCache duration: ")
}
}

impl Default for GasPriceCache {
fn default() -> Self {
GasPriceCache {
api_client: Box::new(APIClient::<DefaultProvider>::default_provider("https://example.com".into()).unwrap()),
gas_cache_duration: Duration::default(),
value: None
}
}
}

#[derive(StructPacker, Default)]
struct TelosEVMConfig {
trx_index: u32,
last_block: u32,
gas_used_block: Checksum256,
gas_price: Checksum256,
revision: Option<u32>,
}

impl GasPriceCache {
pub fn new(api_client: Box<APIClient<DefaultProvider>>, gas_cache_duration: Duration) -> Self {
GasPriceCache { api_client, gas_cache_duration, value: None }
}

async fn load_value(&self) -> U256 {
let table_rows_params = GetTableRowsParams {
code: name!("eosio.evm"),
table: name!("config"),
scope: Some(name!("eosio.evm")),
lower_bound: None,
upper_bound: None,
limit: Some(1),
reverse: None,
index_position: None,
show_payer: None,
};
let config_result =
self.api_client.v1_chain.get_table_rows::<TelosEVMConfig>(table_rows_params).await.unwrap();

return U256::from_be_slice(&config_result.rows[0].gas_price.data);
}

pub async fn get(&mut self) -> &U256 {
let now = Instant::now();
if self.value.as_ref().map_or(true, |&(_, ref expiry)| *expiry <= now) {
let new_val = self.load_value(); // Call the hardcoded loader function
self.value = Some((new_val.await, now + self.gas_cache_duration));
}
&self.value.as_ref().unwrap().0
}
}

pub async fn send_to_telos(
network_config: &TelosNetworkConfig,
trxs: &Vec<TransactionSigned>,
) -> Result<String, String> {
let get_info = network_config.api_client.v1_chain.get_info().await.unwrap();
let trx_header = get_info.get_transaction_header(90);
let _trxs_results = trxs.iter().map(|trx| {
let trx_header = trx_header.clone();
async move {
let mut trx_bytes = Vec::new();
trx.encode_enveloped(&mut trx_bytes);

let raw_action_data = RawActionData {
ram_payer: name!("eosio.evm"),
tx: trx_bytes,
estimate_gas: false,
sender: None,
};

let action = Action::new_ex(
name!("eosio.evm"),
name!("raw"),
vec![PermissionLevel::new(
network_config.signer_account,
network_config.signer_permission,
)],
raw_action_data,
);

let transaction = Transaction {
header: trx_header,
context_free_actions: vec![],
actions: vec![action],
extension: vec![],
};

let signed_telos_transaction = SignedTransaction {
transaction: transaction.clone(),
signatures: vec![network_config
.signer_key
.sign_message(&transaction.signing_data(&get_info.chain_id.data.to_vec()))],
context_free_data: vec![],
};

let result = network_config.api_client.v1_chain.send_transaction(signed_telos_transaction);

result.await.unwrap().transaction_id
}
});
Ok("Good".into())
}
Loading

0 comments on commit 88f7d8b

Please sign in to comment.