2025-01-30 12:23:03 +03:00
|
|
|
use std::{sync::Arc, ops::Deref, collections::HashSet};
|
2022-07-15 00:05:00 -04:00
|
|
|
|
2024-03-23 00:09:23 -04:00
|
|
|
use rand_core::{RngCore, OsRng};
|
|
|
|
|
|
2025-10-05 10:58:08 -04:00
|
|
|
use sp_core::Encode;
|
2022-07-15 00:05:00 -04:00
|
|
|
use sp_blockchain::{Error as BlockchainError, HeaderBackend, HeaderMetadata};
|
|
|
|
|
use sp_block_builder::BlockBuilder;
|
|
|
|
|
use sp_api::ProvideRuntimeApi;
|
|
|
|
|
|
2025-11-05 01:18:21 -05:00
|
|
|
use serai_abi::{primitives::prelude::*, SubstrateBlock as Block};
|
2022-07-15 00:05:00 -04:00
|
|
|
|
2023-12-22 21:09:18 -05:00
|
|
|
use tokio::sync::RwLock;
|
|
|
|
|
|
|
|
|
|
use jsonrpsee::RpcModule;
|
|
|
|
|
|
2025-10-05 10:58:08 -04:00
|
|
|
use sc_client_api::BlockBackend;
|
2023-01-20 11:00:18 -05:00
|
|
|
use sc_transaction_pool_api::TransactionPool;
|
2022-07-15 00:05:00 -04:00
|
|
|
|
2025-11-14 03:35:38 -05:00
|
|
|
mod utils;
|
2025-11-06 03:08:43 -05:00
|
|
|
mod blockchain;
|
2025-11-14 03:35:38 -05:00
|
|
|
mod validator_sets;
|
2025-11-06 03:08:43 -05:00
|
|
|
mod p2p_validators;
|
|
|
|
|
|
2022-07-15 00:05:00 -04:00
|
|
|
pub struct FullDeps<C, P> {
|
2024-04-12 00:38:40 -04:00
|
|
|
pub id: String,
|
2022-07-15 00:05:00 -04:00
|
|
|
pub client: Arc<C>,
|
|
|
|
|
pub pool: Arc<P>,
|
2023-12-22 21:09:18 -05:00
|
|
|
pub authority_discovery: Option<sc_authority_discovery::Service>,
|
2022-07-15 00:05:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create_full<
|
2025-11-05 01:18:21 -05:00
|
|
|
C: 'static
|
2022-07-15 01:26:07 -04:00
|
|
|
+ Send
|
|
|
|
|
+ Sync
|
2025-11-05 01:18:21 -05:00
|
|
|
+ ProvideRuntimeApi<Block, Api: BlockBuilder<Block> + serai_runtime::SeraiApi<Block>>
|
|
|
|
|
+ HeaderBackend<Block>
|
|
|
|
|
+ HeaderMetadata<Block, Error = BlockchainError>
|
|
|
|
|
+ BlockBackend<Block>,
|
|
|
|
|
P: 'static + TransactionPool,
|
2022-07-15 00:05:00 -04:00
|
|
|
>(
|
2022-07-15 01:26:07 -04:00
|
|
|
deps: FullDeps<C, P>,
|
2025-11-05 01:18:21 -05:00
|
|
|
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>> {
|
2025-10-05 10:58:08 -04:00
|
|
|
let FullDeps { id, client, pool, authority_discovery } = deps;
|
2022-07-15 00:05:00 -04:00
|
|
|
|
2025-11-06 03:08:43 -05:00
|
|
|
let mut root = RpcModule::new(());
|
|
|
|
|
root.merge(blockchain::module(client.clone())?)?;
|
2025-11-14 03:35:38 -05:00
|
|
|
root.merge(validator_sets::module(client.clone()))?;
|
2023-12-22 21:09:18 -05:00
|
|
|
if let Some(authority_discovery) = authority_discovery {
|
2025-11-06 03:08:43 -05:00
|
|
|
root.merge(p2p_validators::module(id, client, authority_discovery)?)?;
|
2023-12-22 21:09:18 -05:00
|
|
|
}
|
2025-11-06 03:08:43 -05:00
|
|
|
Ok(root)
|
2022-07-15 00:05:00 -04:00
|
|
|
|
2025-10-05 18:43:53 -04:00
|
|
|
/* TODO
|
2025-11-05 01:18:21 -05:00
|
|
|
use ciphersuite::{GroupIo, WithPreferredHash};
|
|
|
|
|
use ciphersuite_kp256::{k256::elliptic_curve::point::AffineCoordinates, Secp256k1};
|
|
|
|
|
use dalek_ff_group::Ed25519;
|
|
|
|
|
use bitcoin_serai::bitcoin;
|
|
|
|
|
|
2025-01-30 12:23:03 +03:00
|
|
|
let mut serai_json_module = RpcModule::new(client);
|
|
|
|
|
|
|
|
|
|
// add network address rpc
|
|
|
|
|
serai_json_module.register_async_method(
|
|
|
|
|
"external_network_address",
|
|
|
|
|
|params, context| async move {
|
|
|
|
|
let network: ExternalNetworkId = params.parse()?;
|
|
|
|
|
let client = &*context;
|
|
|
|
|
let latest_block = client.info().best_hash;
|
|
|
|
|
|
|
|
|
|
let external_key = client
|
|
|
|
|
.runtime_api()
|
|
|
|
|
.external_network_key(latest_block, network)
|
|
|
|
|
.map_err(|_| Error::Custom("api call error".to_string()))?
|
|
|
|
|
.ok_or(Error::Custom("no address for the network".to_string()))?;
|
|
|
|
|
|
|
|
|
|
match network {
|
|
|
|
|
ExternalNetworkId::Bitcoin => {
|
Smash the singular `Ciphersuite` trait into multiple
This helps identify where the various functionalities are used, or rather, not
used. The `Ciphersuite` trait present in `patches/ciphersuite`, facilitating
the entire FCMP++ tree, only requires the markers _and_ canonical point
decoding. I've opened a PR to upstream such a trait into `group`
(https://github.com/zkcrypto/group/pull/68).
`WrappedGroup` is still justified for as long as `Group::generator` exists.
Moving `::generator()` to its own trait, on an independent structure (upstream)
would be massively appreciated. @tarcieri also wanted to update from
`fn generator()` to `const GENERATOR`, which would encourage further discussion
on https://github.com/zkcrypto/group/issues/32 and
https://github.com/zkcrypto/group/issues/45, which have been stagnant.
The `Id` trait is occasionally used yet really should be first off the chopping
block.
Finally, `WithPreferredHash` is only actually used around a third of the time,
which more than justifies it being a separate trait.
---
Updates `dalek_ff_group::Scalar` to directly re-export
`curve25519_dalek::Scalar`, as without issue. `dalek_ff_group::RistrettoPoint`
also could be replaced with an export of `curve25519_dalek::RistrettoPoint`,
yet the coordinator relies on how we implemented `Hash` on it for the hell of
it so it isn't worth it at this time. `dalek_ff_group::EdwardsPoint` can't be
replaced for an re-export of `curve25519_dalek::SubgroupPoint` as it doesn't
implement `zeroize`, `subtle` traits within a released, non-yanked version.
Relevance to https://github.com/serai-dex/serai/issues/201 and
https://github.com/dalek-cryptography/curve25519-dalek/issues/811#issuecomment-3247732746.
Also updates the `Ristretto` ciphersuite to prefer `Blake2b-512` over
`SHA2-512`. In order to maintain compliance with FROST's IETF standard,
`modular-frost` defines its own ciphersuite for Ristretto which still uses
`SHA2-512`.
2025-09-03 12:25:37 -04:00
|
|
|
let key = <Secp256k1 as GroupIo>::read_G::<&[u8]>(&mut external_key.as_slice())
|
2025-01-30 12:23:03 +03:00
|
|
|
.map_err(|_| Error::Custom("invalid key stored in db".to_string()))?;
|
|
|
|
|
|
|
|
|
|
let addr = bitcoin::Address::p2tr_tweaked(
|
2025-08-25 09:17:29 -04:00
|
|
|
bitcoin::key::TweakedPublicKey::dangerous_assume_tweaked(
|
|
|
|
|
bitcoin::key::XOnlyPublicKey::from_slice(key.to_affine().x().as_slice()).map_err(
|
|
|
|
|
|_| Error::Custom("x-coordinate for Bitcoin key was invalid".to_string()),
|
|
|
|
|
)?,
|
|
|
|
|
),
|
2025-01-30 12:23:03 +03:00
|
|
|
bitcoin::address::KnownHrp::Mainnet,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Ok(addr.to_string())
|
|
|
|
|
}
|
|
|
|
|
// We don't know the eth address before the smart contract is deployed.
|
|
|
|
|
ExternalNetworkId::Ethereum => Ok(String::new()),
|
|
|
|
|
ExternalNetworkId::Monero => {
|
Replace `Ciphersuite::hash_to_F`
The prior-present `Ciphersuite::hash_to_F` was a sin. Implementations took a
DST, yet were not require to securely handle it. It was also biased towards the
requirements of `modular-frost` as `ciphersuite` was originally written all
those years ago, when `modular-frost` had needs exceeding what `ff`, `group`
satisfied.
Now, the hash is bound to produce an output which can be converted to a scalar
with `ff::FromUniformBytes`. A new `hash_to_F`, which accepts a single argument
of the value to hash (removing the potential to insecurely handle the DST by
removing the DST entirely). Due to `digest` yielding a `GenericArray`, yet
`FromUniformBytes` taking a `const usize`, the `ciphersuite` crate now defines
a `FromUniformBytes` trait taking an array (then implemented for all satisfiers
of `ff::FromUniformBytes`). In order to get the array type from the
`GenericArray`, the output of the hash, `digest` is updated to the `0.11`
release candidate which moves to `flexible-array` which solves that problem.
The existing, specific `hash_to_F` functions have been moved to `modular-frost`
as necessary.
`flexible-array` itself is patched to a fork due to
https://github.com/RustCrypto/hybrid-array/issues/131.
2025-08-29 05:04:03 -04:00
|
|
|
// TODO: Serai view-key crate
|
Smash the singular `Ciphersuite` trait into multiple
This helps identify where the various functionalities are used, or rather, not
used. The `Ciphersuite` trait present in `patches/ciphersuite`, facilitating
the entire FCMP++ tree, only requires the markers _and_ canonical point
decoding. I've opened a PR to upstream such a trait into `group`
(https://github.com/zkcrypto/group/pull/68).
`WrappedGroup` is still justified for as long as `Group::generator` exists.
Moving `::generator()` to its own trait, on an independent structure (upstream)
would be massively appreciated. @tarcieri also wanted to update from
`fn generator()` to `const GENERATOR`, which would encourage further discussion
on https://github.com/zkcrypto/group/issues/32 and
https://github.com/zkcrypto/group/issues/45, which have been stagnant.
The `Id` trait is occasionally used yet really should be first off the chopping
block.
Finally, `WithPreferredHash` is only actually used around a third of the time,
which more than justifies it being a separate trait.
---
Updates `dalek_ff_group::Scalar` to directly re-export
`curve25519_dalek::Scalar`, as without issue. `dalek_ff_group::RistrettoPoint`
also could be replaced with an export of `curve25519_dalek::RistrettoPoint`,
yet the coordinator relies on how we implemented `Hash` on it for the hell of
it so it isn't worth it at this time. `dalek_ff_group::EdwardsPoint` can't be
replaced for an re-export of `curve25519_dalek::SubgroupPoint` as it doesn't
implement `zeroize`, `subtle` traits within a released, non-yanked version.
Relevance to https://github.com/serai-dex/serai/issues/201 and
https://github.com/dalek-cryptography/curve25519-dalek/issues/811#issuecomment-3247732746.
Also updates the `Ristretto` ciphersuite to prefer `Blake2b-512` over
`SHA2-512`. In order to maintain compliance with FROST's IETF standard,
`modular-frost` defines its own ciphersuite for Ristretto which still uses
`SHA2-512`.
2025-09-03 12:25:37 -04:00
|
|
|
let view_private = zeroize::Zeroizing::new(<Ed25519 as WithPreferredHash>::hash_to_F(
|
|
|
|
|
&["Monero".as_bytes(), &0u64.to_le_bytes()].concat(),
|
|
|
|
|
));
|
2025-01-30 12:23:03 +03:00
|
|
|
|
Smash the singular `Ciphersuite` trait into multiple
This helps identify where the various functionalities are used, or rather, not
used. The `Ciphersuite` trait present in `patches/ciphersuite`, facilitating
the entire FCMP++ tree, only requires the markers _and_ canonical point
decoding. I've opened a PR to upstream such a trait into `group`
(https://github.com/zkcrypto/group/pull/68).
`WrappedGroup` is still justified for as long as `Group::generator` exists.
Moving `::generator()` to its own trait, on an independent structure (upstream)
would be massively appreciated. @tarcieri also wanted to update from
`fn generator()` to `const GENERATOR`, which would encourage further discussion
on https://github.com/zkcrypto/group/issues/32 and
https://github.com/zkcrypto/group/issues/45, which have been stagnant.
The `Id` trait is occasionally used yet really should be first off the chopping
block.
Finally, `WithPreferredHash` is only actually used around a third of the time,
which more than justifies it being a separate trait.
---
Updates `dalek_ff_group::Scalar` to directly re-export
`curve25519_dalek::Scalar`, as without issue. `dalek_ff_group::RistrettoPoint`
also could be replaced with an export of `curve25519_dalek::RistrettoPoint`,
yet the coordinator relies on how we implemented `Hash` on it for the hell of
it so it isn't worth it at this time. `dalek_ff_group::EdwardsPoint` can't be
replaced for an re-export of `curve25519_dalek::SubgroupPoint` as it doesn't
implement `zeroize`, `subtle` traits within a released, non-yanked version.
Relevance to https://github.com/serai-dex/serai/issues/201 and
https://github.com/dalek-cryptography/curve25519-dalek/issues/811#issuecomment-3247732746.
Also updates the `Ristretto` ciphersuite to prefer `Blake2b-512` over
`SHA2-512`. In order to maintain compliance with FROST's IETF standard,
`modular-frost` defines its own ciphersuite for Ristretto which still uses
`SHA2-512`.
2025-09-03 12:25:37 -04:00
|
|
|
let spend = <Ed25519 as GroupIo>::read_G::<&[u8]>(&mut external_key.as_slice())
|
2025-01-30 12:23:03 +03:00
|
|
|
.map_err(|_| Error::Custom("invalid key stored in db".to_string()))?;
|
|
|
|
|
|
2025-01-30 05:04:28 -05:00
|
|
|
let addr = monero_address::MoneroAddress::new(
|
|
|
|
|
monero_address::Network::Mainnet,
|
|
|
|
|
monero_address::AddressType::Featured {
|
2025-01-30 12:23:03 +03:00
|
|
|
subaddress: false,
|
|
|
|
|
payment_id: None,
|
|
|
|
|
guaranteed: true,
|
|
|
|
|
},
|
|
|
|
|
*spend,
|
|
|
|
|
view_private.deref() * curve25519_dalek::constants::ED25519_BASEPOINT_TABLE,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Ok(addr.to_string())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
// add shorthand encoding rpc
|
|
|
|
|
serai_json_module.register_async_method("encoded_shorthand", |params, _| async move {
|
|
|
|
|
// decode using serde and encode back using scale
|
|
|
|
|
let shorthand: Shorthand = params.parse()?;
|
|
|
|
|
Ok(shorthand.encode())
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
// add simulating a swap path rpc
|
|
|
|
|
serai_json_module.register_async_method("quote_price", |params, context| async move {
|
|
|
|
|
let client = &*context;
|
|
|
|
|
let latest_block = client.info().best_hash;
|
|
|
|
|
let QuotePriceParams { coin1, coin2, amount, include_fee, exact_in } = params.parse()?;
|
|
|
|
|
|
|
|
|
|
let amount = if exact_in {
|
|
|
|
|
client
|
|
|
|
|
.runtime_api()
|
|
|
|
|
.quote_price_exact_tokens_for_tokens(latest_block, coin1, coin2, amount, include_fee)
|
|
|
|
|
.map_err(|_| Error::Custom("api call error".to_string()))?
|
|
|
|
|
.ok_or(Error::Custom("invalid params or empty pool".to_string()))?
|
|
|
|
|
} else {
|
|
|
|
|
client
|
|
|
|
|
.runtime_api()
|
|
|
|
|
.quote_price_tokens_for_exact_tokens(latest_block, coin1, coin2, amount, include_fee)
|
|
|
|
|
.map_err(|_| Error::Custom("api call error".to_string()))?
|
|
|
|
|
.ok_or(Error::Custom("invalid params or empty pool".to_string()))?
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(amount)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
module.merge(serai_json_module)?;
|
2025-10-05 18:43:53 -04:00
|
|
|
*/
|
2022-07-15 00:05:00 -04:00
|
|
|
}
|