mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-13 06:14:44 +00:00
Have the TransactionPublisher build a TxLegacy from Transaction
This commit is contained in:
parent
98c3f75fa2
commit
a717ae9ea7
6 changed files with 75 additions and 33 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -8334,6 +8334,7 @@ dependencies = [
|
||||||
"alloy-rpc-client",
|
"alloy-rpc-client",
|
||||||
"alloy-rpc-types-eth",
|
"alloy-rpc-types-eth",
|
||||||
"alloy-simple-request-transport",
|
"alloy-simple-request-transport",
|
||||||
|
"alloy-transport",
|
||||||
"borsh",
|
"borsh",
|
||||||
"ciphersuite",
|
"ciphersuite",
|
||||||
"const-hex",
|
"const-hex",
|
||||||
|
|
|
@ -35,6 +35,7 @@ alloy-rlp = { version = "0.3", default-features = false }
|
||||||
alloy-consensus = { version = "0.3", default-features = false }
|
alloy-consensus = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
alloy-rpc-types-eth = { version = "0.3", default-features = false }
|
alloy-rpc-types-eth = { version = "0.3", default-features = false }
|
||||||
|
alloy-transport = { version = "0.3", default-features = false }
|
||||||
alloy-simple-request-transport = { path = "../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
alloy-simple-request-transport = { path = "../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||||
alloy-rpc-client = { version = "0.3", default-features = false }
|
alloy-rpc-client = { version = "0.3", default-features = false }
|
||||||
alloy-provider = { version = "0.3", default-features = false }
|
alloy-provider = { version = "0.3", default-features = false }
|
||||||
|
|
|
@ -30,14 +30,13 @@ use publisher::TransactionPublisher;
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let db = bin::init();
|
let db = bin::init();
|
||||||
let feed = {
|
|
||||||
let provider = Arc::new(RootProvider::new(
|
let provider = Arc::new(RootProvider::new(
|
||||||
ClientBuilder::default().transport(SimpleRequest::new(bin::url()), true),
|
ClientBuilder::default().transport(SimpleRequest::new(bin::url()), true),
|
||||||
));
|
));
|
||||||
Rpc { provider }
|
|
||||||
};
|
|
||||||
let chain_id = loop {
|
let chain_id = loop {
|
||||||
match feed.provider.get_chain_id().await {
|
match provider.get_chain_id().await {
|
||||||
Ok(chain_id) => break U256::try_from(chain_id).unwrap(),
|
Ok(chain_id) => break U256::try_from(chain_id).unwrap(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("couldn't connect to the Ethereum node for the chain ID: {e:?}");
|
log::error!("couldn't connect to the Ethereum node for the chain ID: {e:?}");
|
||||||
|
@ -48,9 +47,9 @@ async fn main() {
|
||||||
|
|
||||||
bin::main_loop::<_, KeyGenParams, _>(
|
bin::main_loop::<_, KeyGenParams, _>(
|
||||||
db,
|
db,
|
||||||
feed.clone(),
|
Rpc { provider: provider.clone() },
|
||||||
Scheduler::new(SmartContract { chain_id }),
|
Scheduler::new(SmartContract { chain_id }),
|
||||||
TransactionPublisher::new({
|
TransactionPublisher::new(provider, {
|
||||||
let relayer_hostname = env::var("ETHEREUM_RELAYER_HOSTNAME")
|
let relayer_hostname = env::var("ETHEREUM_RELAYER_HOSTNAME")
|
||||||
.expect("ethereum relayer hostname wasn't specified")
|
.expect("ethereum relayer hostname wasn't specified")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use ciphersuite::{Ciphersuite, Secp256k1};
|
use ciphersuite::{group::GroupEncoding, Ciphersuite, Secp256k1};
|
||||||
|
|
||||||
use alloy_core::primitives::U256;
|
use alloy_core::primitives::U256;
|
||||||
|
|
||||||
|
@ -59,7 +59,10 @@ impl AsMut<[u8]> for OutputId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub(crate) struct Output(pub(crate) EthereumInInstruction);
|
pub(crate) struct Output {
|
||||||
|
pub(crate) key: <Secp256k1 as Ciphersuite>::G,
|
||||||
|
pub(crate) instruction: EthereumInInstruction,
|
||||||
|
}
|
||||||
impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
||||||
type Id = OutputId;
|
type Id = OutputId;
|
||||||
type TransactionId = [u8; 32];
|
type TransactionId = [u8; 32];
|
||||||
|
@ -71,40 +74,43 @@ impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
||||||
|
|
||||||
fn id(&self) -> Self::Id {
|
fn id(&self) -> Self::Id {
|
||||||
let mut id = [0; 40];
|
let mut id = [0; 40];
|
||||||
id[.. 32].copy_from_slice(&self.0.id.0);
|
id[.. 32].copy_from_slice(&self.instruction.id.0);
|
||||||
id[32 ..].copy_from_slice(&self.0.id.1.to_le_bytes());
|
id[32 ..].copy_from_slice(&self.instruction.id.1.to_le_bytes());
|
||||||
OutputId(id)
|
OutputId(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_id(&self) -> Self::TransactionId {
|
fn transaction_id(&self) -> Self::TransactionId {
|
||||||
self.0.id.0
|
self.instruction.id.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key(&self) -> <Secp256k1 as Ciphersuite>::G {
|
fn key(&self) -> <Secp256k1 as Ciphersuite>::G {
|
||||||
todo!("TODO")
|
self.key
|
||||||
}
|
}
|
||||||
|
|
||||||
fn presumed_origin(&self) -> Option<Address> {
|
fn presumed_origin(&self) -> Option<Address> {
|
||||||
Some(Address::from(self.0.from))
|
Some(Address::from(self.instruction.from))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self) -> Balance {
|
fn balance(&self) -> Balance {
|
||||||
let coin = coin_to_serai_coin(&self.0.coin).unwrap_or_else(|| {
|
let coin = coin_to_serai_coin(&self.instruction.coin).unwrap_or_else(|| {
|
||||||
panic!(
|
panic!(
|
||||||
"mapping coin from an EthereumInInstruction with coin {}, which we don't handle.",
|
"mapping coin from an EthereumInInstruction with coin {}, which we don't handle.",
|
||||||
"this never should have been yielded"
|
"this never should have been yielded"
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
Balance { coin, amount: amount_to_serai_amount(coin, self.0.amount) }
|
Balance { coin, amount: amount_to_serai_amount(coin, self.instruction.amount) }
|
||||||
}
|
}
|
||||||
fn data(&self) -> &[u8] {
|
fn data(&self) -> &[u8] {
|
||||||
&self.0.data
|
&self.instruction.data
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
self.0.write(writer)
|
writer.write_all(self.key.to_bytes().as_ref())?;
|
||||||
|
self.instruction.write(writer)
|
||||||
}
|
}
|
||||||
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||||
EthereumInInstruction::read(reader).map(Self)
|
let key = Secp256k1::read_G(reader)?;
|
||||||
|
let instruction = EthereumInInstruction::read(reader)?;
|
||||||
|
Ok(Self { key, instruction })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,68 @@
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::transaction::Transaction;
|
use alloy_transport::{TransportErrorKind, RpcError};
|
||||||
|
use alloy_simple_request_transport::SimpleRequest;
|
||||||
|
use alloy_provider::RootProvider;
|
||||||
|
|
||||||
|
use tokio::sync::{RwLockReadGuard, RwLock};
|
||||||
|
|
||||||
|
use ethereum_schnorr::PublicKey;
|
||||||
|
use ethereum_router::{OutInstructions, Router};
|
||||||
|
|
||||||
|
use crate::transaction::{Action, Transaction};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct TransactionPublisher {
|
pub(crate) struct TransactionPublisher {
|
||||||
|
initial_serai_key: PublicKey,
|
||||||
|
rpc: Arc<RootProvider<SimpleRequest>>,
|
||||||
|
router: Arc<RwLock<Option<Router>>>,
|
||||||
relayer_url: String,
|
relayer_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionPublisher {
|
impl TransactionPublisher {
|
||||||
pub(crate) fn new(relayer_url: String) -> Self {
|
pub(crate) fn new(rpc: Arc<RootProvider<SimpleRequest>>, relayer_url: String) -> Self {
|
||||||
Self { relayer_url }
|
Self { initial_serai_key: todo!("TODO"), rpc, router: Arc::new(RwLock::new(None)), relayer_url }
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will always return Ok(Some(_)) or Err(_), never Ok(None)
|
||||||
|
async fn router(&self) -> Result<RwLockReadGuard<'_, Option<Router>>, RpcError<TransportErrorKind>> {
|
||||||
|
let router = self.router.read().await;
|
||||||
|
|
||||||
|
// If the router is None, find it on-chain
|
||||||
|
if router.is_none() {
|
||||||
|
drop(router);
|
||||||
|
let mut router = self.router.write().await;
|
||||||
|
// Check again if it's None in case a different task already did this
|
||||||
|
if router.is_none() {
|
||||||
|
let Some(router_actual) = Router::new(self.rpc.clone(), &self.initial_serai_key).await? else {
|
||||||
|
Err(TransportErrorKind::Custom("publishing transaction yet couldn't find router on chain. was our node reset?".to_string().into()))?
|
||||||
|
};
|
||||||
|
*router = Some(router_actual);
|
||||||
|
}
|
||||||
|
return Ok(router.downgrade());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(router)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl signers::TransactionPublisher<Transaction> for TransactionPublisher {
|
impl signers::TransactionPublisher<Transaction> for TransactionPublisher {
|
||||||
type EphemeralError = ();
|
type EphemeralError = RpcError<TransportErrorKind>;
|
||||||
|
|
||||||
fn publish(
|
fn publish(
|
||||||
&self,
|
&self,
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
) -> impl Send + Future<Output = Result<(), Self::EphemeralError>> {
|
) -> impl Send + Future<Output = Result<(), Self::EphemeralError>> {
|
||||||
// Convert from an Action (an internal representation of a signable event) to a TxLegacy
|
|
||||||
/* TODO
|
|
||||||
match tx.0 {
|
|
||||||
Action::SetKey { chain_id: _, nonce: _, key } => self.router.update_serai_key(key, tx.1),
|
|
||||||
Action::Batch { chain_id: _, nonce: _, outs } => self.router.execute(outs, tx.1),
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
|
// Convert from an Action (an internal representation of a signable event) to a TxLegacy
|
||||||
|
let router = self.router().await?;
|
||||||
|
let router = router.as_ref().unwrap();
|
||||||
|
let tx = match tx.0 {
|
||||||
|
Action::SetKey { chain_id: _, nonce: _, key } => router.update_serai_key(&key, &tx.1),
|
||||||
|
Action::Batch { chain_id: _, nonce: _, outs } => router.execute(OutInstructions::from(outs.as_ref()), &tx.1),
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncReadExt, AsyncWriteExt},
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
|
|
|
@ -68,6 +68,7 @@ impl smart_contract_scheduler::SmartContract<Rpc> for SmartContract {
|
||||||
|
|
||||||
// TODO: Per-batch gas limit
|
// TODO: Per-batch gas limit
|
||||||
// TODO: Create several batches
|
// TODO: Create several batches
|
||||||
|
// TODO: Handle fees
|
||||||
let action = Action::Batch { chain_id: self.chain_id, nonce, outs };
|
let action = Action::Batch { chain_id: self.chain_id, nonce, outs };
|
||||||
|
|
||||||
vec![(action.clone(), action.eventuality())]
|
vec![(action.clone(), action.eventuality())]
|
||||||
|
|
Loading…
Reference in a new issue