mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-10 13:09:24 +00:00
Merge branch 'develop' into HEAD
This commit is contained in:
2
.github/nightly-version
vendored
2
.github/nightly-version
vendored
@@ -1 +1 @@
|
||||
nightly-2024-05-01
|
||||
nightly-2024-06-01
|
||||
|
||||
1318
Cargo.lock
generated
1318
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,8 @@
|
||||
resolver = "2"
|
||||
members = [
|
||||
# Version patches
|
||||
"patches/parking_lot_core",
|
||||
"patches/parking_lot",
|
||||
"patches/zstd",
|
||||
"patches/rocksdb",
|
||||
"patches/proc-macro-crate",
|
||||
@@ -36,8 +38,11 @@ members = [
|
||||
"crypto/schnorrkel",
|
||||
|
||||
"coins/bitcoin",
|
||||
|
||||
"coins/ethereum/alloy-simple-request-transport",
|
||||
"coins/ethereum",
|
||||
"coins/ethereum/relayer",
|
||||
|
||||
"coins/monero/generators",
|
||||
"coins/monero",
|
||||
|
||||
@@ -112,6 +117,8 @@ lazy_static = { git = "https://github.com/rust-lang-nursery/lazy-static.rs", rev
|
||||
# Needed due to dockertest's usage of `Rc`s when we need `Arc`s
|
||||
dockertest = { git = "https://github.com/orcalabs/dockertest-rs", rev = "4dd6ae24738aa6dc5c89444cc822ea4745517493" }
|
||||
|
||||
parking_lot_core = { path = "patches/parking_lot_core" }
|
||||
parking_lot = { path = "patches/parking_lot" }
|
||||
# wasmtime pulls in an old version for this
|
||||
zstd = { path = "patches/zstd" }
|
||||
# Needed for WAL compression
|
||||
|
||||
@@ -6,7 +6,7 @@ license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/coins/bitcoin"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>", "Vrx <vrx00@proton.me>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
@@ -23,7 +23,7 @@ thiserror = { version = "1", default-features = false, optional = true }
|
||||
zeroize = { version = "^1.5", default-features = false }
|
||||
rand_core = { version = "0.6", default-features = false }
|
||||
|
||||
bitcoin = { version = "0.31", default-features = false, features = ["no-std"] }
|
||||
bitcoin = { version = "0.32", default-features = false }
|
||||
|
||||
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
||||
|
||||
@@ -36,7 +36,7 @@ serde_json = { version = "1", default-features = false, optional = true }
|
||||
simple-request = { path = "../../common/request", version = "0.1", default-features = false, features = ["tls", "basic-auth"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
secp256k1 = { version = "0.28", default-features = false, features = ["std"] }
|
||||
secp256k1 = { version = "0.29", default-features = false, features = ["std"] }
|
||||
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", features = ["tests"] }
|
||||
|
||||
|
||||
@@ -195,13 +195,13 @@ impl Rpc {
|
||||
// If this was already successfully published, consider this having succeeded
|
||||
if let RpcError::RequestError(Error { code, .. }) = e {
|
||||
if code == RPC_VERIFY_ALREADY_IN_CHAIN {
|
||||
return Ok(tx.txid());
|
||||
return Ok(tx.compute_txid());
|
||||
}
|
||||
}
|
||||
Err(e)?
|
||||
}
|
||||
};
|
||||
if txid != tx.txid() {
|
||||
if txid != tx.compute_txid() {
|
||||
Err(RpcError::InvalidResponse("returned TX ID inequals calculated TX ID"))?;
|
||||
}
|
||||
Ok(txid)
|
||||
@@ -215,7 +215,7 @@ impl Rpc {
|
||||
let tx: Transaction = encode::deserialize(&bytes)
|
||||
.map_err(|_| RpcError::InvalidResponse("node sent an improperly serialized transaction"))?;
|
||||
|
||||
let mut tx_hash = *tx.txid().as_raw_hash().as_byte_array();
|
||||
let mut tx_hash = *tx.compute_txid().as_raw_hash().as_byte_array();
|
||||
tx_hash.reverse();
|
||||
if hash != &tx_hash {
|
||||
Err(RpcError::InvalidResponse("node replied with a different transaction"))?;
|
||||
|
||||
@@ -39,7 +39,7 @@ fn test_algorithm() {
|
||||
.verify_schnorr(
|
||||
&Signature::from_slice(&sig)
|
||||
.expect("couldn't convert produced signature to secp256k1::Signature"),
|
||||
&Message::from(Hash::hash(MESSAGE)),
|
||||
&Message::from_digest_slice(Hash::hash(MESSAGE).as_ref()).unwrap(),
|
||||
&x_only(&keys[&Participant::new(1).unwrap()].group_key()),
|
||||
)
|
||||
.unwrap()
|
||||
|
||||
@@ -4,7 +4,7 @@ use std_shims::{
|
||||
io::{self, Write},
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std_shims::io::Read;
|
||||
use std::io::{Read, BufReader};
|
||||
|
||||
use k256::{
|
||||
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
||||
@@ -18,8 +18,8 @@ use frost::{
|
||||
};
|
||||
|
||||
use bitcoin::{
|
||||
consensus::encode::serialize, key::TweakedPublicKey, address::Payload, OutPoint, ScriptBuf,
|
||||
TxOut, Transaction, Block,
|
||||
consensus::encode::serialize, key::TweakedPublicKey, OutPoint, ScriptBuf, TxOut, Transaction,
|
||||
Block,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use bitcoin::consensus::encode::Decodable;
|
||||
@@ -46,12 +46,12 @@ pub fn tweak_keys(keys: &ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
|
||||
/// Return the Taproot address payload for a public key.
|
||||
///
|
||||
/// If the key is odd, this will return None.
|
||||
pub fn address_payload(key: ProjectivePoint) -> Option<Payload> {
|
||||
pub fn p2tr_script_buf(key: ProjectivePoint) -> Option<ScriptBuf> {
|
||||
if key.to_encoded_point(true).tag() != Tag::CompressedEvenY {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Payload::p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(x_only(&key))))
|
||||
Some(ScriptBuf::new_p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(x_only(&key))))
|
||||
}
|
||||
|
||||
/// A spendable output.
|
||||
@@ -89,11 +89,17 @@ impl ReceivedOutput {
|
||||
/// Read a ReceivedOutput from a generic satisfying Read.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
||||
Ok(ReceivedOutput {
|
||||
offset: Secp256k1::read_F(r)?,
|
||||
output: TxOut::consensus_decode(r).map_err(|_| io::Error::other("invalid TxOut"))?,
|
||||
outpoint: OutPoint::consensus_decode(r).map_err(|_| io::Error::other("invalid OutPoint"))?,
|
||||
})
|
||||
let offset = Secp256k1::read_F(r)?;
|
||||
let output;
|
||||
let outpoint;
|
||||
{
|
||||
let mut buf_r = BufReader::with_capacity(0, r);
|
||||
output =
|
||||
TxOut::consensus_decode(&mut buf_r).map_err(|_| io::Error::other("invalid TxOut"))?;
|
||||
outpoint =
|
||||
OutPoint::consensus_decode(&mut buf_r).map_err(|_| io::Error::other("invalid OutPoint"))?;
|
||||
}
|
||||
Ok(ReceivedOutput { offset, output, outpoint })
|
||||
}
|
||||
|
||||
/// Write a ReceivedOutput to a generic satisfying Write.
|
||||
@@ -124,7 +130,7 @@ impl Scanner {
|
||||
/// Returns None if this key can't be scanned for.
|
||||
pub fn new(key: ProjectivePoint) -> Option<Scanner> {
|
||||
let mut scripts = HashMap::new();
|
||||
scripts.insert(address_payload(key)?.script_pubkey(), Scalar::ZERO);
|
||||
scripts.insert(p2tr_script_buf(key)?, Scalar::ZERO);
|
||||
Some(Scanner { key, scripts })
|
||||
}
|
||||
|
||||
@@ -141,9 +147,8 @@ impl Scanner {
|
||||
// chance of being even
|
||||
// That means this should terminate within a very small amount of iterations
|
||||
loop {
|
||||
match address_payload(self.key + (ProjectivePoint::GENERATOR * offset)) {
|
||||
Some(address) => {
|
||||
let script = address.script_pubkey();
|
||||
match p2tr_script_buf(self.key + (ProjectivePoint::GENERATOR * offset)) {
|
||||
Some(script) => {
|
||||
if self.scripts.contains_key(&script) {
|
||||
None?;
|
||||
}
|
||||
@@ -166,7 +171,7 @@ impl Scanner {
|
||||
res.push(ReceivedOutput {
|
||||
offset: *offset,
|
||||
output: output.clone(),
|
||||
outpoint: OutPoint::new(tx.txid(), vout),
|
||||
outpoint: OutPoint::new(tx.compute_txid(), vout),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ use bitcoin::{
|
||||
absolute::LockTime,
|
||||
script::{PushBytesBuf, ScriptBuf},
|
||||
transaction::{Version, Transaction},
|
||||
OutPoint, Sequence, Witness, TxIn, Amount, TxOut, Address,
|
||||
OutPoint, Sequence, Witness, TxIn, Amount, TxOut,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
crypto::Schnorr,
|
||||
wallet::{ReceivedOutput, address_payload},
|
||||
wallet::{ReceivedOutput, p2tr_script_buf},
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
@@ -61,7 +61,11 @@ pub struct SignableTransaction {
|
||||
}
|
||||
|
||||
impl SignableTransaction {
|
||||
fn calculate_weight(inputs: usize, payments: &[(Address, u64)], change: Option<&Address>) -> u64 {
|
||||
fn calculate_weight(
|
||||
inputs: usize,
|
||||
payments: &[(ScriptBuf, u64)],
|
||||
change: Option<&ScriptBuf>,
|
||||
) -> u64 {
|
||||
// Expand this a full transaction in order to use the bitcoin library's weight function
|
||||
let mut tx = Transaction {
|
||||
version: Version(2),
|
||||
@@ -86,14 +90,14 @@ impl SignableTransaction {
|
||||
// The script pub key is not of a fixed size and does have to be used here
|
||||
.map(|payment| TxOut {
|
||||
value: Amount::from_sat(payment.1),
|
||||
script_pubkey: payment.0.script_pubkey(),
|
||||
script_pubkey: payment.0.clone(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
if let Some(change) = change {
|
||||
// Use a 0 value since we're currently unsure what the change amount will be, and since
|
||||
// the value is fixed size (so any value could be used here)
|
||||
tx.output.push(TxOut { value: Amount::ZERO, script_pubkey: change.script_pubkey() });
|
||||
tx.output.push(TxOut { value: Amount::ZERO, script_pubkey: change.clone() });
|
||||
}
|
||||
u64::from(tx.weight())
|
||||
}
|
||||
@@ -121,8 +125,8 @@ impl SignableTransaction {
|
||||
/// If data is specified, an OP_RETURN output will be added with it.
|
||||
pub fn new(
|
||||
mut inputs: Vec<ReceivedOutput>,
|
||||
payments: &[(Address, u64)],
|
||||
change: Option<&Address>,
|
||||
payments: &[(ScriptBuf, u64)],
|
||||
change: Option<ScriptBuf>,
|
||||
data: Option<Vec<u8>>,
|
||||
fee_per_weight: u64,
|
||||
) -> Result<SignableTransaction, TransactionError> {
|
||||
@@ -159,10 +163,7 @@ impl SignableTransaction {
|
||||
let payment_sat = payments.iter().map(|payment| payment.1).sum::<u64>();
|
||||
let mut tx_outs = payments
|
||||
.iter()
|
||||
.map(|payment| TxOut {
|
||||
value: Amount::from_sat(payment.1),
|
||||
script_pubkey: payment.0.script_pubkey(),
|
||||
})
|
||||
.map(|payment| TxOut { value: Amount::from_sat(payment.1), script_pubkey: payment.0.clone() })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Add the OP_RETURN output
|
||||
@@ -213,12 +214,11 @@ impl SignableTransaction {
|
||||
|
||||
// If there's a change address, check if there's change to give it
|
||||
if let Some(change) = change {
|
||||
let weight_with_change = Self::calculate_weight(tx_ins.len(), payments, Some(change));
|
||||
let weight_with_change = Self::calculate_weight(tx_ins.len(), payments, Some(&change));
|
||||
let fee_with_change = fee_per_weight * weight_with_change;
|
||||
if let Some(value) = input_sat.checked_sub(payment_sat + fee_with_change) {
|
||||
if value >= DUST {
|
||||
tx_outs
|
||||
.push(TxOut { value: Amount::from_sat(value), script_pubkey: change.script_pubkey() });
|
||||
tx_outs.push(TxOut { value: Amount::from_sat(value), script_pubkey: change });
|
||||
weight = weight_with_change;
|
||||
needed_fee = fee_with_change;
|
||||
}
|
||||
@@ -248,7 +248,7 @@ impl SignableTransaction {
|
||||
|
||||
/// Returns the TX ID of the transaction this will create.
|
||||
pub fn txid(&self) -> [u8; 32] {
|
||||
let mut res = self.tx.txid().to_byte_array();
|
||||
let mut res = self.tx.compute_txid().to_byte_array();
|
||||
res.reverse();
|
||||
res
|
||||
}
|
||||
@@ -288,7 +288,7 @@ impl SignableTransaction {
|
||||
transcript.append_message(b"signing_input", u32::try_from(i).unwrap().to_le_bytes());
|
||||
|
||||
let offset = keys.clone().offset(self.offsets[i]);
|
||||
if address_payload(offset.group_key())?.script_pubkey() != self.prevouts[i].script_pubkey {
|
||||
if p2tr_script_buf(offset.group_key())? != self.prevouts[i].script_pubkey {
|
||||
None?;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,10 @@ use bitcoin_serai::{
|
||||
hashes::Hash as HashTrait,
|
||||
blockdata::opcodes::all::OP_RETURN,
|
||||
script::{PushBytesBuf, Instruction, Instructions, Script},
|
||||
address::NetworkChecked,
|
||||
OutPoint, Amount, TxOut, Transaction, Network, Address,
|
||||
},
|
||||
wallet::{
|
||||
tweak_keys, address_payload, ReceivedOutput, Scanner, TransactionError, SignableTransaction,
|
||||
tweak_keys, p2tr_script_buf, ReceivedOutput, Scanner, TransactionError, SignableTransaction,
|
||||
},
|
||||
rpc::Rpc,
|
||||
};
|
||||
@@ -48,7 +47,7 @@ async fn send_and_get_output(rpc: &Rpc, scanner: &Scanner, key: ProjectivePoint)
|
||||
"generatetoaddress",
|
||||
serde_json::json!([
|
||||
1,
|
||||
Address::<NetworkChecked>::new(Network::Regtest, address_payload(key).unwrap())
|
||||
Address::from_script(&p2tr_script_buf(key).unwrap(), Network::Regtest).unwrap()
|
||||
]),
|
||||
)
|
||||
.await
|
||||
@@ -69,7 +68,7 @@ async fn send_and_get_output(rpc: &Rpc, scanner: &Scanner, key: ProjectivePoint)
|
||||
assert_eq!(outputs, scanner.scan_transaction(&block.txdata[0]));
|
||||
|
||||
assert_eq!(outputs.len(), 1);
|
||||
assert_eq!(outputs[0].outpoint(), &OutPoint::new(block.txdata[0].txid(), 0));
|
||||
assert_eq!(outputs[0].outpoint(), &OutPoint::new(block.txdata[0].compute_txid(), 0));
|
||||
assert_eq!(outputs[0].value(), block.txdata[0].output[0].value.to_sat());
|
||||
|
||||
assert_eq!(
|
||||
@@ -193,7 +192,7 @@ async_sequential! {
|
||||
assert_eq!(output.offset(), Scalar::ZERO);
|
||||
|
||||
let inputs = vec![output];
|
||||
let addr = || Address::<NetworkChecked>::new(Network::Regtest, address_payload(key).unwrap());
|
||||
let addr = || p2tr_script_buf(key).unwrap();
|
||||
let payments = vec![(addr(), 1000)];
|
||||
|
||||
assert!(SignableTransaction::new(inputs.clone(), &payments, None, None, FEE).is_ok());
|
||||
@@ -206,7 +205,7 @@ async_sequential! {
|
||||
// No change
|
||||
assert!(SignableTransaction::new(inputs.clone(), &[(addr(), 1000)], None, None, FEE).is_ok());
|
||||
// Consolidation TX
|
||||
assert!(SignableTransaction::new(inputs.clone(), &[], Some(&addr()), None, FEE).is_ok());
|
||||
assert!(SignableTransaction::new(inputs.clone(), &[], Some(addr()), None, FEE).is_ok());
|
||||
// Data
|
||||
assert!(SignableTransaction::new(inputs.clone(), &[], None, Some(vec![]), FEE).is_ok());
|
||||
// No outputs
|
||||
@@ -229,7 +228,7 @@ async_sequential! {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
SignableTransaction::new(inputs.clone(), &[], Some(&addr()), None, 0),
|
||||
SignableTransaction::new(inputs.clone(), &[], Some(addr()), None, 0),
|
||||
Err(TransactionError::TooLowFee),
|
||||
);
|
||||
|
||||
@@ -261,20 +260,19 @@ async_sequential! {
|
||||
|
||||
// Declare payments, change, fee
|
||||
let payments = [
|
||||
(Address::<NetworkChecked>::new(Network::Regtest, address_payload(key).unwrap()), 1005),
|
||||
(Address::<NetworkChecked>::new(Network::Regtest, address_payload(offset_key).unwrap()), 1007)
|
||||
(p2tr_script_buf(key).unwrap(), 1005),
|
||||
(p2tr_script_buf(offset_key).unwrap(), 1007)
|
||||
];
|
||||
|
||||
let change_offset = scanner.register_offset(Scalar::random(&mut OsRng)).unwrap();
|
||||
let change_key = key + (ProjectivePoint::GENERATOR * change_offset);
|
||||
let change_addr =
|
||||
Address::<NetworkChecked>::new(Network::Regtest, address_payload(change_key).unwrap());
|
||||
let change_addr = p2tr_script_buf(change_key).unwrap();
|
||||
|
||||
// Create and sign the TX
|
||||
let tx = SignableTransaction::new(
|
||||
vec![output.clone(), offset_output.clone()],
|
||||
&payments,
|
||||
Some(&change_addr),
|
||||
Some(change_addr.clone()),
|
||||
None,
|
||||
FEE
|
||||
).unwrap();
|
||||
@@ -287,7 +285,7 @@ async_sequential! {
|
||||
// Ensure we can scan it
|
||||
let outputs = scanner.scan_transaction(&tx);
|
||||
for (o, output) in outputs.iter().enumerate() {
|
||||
assert_eq!(output.outpoint(), &OutPoint::new(tx.txid(), u32::try_from(o).unwrap()));
|
||||
assert_eq!(output.outpoint(), &OutPoint::new(tx.compute_txid(), u32::try_from(o).unwrap()));
|
||||
assert_eq!(&ReceivedOutput::read::<&[u8]>(&mut output.serialize().as_ref()).unwrap(), output);
|
||||
}
|
||||
|
||||
@@ -299,7 +297,7 @@ async_sequential! {
|
||||
for ((output, scanned), payment) in tx.output.iter().zip(outputs.iter()).zip(payments.iter()) {
|
||||
assert_eq!(
|
||||
output,
|
||||
&TxOut { script_pubkey: payment.0.script_pubkey(), value: Amount::from_sat(payment.1) },
|
||||
&TxOut { script_pubkey: payment.0.clone(), value: Amount::from_sat(payment.1) },
|
||||
);
|
||||
assert_eq!(scanned.value(), payment.1 );
|
||||
}
|
||||
@@ -314,13 +312,13 @@ async_sequential! {
|
||||
input_value - payments.iter().map(|payment| payment.1).sum::<u64>() - needed_fee;
|
||||
assert_eq!(
|
||||
tx.output[2],
|
||||
TxOut { script_pubkey: change_addr.script_pubkey(), value: Amount::from_sat(change_amount) },
|
||||
TxOut { script_pubkey: change_addr, value: Amount::from_sat(change_amount) },
|
||||
);
|
||||
|
||||
// This also tests send_raw_transaction and get_transaction, which the RPC test can't
|
||||
// effectively test
|
||||
rpc.send_raw_transaction(&tx).await.unwrap();
|
||||
let mut hash = *tx.txid().as_raw_hash().as_byte_array();
|
||||
let mut hash = *tx.compute_txid().as_raw_hash().as_byte_array();
|
||||
hash.reverse();
|
||||
assert_eq!(tx, rpc.get_transaction(&hash).await.unwrap());
|
||||
assert_eq!(expected_id, hash);
|
||||
@@ -344,7 +342,7 @@ async_sequential! {
|
||||
&SignableTransaction::new(
|
||||
vec![output],
|
||||
&[],
|
||||
Some(&Address::<NetworkChecked>::new(Network::Regtest, address_payload(key).unwrap())),
|
||||
Some(p2tr_script_buf(key).unwrap()),
|
||||
Some(data.clone()),
|
||||
FEE
|
||||
).unwrap()
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/coins/ethereum"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>", "Elizabeth Binks <elizabethjbinks@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
@@ -29,21 +29,21 @@ frost = { package = "modular-frost", path = "../../crypto/frost", default-featur
|
||||
|
||||
alloy-core = { version = "0.7", default-features = false }
|
||||
alloy-sol-types = { version = "0.7", default-features = false, features = ["json"] }
|
||||
alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false, features = ["k256"] }
|
||||
alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false }
|
||||
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false }
|
||||
alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false }
|
||||
alloy-consensus = { version = "0.1", default-features = false, features = ["k256"] }
|
||||
alloy-network = { version = "0.1", default-features = false }
|
||||
alloy-rpc-types-eth = { version = "0.1", default-features = false }
|
||||
alloy-rpc-client = { version = "0.1", default-features = false }
|
||||
alloy-simple-request-transport = { path = "./alloy-simple-request-transport", default-features = false }
|
||||
alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false }
|
||||
alloy-provider = { version = "0.1", default-features = false }
|
||||
|
||||
alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false, optional = true }
|
||||
alloy-node-bindings = { version = "0.1", default-features = false, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false, features = ["tests"] }
|
||||
|
||||
tokio = { version = "1", features = ["macros"] }
|
||||
|
||||
alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false }
|
||||
alloy-node-bindings = { version = "0.1", default-features = false }
|
||||
|
||||
[features]
|
||||
tests = ["alloy-node-bindings"]
|
||||
tests = ["alloy-node-bindings", "frost/tests"]
|
||||
|
||||
@@ -21,8 +21,8 @@ tower = "0.4"
|
||||
serde_json = { version = "1", default-features = false }
|
||||
simple-request = { path = "../../../common/request", default-features = false }
|
||||
|
||||
alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false }
|
||||
alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false }
|
||||
alloy-json-rpc = { version = "0.1", default-features = false }
|
||||
alloy-transport = { version = "0.1", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["tls"]
|
||||
|
||||
30
coins/ethereum/relayer/Cargo.toml
Normal file
30
coins/ethereum/relayer/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "serai-ethereum-relayer"
|
||||
version = "0.1.0"
|
||||
description = "A relayer for Serai's Ethereum transactions"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/coins/ethereum/relayer"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = []
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }
|
||||
|
||||
tokio = { version = "1", default-features = false, features = ["rt", "time", "io-util", "net", "macros"] }
|
||||
|
||||
serai-env = { path = "../../../common/env" }
|
||||
serai-db = { path = "../../../common/db" }
|
||||
|
||||
[features]
|
||||
parity-db = ["serai-db/parity-db"]
|
||||
rocksdb = ["serai-db/rocksdb"]
|
||||
15
coins/ethereum/relayer/LICENSE
Normal file
15
coins/ethereum/relayer/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
||||
AGPL-3.0-only license
|
||||
|
||||
Copyright (c) 2023-2024 Luke Parker
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License Version 3 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
4
coins/ethereum/relayer/README.md
Normal file
4
coins/ethereum/relayer/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ethereum Transaction Relayer
|
||||
|
||||
This server collects Ethereum router commands to be published, offering an RPC
|
||||
to fetch them.
|
||||
100
coins/ethereum/relayer/src/main.rs
Normal file
100
coins/ethereum/relayer/src/main.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
pub(crate) use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpListener,
|
||||
};
|
||||
|
||||
use serai_db::{Get, DbTxn, Db as DbTrait};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
// Override the panic handler with one which will panic if any tokio task panics
|
||||
{
|
||||
let existing = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |panic| {
|
||||
existing(panic);
|
||||
const MSG: &str = "exiting the process due to a task panicking";
|
||||
println!("{MSG}");
|
||||
log::error!("{MSG}");
|
||||
std::process::exit(1);
|
||||
}));
|
||||
}
|
||||
|
||||
if std::env::var("RUST_LOG").is_err() {
|
||||
std::env::set_var("RUST_LOG", serai_env::var("RUST_LOG").unwrap_or_else(|| "info".to_string()));
|
||||
}
|
||||
env_logger::init();
|
||||
|
||||
log::info!("Starting Ethereum relayer server...");
|
||||
|
||||
// Open the DB
|
||||
#[allow(unused_variables, unreachable_code)]
|
||||
let db = {
|
||||
#[cfg(all(feature = "parity-db", feature = "rocksdb"))]
|
||||
panic!("built with parity-db and rocksdb");
|
||||
#[cfg(all(feature = "parity-db", not(feature = "rocksdb")))]
|
||||
let db =
|
||||
serai_db::new_parity_db(&serai_env::var("DB_PATH").expect("path to DB wasn't specified"));
|
||||
#[cfg(feature = "rocksdb")]
|
||||
let db =
|
||||
serai_db::new_rocksdb(&serai_env::var("DB_PATH").expect("path to DB wasn't specified"));
|
||||
db
|
||||
};
|
||||
|
||||
// Start command recipience server
|
||||
// This should not be publicly exposed
|
||||
// TODO: Add auth
|
||||
tokio::spawn({
|
||||
let db = db.clone();
|
||||
async move {
|
||||
// 5132 ^ ((b'E' << 8) | b'R')
|
||||
let server = TcpListener::bind("0.0.0.0:20830").await.unwrap();
|
||||
loop {
|
||||
let (mut socket, _) = server.accept().await.unwrap();
|
||||
let db = db.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut db = db.clone();
|
||||
loop {
|
||||
let Ok(msg_len) = socket.read_u32_le().await else { break };
|
||||
let mut buf = vec![0; usize::try_from(msg_len).unwrap()];
|
||||
let Ok(_) = socket.read_exact(&mut buf).await else { break };
|
||||
|
||||
if buf.len() < 5 {
|
||||
break;
|
||||
}
|
||||
let nonce = u32::from_le_bytes(buf[.. 4].try_into().unwrap());
|
||||
let mut txn = db.txn();
|
||||
txn.put(nonce.to_le_bytes(), &buf[4 ..]);
|
||||
txn.commit();
|
||||
|
||||
let Ok(()) = socket.write_all(&[1]).await else { break };
|
||||
|
||||
log::info!("received signed command #{nonce}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start command fetch server
|
||||
// 5132 ^ ((b'E' << 8) | b'R') + 1
|
||||
let server = TcpListener::bind("0.0.0.0:20831").await.unwrap();
|
||||
loop {
|
||||
let (mut socket, _) = server.accept().await.unwrap();
|
||||
let db = db.clone();
|
||||
tokio::spawn(async move {
|
||||
let db = db.clone();
|
||||
loop {
|
||||
// Nonce to get the router comamnd for
|
||||
let mut buf = vec![0; 4];
|
||||
let Ok(_) = socket.read_exact(&mut buf).await else { break };
|
||||
|
||||
let command = db.get(&buf[.. 4]).unwrap_or(vec![]);
|
||||
let Ok(()) = socket.write_all(&u32::try_from(command.len()).unwrap().to_le_bytes()).await
|
||||
else {
|
||||
break;
|
||||
};
|
||||
let Ok(()) = socket.write_all(&command).await else { break };
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use alloy_consensus::{Signed, TxLegacy};
|
||||
|
||||
use alloy_sol_types::{SolCall, SolEvent};
|
||||
|
||||
use alloy_rpc_types::{BlockNumberOrTag, Filter};
|
||||
use alloy_rpc_types_eth::{BlockNumberOrTag, Filter};
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
@@ -58,14 +58,7 @@ impl Deployer {
|
||||
/// Construct a new view of the `Deployer`.
|
||||
pub async fn new(provider: Arc<RootProvider<SimpleRequest>>) -> Result<Option<Self>, Error> {
|
||||
let address = Self::address();
|
||||
#[cfg(not(test))]
|
||||
let required_block = BlockNumberOrTag::Finalized;
|
||||
#[cfg(test)]
|
||||
let required_block = BlockNumberOrTag::Latest;
|
||||
let code = provider
|
||||
.get_code_at(address.into(), required_block.into())
|
||||
.await
|
||||
.map_err(|_| Error::ConnectionError)?;
|
||||
let code = provider.get_code_at(address.into()).await.map_err(|_| Error::ConnectionError)?;
|
||||
// Contract has yet to be deployed
|
||||
if code.is_empty() {
|
||||
return Ok(None);
|
||||
|
||||
@@ -4,7 +4,7 @@ use alloy_core::primitives::{Address, B256, U256};
|
||||
|
||||
use alloy_sol_types::{SolInterface, SolEvent};
|
||||
|
||||
use alloy_rpc_types::Filter;
|
||||
use alloy_rpc_types_eth::Filter;
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ pub mod alloy {
|
||||
|
||||
pub use alloy_consensus as consensus;
|
||||
pub use alloy_network as network;
|
||||
pub use alloy_rpc_types as rpc_types;
|
||||
pub use alloy_rpc_types_eth as rpc_types;
|
||||
pub use alloy_simple_request_transport as simple_request_transport;
|
||||
pub use alloy_rpc_client as rpc_client;
|
||||
pub use alloy_provider as provider;
|
||||
|
||||
@@ -12,9 +12,9 @@ use alloy_consensus::TxLegacy;
|
||||
|
||||
use alloy_sol_types::{SolValue, SolConstructor, SolCall, SolEvent};
|
||||
|
||||
use alloy_rpc_types::Filter;
|
||||
use alloy_rpc_types_eth::Filter;
|
||||
#[cfg(test)]
|
||||
use alloy_rpc_types::{BlockId, TransactionRequest, TransactionInput};
|
||||
use alloy_rpc_types_eth::{BlockId, TransactionRequest, TransactionInput};
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use alloy_core::{
|
||||
};
|
||||
use alloy_consensus::{SignableTransaction, TxLegacy};
|
||||
|
||||
use alloy_rpc_types::{BlockNumberOrTag, TransactionReceipt};
|
||||
use alloy_rpc_types_eth::TransactionReceipt;
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
@@ -57,15 +57,14 @@ pub async fn send(
|
||||
// let chain_id = provider.get_chain_id().await.unwrap();
|
||||
// tx.chain_id = Some(chain_id);
|
||||
tx.chain_id = None;
|
||||
tx.nonce =
|
||||
provider.get_transaction_count(address, BlockNumberOrTag::Latest.into()).await.unwrap();
|
||||
tx.nonce = provider.get_transaction_count(address).await.unwrap();
|
||||
// 100 gwei
|
||||
tx.gas_price = 100_000_000_000u128;
|
||||
|
||||
let sig = wallet.sign_prehash_recoverable(tx.signature_hash().as_ref()).unwrap();
|
||||
assert_eq!(address, tx.clone().into_signed(sig.into()).recover_signer().unwrap());
|
||||
assert!(
|
||||
provider.get_balance(address, BlockNumberOrTag::Latest.into()).await.unwrap() >
|
||||
provider.get_balance(address).await.unwrap() >
|
||||
((U256::from(tx.gas_price) * U256::from(tx.gas_limit)) + tx.value)
|
||||
);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ use frost::{
|
||||
use alloy_core::primitives::{Address, U256};
|
||||
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_rpc_types_eth::BlockTransactionsKind;
|
||||
use alloy_rpc_client::ClientBuilder;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
@@ -84,7 +85,7 @@ async fn setup_test() -> (
|
||||
|
||||
async fn latest_block_hash(client: &RootProvider<SimpleRequest>) -> [u8; 32] {
|
||||
client
|
||||
.get_block(client.get_block_number().await.unwrap().into(), false)
|
||||
.get_block(client.get_block_number().await.unwrap().into(), BlockTransactionsKind::Hashes)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
|
||||
@@ -15,7 +15,7 @@ use alloy_core::primitives::Address;
|
||||
|
||||
use alloy_sol_types::SolCall;
|
||||
|
||||
use alloy_rpc_types::{TransactionInput, TransactionRequest};
|
||||
use alloy_rpc_types_eth::{TransactionInput, TransactionRequest};
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_rpc_client::ClientBuilder;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
@@ -6,7 +6,7 @@ license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -105,13 +105,13 @@ pub struct Metadata {
|
||||
/// but the payment ID will be returned here anyway:
|
||||
///
|
||||
/// 1) If the payment ID is tied to an output received by a subaddress account
|
||||
/// that spent Monero in the transaction (the received output is considered
|
||||
/// "change" and is not considered a "payment" in this case). If there are multiple
|
||||
/// spending subaddress accounts in a transaction, the highest index spent key image
|
||||
/// is used to determine the spending subaddress account.
|
||||
/// that spent Monero in the transaction (the received output is considered
|
||||
/// "change" and is not considered a "payment" in this case). If there are multiple
|
||||
/// spending subaddress accounts in a transaction, the highest index spent key image
|
||||
/// is used to determine the spending subaddress account.
|
||||
///
|
||||
/// 2) If the payment ID is the unencrypted variant and the block's hf version is
|
||||
/// v12 or higher (https://github.com/serai-dex/serai/issues/512)
|
||||
/// v12 or higher (https://github.com/serai-dex/serai/issues/512)
|
||||
pub payment_id: Option<PaymentId>,
|
||||
/// Arbitrary data encoded in TX extra.
|
||||
pub arbitrary_data: Vec<Vec<u8>>,
|
||||
|
||||
@@ -364,8 +364,8 @@ impl Change {
|
||||
/// 1) The change in the tx is shunted to the fee (fingerprintable fee).
|
||||
///
|
||||
/// 2) If there are 2 outputs in the tx, there would be no payment ID as is the case when the
|
||||
/// reference wallet creates 2 output txs, since monero-serai doesn't know which output
|
||||
/// to tie the dummy payment ID to.
|
||||
/// reference wallet creates 2 output txs, since monero-serai doesn't know which output
|
||||
/// to tie the dummy payment ID to.
|
||||
pub fn fingerprintable(address: Option<MoneroAddress>) -> Change {
|
||||
Change { address, view: None }
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/common/zalloc"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = []
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
@@ -19,8 +19,10 @@ workspace = true
|
||||
[dependencies]
|
||||
zeroize = { version = "^1.5", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
rustversion = { version = "1", default-features = false }
|
||||
|
||||
[features]
|
||||
std = ["zeroize/std"]
|
||||
default = ["std"]
|
||||
# Commented for now as it requires nightly and we don't use nightly
|
||||
# allocator = []
|
||||
allocator = []
|
||||
|
||||
10
common/zalloc/build.rs
Normal file
10
common/zalloc/build.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
#[rustversion::nightly]
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(zalloc_rustc_nightly)");
|
||||
println!("cargo::rustc-cfg=zalloc_rustc_nightly");
|
||||
}
|
||||
|
||||
#[rustversion::not(nightly)]
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(zalloc_rustc_nightly)");
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(feature = "allocator", feature(allocator_api))]
|
||||
#![cfg_attr(all(zalloc_rustc_nightly, feature = "allocator"), feature(allocator_api))]
|
||||
|
||||
//! Implementation of a Zeroizing Allocator, enabling zeroizing memory on deallocation.
|
||||
//! This can either be used with Box (requires nightly and the "allocator" feature) to provide the
|
||||
@@ -17,12 +17,12 @@ use zeroize::Zeroize;
|
||||
/// An allocator wrapper which zeroizes its memory on dealloc.
|
||||
pub struct ZeroizingAlloc<T>(pub T);
|
||||
|
||||
#[cfg(feature = "allocator")]
|
||||
#[cfg(all(zalloc_rustc_nightly, feature = "allocator"))]
|
||||
use core::{
|
||||
ptr::NonNull,
|
||||
alloc::{AllocError, Allocator},
|
||||
};
|
||||
#[cfg(feature = "allocator")]
|
||||
#[cfg(all(zalloc_rustc_nightly, feature = "allocator"))]
|
||||
unsafe impl<T: Allocator> Allocator for ZeroizingAlloc<T> {
|
||||
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||
self.0.allocate(layout)
|
||||
|
||||
@@ -122,7 +122,7 @@ impl QueuedBatchesDb {
|
||||
|
||||
pub fn take(txn: &mut impl DbTxn, set: ValidatorSet) -> Vec<Transaction> {
|
||||
let batches_vec = Self::get(txn, set).unwrap_or_default();
|
||||
txn.del(&Self::key(set));
|
||||
txn.del(Self::key(set));
|
||||
|
||||
let mut batches: &[u8] = &batches_vec;
|
||||
let mut res = vec![];
|
||||
|
||||
@@ -133,7 +133,13 @@ mod impl_pst_for_serai {
|
||||
key_pair: KeyPair,
|
||||
signature: Signature,
|
||||
) {
|
||||
let tx = SeraiValidatorSets::set_keys(set.network, removed, key_pair, signature);
|
||||
// TODO: BoundedVec as an arg to avoid this expect
|
||||
let tx = SeraiValidatorSets::set_keys(
|
||||
set.network,
|
||||
removed.try_into().expect("removing more than allowed"),
|
||||
key_pair,
|
||||
signature,
|
||||
);
|
||||
async fn check(serai: SeraiValidatorSets<'_>, set: ValidatorSet, (): ()) -> bool {
|
||||
if matches!(serai.keys(set).await, Ok(Some(_))) {
|
||||
log::info!("another coordinator set key pair for {:?}", set);
|
||||
|
||||
@@ -177,14 +177,14 @@ impl<N: Network> BlockData<N> {
|
||||
let new_block = last_block_or_round(&mut txn, LATEST_BLOCK_KEY, self.number.0)?;
|
||||
if new_block {
|
||||
// Delete the latest round key
|
||||
txn.del(&key(LATEST_ROUND_KEY));
|
||||
txn.del(key(LATEST_ROUND_KEY));
|
||||
}
|
||||
let new_round = last_block_or_round(&mut txn, LATEST_ROUND_KEY, round_number.0.into())?;
|
||||
if new_block || new_round {
|
||||
// Delete the messages for the old round
|
||||
txn.del(&key(PROPOSE_KEY));
|
||||
txn.del(&key(PEVOTE_KEY));
|
||||
txn.del(&key(PRECOMMIT_KEY));
|
||||
txn.del(key(PROPOSE_KEY));
|
||||
txn.del(key(PEVOTE_KEY));
|
||||
txn.del(key(PRECOMMIT_KEY));
|
||||
}
|
||||
|
||||
// Check we haven't sent this message within this round
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -64,10 +64,7 @@ pub struct GeneratorPromotion<C1: Ciphersuite, C2: Ciphersuite> {
|
||||
_c2: PhantomData<C2>,
|
||||
}
|
||||
|
||||
impl<C1: Ciphersuite, C2: Ciphersuite> GeneratorPromotion<C1, C2>
|
||||
where
|
||||
C2: Ciphersuite<F = C1::F, G = C1::G>,
|
||||
{
|
||||
impl<C1: Ciphersuite, C2: Ciphersuite<F = C1::F, G = C1::G>> GeneratorPromotion<C1, C2> {
|
||||
/// Begin promoting keys from one generator to another. Returns a proof this share was properly
|
||||
/// promoted.
|
||||
pub fn promote<R: RngCore + CryptoRng>(
|
||||
|
||||
@@ -6,7 +6,7 @@ license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dleq"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -53,11 +53,11 @@ pub(crate) struct Aos<G0: PrimeGroup + Zeroize, G1: PrimeGroup + Zeroize, const
|
||||
s: [(G0::Scalar, G1::Scalar); RING_LEN],
|
||||
}
|
||||
|
||||
impl<G0: PrimeGroup + Zeroize, G1: PrimeGroup + Zeroize, const RING_LEN: usize>
|
||||
Aos<G0, G1, RING_LEN>
|
||||
where
|
||||
G0::Scalar: PrimeFieldBits + Zeroize,
|
||||
G1::Scalar: PrimeFieldBits + Zeroize,
|
||||
impl<
|
||||
G0: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize,
|
||||
G1: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize,
|
||||
const RING_LEN: usize,
|
||||
> Aos<G0, G1, RING_LEN>
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn nonces<T: Transcript>(mut transcript: T, nonces: (G0, G1)) -> (G0::Scalar, G1::Scalar) {
|
||||
|
||||
@@ -76,14 +76,11 @@ pub(crate) struct Bits<
|
||||
}
|
||||
|
||||
impl<
|
||||
G0: PrimeGroup + Zeroize,
|
||||
G1: PrimeGroup + Zeroize,
|
||||
G0: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize,
|
||||
G1: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize,
|
||||
const SIGNATURE: u8,
|
||||
const RING_LEN: usize,
|
||||
> Bits<G0, G1, SIGNATURE, RING_LEN>
|
||||
where
|
||||
G0::Scalar: PrimeFieldBits + Zeroize,
|
||||
G1::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
fn transcript<T: Transcript>(transcript: &mut T, i: usize, commitments: (G0, G1)) {
|
||||
transcript.domain_separate(b"bits");
|
||||
|
||||
@@ -112,15 +112,12 @@ pub enum DLEqError {
|
||||
// anyone who wants it
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct __DLEqProof<
|
||||
G0: PrimeGroup + Zeroize,
|
||||
G1: PrimeGroup + Zeroize,
|
||||
G0: PrimeGroup<Scalar: PrimeFieldBits> + Zeroize,
|
||||
G1: PrimeGroup<Scalar: PrimeFieldBits> + Zeroize,
|
||||
const SIGNATURE: u8,
|
||||
const RING_LEN: usize,
|
||||
const REMAINDER_RING_LEN: usize,
|
||||
> where
|
||||
G0::Scalar: PrimeFieldBits,
|
||||
G1::Scalar: PrimeFieldBits,
|
||||
{
|
||||
> {
|
||||
bits: Vec<Bits<G0, G1, SIGNATURE, RING_LEN>>,
|
||||
remainder: Option<Bits<G0, G1, SIGNATURE, REMAINDER_RING_LEN>>,
|
||||
poks: (SchnorrPoK<G0>, SchnorrPoK<G1>),
|
||||
@@ -200,15 +197,12 @@ dleq!(
|
||||
);
|
||||
|
||||
impl<
|
||||
G0: PrimeGroup + Zeroize,
|
||||
G1: PrimeGroup + Zeroize,
|
||||
G0: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize,
|
||||
G1: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize,
|
||||
const SIGNATURE: u8,
|
||||
const RING_LEN: usize,
|
||||
const REMAINDER_RING_LEN: usize,
|
||||
> __DLEqProof<G0, G1, SIGNATURE, RING_LEN, REMAINDER_RING_LEN>
|
||||
where
|
||||
G0::Scalar: PrimeFieldBits + Zeroize,
|
||||
G1::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
pub(crate) fn transcript<T: Transcript>(
|
||||
transcript: &mut T,
|
||||
|
||||
@@ -28,10 +28,7 @@ pub(crate) struct SchnorrPoK<G: PrimeGroup + Zeroize> {
|
||||
s: G::Scalar,
|
||||
}
|
||||
|
||||
impl<G: PrimeGroup + Zeroize> SchnorrPoK<G>
|
||||
where
|
||||
G::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
impl<G: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize> SchnorrPoK<G> {
|
||||
// Not HRAm due to the lack of m
|
||||
#[allow(non_snake_case)]
|
||||
fn hra<T: Transcript>(transcript: &mut T, generator: G, R: G, A: G) -> G::Scalar {
|
||||
|
||||
@@ -105,19 +105,13 @@ pub enum DLEqError {
|
||||
|
||||
/// A proof that points have the same discrete logarithm across generators.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct DLEqProof<G: PrimeGroup>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
pub struct DLEqProof<G: PrimeGroup<Scalar: Zeroize>> {
|
||||
c: G::Scalar,
|
||||
s: G::Scalar,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl<G: PrimeGroup> DLEqProof<G>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
impl<G: PrimeGroup<Scalar: Zeroize>> DLEqProof<G> {
|
||||
fn transcript<T: Transcript>(transcript: &mut T, generator: G, nonce: G, point: G) {
|
||||
transcript.append_message(b"generator", generator.to_bytes());
|
||||
transcript.append_message(b"nonce", nonce.to_bytes());
|
||||
@@ -213,20 +207,14 @@ where
|
||||
/// across some generators, yet with a smaller overall proof size.
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct MultiDLEqProof<G: PrimeGroup>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
pub struct MultiDLEqProof<G: PrimeGroup<Scalar: Zeroize>> {
|
||||
c: G::Scalar,
|
||||
s: Vec<G::Scalar>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(non_snake_case)]
|
||||
impl<G: PrimeGroup> MultiDLEqProof<G>
|
||||
where
|
||||
G::Scalar: Zeroize,
|
||||
{
|
||||
impl<G: PrimeGroup<Scalar: Zeroize>> MultiDLEqProof<G> {
|
||||
/// Prove for each scalar that the series of points created by multiplying it against its
|
||||
/// matching generators share a discrete logarithm.
|
||||
/// This function panics if `generators.len() != scalars.len()`.
|
||||
|
||||
@@ -14,10 +14,7 @@ use transcript::{Transcript, RecommendedTranscript};
|
||||
|
||||
use crate::cross_group::schnorr::SchnorrPoK;
|
||||
|
||||
fn test_schnorr<G: PrimeGroup + Zeroize>()
|
||||
where
|
||||
G::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
fn test_schnorr<G: PrimeGroup<Scalar: PrimeFieldBits + Zeroize> + Zeroize>() {
|
||||
let transcript = RecommendedTranscript::new(b"Schnorr Test");
|
||||
|
||||
let mut batch = BatchVerifier::new(10);
|
||||
|
||||
@@ -34,7 +34,7 @@ macro_rules! math_op {
|
||||
impl $Op<$Other> for $Value {
|
||||
type Output = $Value;
|
||||
fn $op_fn(self, other: $Other) -> Self::Output {
|
||||
Self($function(self.0, other.0))
|
||||
$Value($function(self.0, other.0))
|
||||
}
|
||||
}
|
||||
impl $Assign<$Other> for $Value {
|
||||
@@ -45,7 +45,7 @@ macro_rules! math_op {
|
||||
impl<'a> $Op<&'a $Other> for $Value {
|
||||
type Output = $Value;
|
||||
fn $op_fn(self, other: &'a $Other) -> Self::Output {
|
||||
Self($function(self.0, other.0))
|
||||
$Value($function(self.0, other.0))
|
||||
}
|
||||
}
|
||||
impl<'a> $Assign<&'a $Other> for $Value {
|
||||
@@ -60,7 +60,7 @@ macro_rules! from_wrapper {
|
||||
($wrapper: ident, $inner: ident, $uint: ident) => {
|
||||
impl From<$uint> for $wrapper {
|
||||
fn from(a: $uint) -> $wrapper {
|
||||
Self(Residue::new(&$inner::from(a)))
|
||||
$wrapper(Residue::new(&$inner::from(a)))
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -127,7 +127,7 @@ macro_rules! field {
|
||||
impl Neg for $FieldName {
|
||||
type Output = $FieldName;
|
||||
fn neg(self) -> $FieldName {
|
||||
Self(self.0.neg())
|
||||
$FieldName(self.0.neg())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,13 +141,13 @@ macro_rules! field {
|
||||
impl $FieldName {
|
||||
/// Perform an exponentiation.
|
||||
pub fn pow(&self, other: $FieldName) -> $FieldName {
|
||||
let mut table = [Self(Residue::ONE); 16];
|
||||
let mut table = [$FieldName(Residue::ONE); 16];
|
||||
table[1] = *self;
|
||||
for i in 2 .. 16 {
|
||||
table[i] = table[i - 1] * self;
|
||||
}
|
||||
|
||||
let mut res = Self(Residue::ONE);
|
||||
let mut res = $FieldName(Residue::ONE);
|
||||
let mut bits = 0;
|
||||
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||
bits <<= 1;
|
||||
@@ -170,8 +170,8 @@ macro_rules! field {
|
||||
}
|
||||
|
||||
impl Field for $FieldName {
|
||||
const ZERO: Self = Self(Residue::ZERO);
|
||||
const ONE: Self = Self(Residue::ONE);
|
||||
const ZERO: Self = $FieldName(Residue::ZERO);
|
||||
const ONE: Self = $FieldName(Residue::ONE);
|
||||
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
let mut bytes = [0; 112];
|
||||
@@ -188,12 +188,12 @@ macro_rules! field {
|
||||
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
const NEG_2: $FieldName =
|
||||
Self($ResidueType::sub(&$ResidueType::ZERO, &$ResidueType::new(&U448::from_u8(2))));
|
||||
$FieldName($ResidueType::sub(&$ResidueType::ZERO, &$ResidueType::new(&U448::from_u8(2))));
|
||||
CtOption::new(self.pow(NEG_2), !self.is_zero())
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
const MOD_1_4: $FieldName = Self($ResidueType::new(
|
||||
const MOD_1_4: $FieldName = $FieldName($ResidueType::new(
|
||||
&$MODULUS.saturating_add(&U448::ONE).wrapping_div(&U448::from_u8(4)),
|
||||
));
|
||||
|
||||
@@ -217,14 +217,14 @@ macro_rules! field {
|
||||
const TWO_INV: Self = $FieldName($ResidueType::new(&U448::from_u8(2)).invert().0);
|
||||
|
||||
const MULTIPLICATIVE_GENERATOR: Self =
|
||||
Self(Residue::new(&U448::from_u8($MULTIPLICATIVE_GENERATOR)));
|
||||
$FieldName(Residue::new(&U448::from_u8($MULTIPLICATIVE_GENERATOR)));
|
||||
// True for both the Ed448 Scalar field and FieldElement field
|
||||
const S: u32 = 1;
|
||||
|
||||
// Both fields have their root of unity as -1
|
||||
const ROOT_OF_UNITY: Self =
|
||||
Self($ResidueType::sub(&$ResidueType::ZERO, &$ResidueType::new(&U448::ONE)));
|
||||
const ROOT_OF_UNITY_INV: Self = Self(Self::ROOT_OF_UNITY.0.invert().0);
|
||||
$FieldName($ResidueType::sub(&$ResidueType::ZERO, &$ResidueType::new(&U448::ONE)));
|
||||
const ROOT_OF_UNITY_INV: Self = $FieldName(Self::ROOT_OF_UNITY.0.invert().0);
|
||||
|
||||
const DELTA: Self = $FieldName(Residue::new(&U448::from_le_hex($DELTA)));
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ff-group-te
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["ff", "group", "ecc"]
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -178,10 +178,7 @@ pub fn test_prime_group<R: RngCore, G: PrimeGroup>(rng: &mut R) {
|
||||
}
|
||||
|
||||
/// Run all tests offered by this crate on the group.
|
||||
pub fn test_prime_group_bits<R: RngCore, G: PrimeGroup>(rng: &mut R)
|
||||
where
|
||||
G::Scalar: PrimeFieldBits,
|
||||
{
|
||||
pub fn test_prime_group_bits<R: RngCore, G: PrimeGroup<Scalar: PrimeFieldBits>>(rng: &mut R) {
|
||||
test_prime_field_bits::<R, G::Scalar>(rng);
|
||||
test_prime_group::<R, G>(rng);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/frost"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["frost", "multisig", "threshold"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -10,7 +10,7 @@ integrating with existing systems.
|
||||
|
||||
This library offers ciphersuites compatible with the
|
||||
[IETF draft](https://github.com/cfrg/draft-irtf-cfrg-frost). Currently, version
|
||||
11 is supported.
|
||||
15 is supported.
|
||||
|
||||
This library was
|
||||
[audited by Cypher Stack in March 2023](https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf),
|
||||
|
||||
@@ -362,9 +362,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
||||
rho_transcript.append_message(b"message", C::hash_msg(msg));
|
||||
rho_transcript.append_message(
|
||||
b"preprocesses",
|
||||
&C::hash_commitments(
|
||||
self.params.algorithm.transcript().challenge(b"preprocesses").as_ref(),
|
||||
),
|
||||
C::hash_commitments(self.params.algorithm.transcript().challenge(b"preprocesses").as_ref()),
|
||||
);
|
||||
|
||||
// Generate the per-signer binding factors
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/multiexp"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["multiexp", "ff", "group"]
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -12,27 +12,21 @@ use crate::{multiexp, multiexp_vartime};
|
||||
// Flatten the contained statements to a single Vec.
|
||||
// Wrapped in Zeroizing in case any of the included statements contain private values.
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn flat<Id: Copy + Zeroize, G: Group + Zeroize>(
|
||||
fn flat<Id: Copy + Zeroize, G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>(
|
||||
slice: &[(Id, Vec<(G::Scalar, G)>)],
|
||||
) -> Zeroizing<Vec<(G::Scalar, G)>>
|
||||
where
|
||||
<G as Group>::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
) -> Zeroizing<Vec<(G::Scalar, G)>> {
|
||||
Zeroizing::new(slice.iter().flat_map(|pairs| pairs.1.iter()).copied().collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// A batch verifier intended to verify a series of statements are each equivalent to zero.
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[derive(Clone, Zeroize)]
|
||||
pub struct BatchVerifier<Id: Copy + Zeroize, G: Group + Zeroize>(
|
||||
pub struct BatchVerifier<Id: Copy + Zeroize, G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>(
|
||||
Zeroizing<Vec<(Id, Vec<(G::Scalar, G)>)>>,
|
||||
)
|
||||
where
|
||||
<G as Group>::Scalar: PrimeFieldBits + Zeroize;
|
||||
);
|
||||
|
||||
impl<Id: Copy + Zeroize, G: Group + Zeroize> BatchVerifier<Id, G>
|
||||
where
|
||||
<G as Group>::Scalar: PrimeFieldBits + Zeroize,
|
||||
impl<Id: Copy + Zeroize, G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>
|
||||
BatchVerifier<Id, G>
|
||||
{
|
||||
/// Create a new batch verifier, expected to verify the following amount of statements.
|
||||
///
|
||||
|
||||
@@ -49,10 +49,10 @@ fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
||||
|
||||
// Convert scalars to `window`-sized bit groups, as needed to index a table
|
||||
// This algorithm works for `window <= 8`
|
||||
pub(crate) fn prep_bits<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> Vec<Vec<u8>>
|
||||
where
|
||||
G::Scalar: PrimeFieldBits,
|
||||
{
|
||||
pub(crate) fn prep_bits<G: Group<Scalar: PrimeFieldBits>>(
|
||||
pairs: &[(G::Scalar, G)],
|
||||
window: u8,
|
||||
) -> Vec<Vec<u8>> {
|
||||
let w_usize = usize::from(window);
|
||||
|
||||
let mut groupings = vec![];
|
||||
@@ -175,10 +175,7 @@ fn algorithm(len: usize) -> Algorithm {
|
||||
|
||||
/// Performs a multiexponentiation, automatically selecting the optimal algorithm based on the
|
||||
/// amount of pairs.
|
||||
pub fn multiexp<G: Group>(pairs: &[(G::Scalar, G)]) -> G
|
||||
where
|
||||
G::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
pub fn multiexp<G: Group<Scalar: PrimeFieldBits + Zeroize>>(pairs: &[(G::Scalar, G)]) -> G {
|
||||
match algorithm(pairs.len()) {
|
||||
Algorithm::Null => Group::identity(),
|
||||
Algorithm::Single => pairs[0].1 * pairs[0].0,
|
||||
@@ -190,10 +187,7 @@ where
|
||||
|
||||
/// Performs a multiexponentiation in variable time, automatically selecting the optimal algorithm
|
||||
/// based on the amount of pairs.
|
||||
pub fn multiexp_vartime<G: Group>(pairs: &[(G::Scalar, G)]) -> G
|
||||
where
|
||||
G::Scalar: PrimeFieldBits,
|
||||
{
|
||||
pub fn multiexp_vartime<G: Group<Scalar: PrimeFieldBits>>(pairs: &[(G::Scalar, G)]) -> G {
|
||||
match algorithm(pairs.len()) {
|
||||
Algorithm::Null => Group::identity(),
|
||||
Algorithm::Single => pairs[0].1 * pairs[0].0,
|
||||
|
||||
@@ -7,10 +7,10 @@ use crate::prep_bits;
|
||||
|
||||
// Pippenger's algorithm for multiexponentiation, as published in the SIAM Journal on Computing
|
||||
// DOI: 10.1137/0209022
|
||||
pub(crate) fn pippenger<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> G
|
||||
where
|
||||
G::Scalar: PrimeFieldBits,
|
||||
{
|
||||
pub(crate) fn pippenger<G: Group<Scalar: PrimeFieldBits>>(
|
||||
pairs: &[(G::Scalar, G)],
|
||||
window: u8,
|
||||
) -> G {
|
||||
let mut bits = prep_bits(pairs, window);
|
||||
|
||||
let mut res = G::identity();
|
||||
@@ -37,10 +37,10 @@ where
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn pippenger_vartime<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> G
|
||||
where
|
||||
G::Scalar: PrimeFieldBits,
|
||||
{
|
||||
pub(crate) fn pippenger_vartime<G: Group<Scalar: PrimeFieldBits>>(
|
||||
pairs: &[(G::Scalar, G)],
|
||||
window: u8,
|
||||
) -> G {
|
||||
let bits = prep_bits(pairs, window);
|
||||
|
||||
let mut res = G::identity();
|
||||
|
||||
@@ -24,10 +24,10 @@ fn prep_tables<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> Vec<Vec<G>> {
|
||||
|
||||
// Straus's algorithm for multiexponentiation, as published in The American Mathematical Monthly
|
||||
// DOI: 10.2307/2310929
|
||||
pub(crate) fn straus<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> G
|
||||
where
|
||||
G::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
pub(crate) fn straus<G: Group<Scalar: PrimeFieldBits + Zeroize>>(
|
||||
pairs: &[(G::Scalar, G)],
|
||||
window: u8,
|
||||
) -> G {
|
||||
let mut groupings = prep_bits(pairs, window);
|
||||
let tables = prep_tables(pairs, window);
|
||||
|
||||
@@ -48,10 +48,10 @@ where
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn straus_vartime<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> G
|
||||
where
|
||||
G::Scalar: PrimeFieldBits,
|
||||
{
|
||||
pub(crate) fn straus_vartime<G: Group<Scalar: PrimeFieldBits>>(
|
||||
pairs: &[(G::Scalar, G)],
|
||||
window: u8,
|
||||
) -> G {
|
||||
let groupings = prep_bits(pairs, window);
|
||||
let tables = prep_tables(pairs, window);
|
||||
|
||||
|
||||
@@ -9,10 +9,7 @@ use group::Group;
|
||||
|
||||
use crate::BatchVerifier;
|
||||
|
||||
pub(crate) fn test_batch<G: Group + Zeroize>()
|
||||
where
|
||||
G::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
pub(crate) fn test_batch<G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>() {
|
||||
let valid = |batch: BatchVerifier<_, G>| {
|
||||
assert!(batch.verify());
|
||||
assert!(batch.verify_vartime());
|
||||
|
||||
@@ -18,10 +18,7 @@ mod batch;
|
||||
use batch::test_batch;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn benchmark_internal<G: Group>(straus_bool: bool)
|
||||
where
|
||||
G::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
fn benchmark_internal<G: Group<Scalar: PrimeFieldBits + Zeroize>>(straus_bool: bool) {
|
||||
let runs: usize = 20;
|
||||
|
||||
let mut start = 0;
|
||||
@@ -86,10 +83,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn test_multiexp<G: Group>()
|
||||
where
|
||||
G::Scalar: PrimeFieldBits + Zeroize,
|
||||
{
|
||||
fn test_multiexp<G: Group<Scalar: PrimeFieldBits + Zeroize>>() {
|
||||
let test = |pairs: &[_], sum| {
|
||||
// These should automatically determine the best algorithm
|
||||
assert_eq!(multiexp(pairs), sum);
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/schnorr"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["schnorr", "ff", "group"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -52,7 +52,7 @@ fn test_rfc8032() {
|
||||
SchnorrSignature::<Ed25519>::read::<&[u8]>(&mut hex::decode(vector.2).unwrap().as_ref())
|
||||
.unwrap();
|
||||
let hram = Sha512::new_with_prefix(
|
||||
&[sig.R.to_bytes().as_ref(), &key.to_bytes(), &hex::decode(vector.1).unwrap()].concat(),
|
||||
[sig.R.to_bytes().as_ref(), &key.to_bytes(), &hex::decode(vector.1).unwrap()].concat(),
|
||||
);
|
||||
assert!(sig.verify(key, Scalar::from_hash(hram)));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/schnorrkel"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["frost", "multisig", "threshold", "schnorrkel"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/transcript"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["transcript"]
|
||||
edition = "2021"
|
||||
rust-version = "1.73"
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
Flexible Transcript is a crate offering:
|
||||
- `Transcript`, a trait offering functions transcripts should implement.
|
||||
- `DigestTranscript`, a competent transcript format instantiated against a
|
||||
provided hash function.
|
||||
provided hash function.
|
||||
- `MerlinTranscript`, a wrapper of `merlin` into the trait (available via the
|
||||
`merlin` feature).
|
||||
`merlin` feature).
|
||||
- `RecommendedTranscript`, a transcript recommended for usage in applications.
|
||||
Currently, this is `DigestTranscript<Blake2b512>` (available via the
|
||||
`recommended` feature).
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use crate::Transcript;
|
||||
|
||||
/// Test the sanity of a transcript.
|
||||
pub fn test_transcript<T: Transcript>()
|
||||
where
|
||||
T::Challenge: PartialEq,
|
||||
{
|
||||
pub fn test_transcript<T: Transcript<Challenge: PartialEq>>() {
|
||||
// Ensure distinct names cause distinct challenges
|
||||
{
|
||||
let mut t1 = T::new(b"1");
|
||||
|
||||
14
deny.toml
14
deny.toml
@@ -1,19 +1,19 @@
|
||||
[advisories]
|
||||
version = 2
|
||||
|
||||
db-path = "~/.cargo/advisory-db"
|
||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
||||
|
||||
vulnerability = "deny"
|
||||
yanked = "deny"
|
||||
notice = "warn"
|
||||
unmaintained = "warn"
|
||||
|
||||
ignore = [
|
||||
"RUSTSEC-2020-0168", # mach is unmaintained
|
||||
"RUSTSEC-2021-0139", # https://github.com/serai-dex/serai/228
|
||||
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227
|
||||
]
|
||||
|
||||
[licenses]
|
||||
unlicensed = "deny"
|
||||
version = 2
|
||||
|
||||
allow = [
|
||||
# Effective public domain
|
||||
@@ -36,14 +36,11 @@ allow = [
|
||||
"GPL-3.0 WITH Classpath-exception-2.0",
|
||||
]
|
||||
|
||||
copyleft = "deny"
|
||||
allow-osi-fsf-free = "neither"
|
||||
default = "deny"
|
||||
|
||||
exceptions = [
|
||||
{ allow = ["AGPL-3.0"], name = "serai-env" },
|
||||
|
||||
{ allow = ["AGPL-3.0"], name = "ethereum-serai" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-ethereum-relayer" },
|
||||
|
||||
{ allow = ["AGPL-3.0"], name = "serai-message-queue" },
|
||||
|
||||
@@ -101,7 +98,6 @@ allow-git = [
|
||||
"https://github.com/rust-lang-nursery/lazy-static.rs",
|
||||
"https://github.com/serai-dex/substrate-bip39",
|
||||
"https://github.com/serai-dex/substrate",
|
||||
"https://github.com/alloy-rs/alloy",
|
||||
"https://github.com/monero-rs/base58-monero",
|
||||
"https://github.com/orcalabs/dockertest-rs",
|
||||
]
|
||||
|
||||
@@ -13,7 +13,7 @@ GEM
|
||||
forwardable-extended (2.6.0)
|
||||
google-protobuf (3.25.3-x86_64-linux)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (1.14.4)
|
||||
i18n (1.14.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (4.3.3)
|
||||
addressable (~> 2.4)
|
||||
@@ -55,17 +55,19 @@ GEM
|
||||
mercenary (0.4.0)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (5.0.4)
|
||||
rake (13.1.0)
|
||||
public_suffix (5.0.5)
|
||||
rake (13.2.1)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
rb-inotify (0.11.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.6)
|
||||
rouge (4.2.0)
|
||||
rexml (3.2.8)
|
||||
strscan (>= 3.0.9)
|
||||
rouge (4.2.1)
|
||||
safe_yaml (1.0.5)
|
||||
sass-embedded (1.63.6)
|
||||
google-protobuf (~> 3.23)
|
||||
rake (>= 13.0.0)
|
||||
strscan (3.1.0)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
unicode-display_width (2.5.0)
|
||||
|
||||
11
orchestration/dev/coins/ethereum-relayer/.folder
Normal file
11
orchestration/dev/coins/ethereum-relayer/.folder
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
RPC_USER="${RPC_USER:=serai}"
|
||||
RPC_PASS="${RPC_PASS:=seraidex}"
|
||||
|
||||
# Run Monero
|
||||
monerod --non-interactive --regtest --offline --fixed-difficulty=1 \
|
||||
--no-zmq --rpc-bind-ip=0.0.0.0 --rpc-bind-port=18081 --confirm-external-bind \
|
||||
--rpc-access-control-origins "*" --disable-rpc-ban \
|
||||
--rpc-login=$RPC_USER:$RPC_PASS \
|
||||
$1
|
||||
@@ -1,5 +1,5 @@
|
||||
# rust:1.77.0-slim-bookworm as of March 22nd, 2024 (GMT)
|
||||
FROM --platform=linux/amd64 rust@sha256:e785e4aa81f87bc1ee02fa2026ffbc491e0410bdaf6652cea74884373f452664 as deterministic
|
||||
# rust:1.79.0-slim-bookworm as of June 14th, 2024 (GMT)
|
||||
FROM --platform=linux/amd64 rust@sha256:fa189cd885739dd17fc6bb4e132687fce43f2bf42983c0ac39b60e4943201e9c as deterministic
|
||||
|
||||
# Move to a Debian package snapshot
|
||||
RUN rm -rf /etc/apt/sources.list.d/debian.sources && \
|
||||
|
||||
@@ -17,6 +17,7 @@ pub fn coordinator(
|
||||
let longer_reattempts = if network == Network::Dev { "longer-reattempts" } else { "" };
|
||||
let setup = mimalloc(Os::Debian).to_string() +
|
||||
&build_serai_service(
|
||||
"",
|
||||
network.release(),
|
||||
&format!("{db} {longer_reattempts}"),
|
||||
"serai-coordinator",
|
||||
|
||||
39
orchestration/src/ethereum_relayer.rs
Normal file
39
orchestration/src/ethereum_relayer.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile};
|
||||
|
||||
pub fn ethereum_relayer(orchestration_path: &Path, network: Network) {
|
||||
let setup = mimalloc(Os::Debian).to_string() +
|
||||
&build_serai_service("", network.release(), network.db(), "serai-ethereum-relayer");
|
||||
|
||||
let env_vars = [
|
||||
("DB_PATH", "/volume/ethereum-relayer-db".to_string()),
|
||||
("RUST_LOG", "info,serai_ethereum_relayer=trace".to_string()),
|
||||
];
|
||||
let mut env_vars_str = String::new();
|
||||
for (env_var, value) in env_vars {
|
||||
env_vars_str += &format!(r#"{env_var}=${{{env_var}:="{value}"}} "#);
|
||||
}
|
||||
|
||||
let run_ethereum_relayer = format!(
|
||||
r#"
|
||||
# Copy the relayer server binary and relevant license
|
||||
COPY --from=builder --chown=ethereumrelayer /serai/bin/serai-ethereum-relayer /bin
|
||||
|
||||
# Run ethereum-relayer
|
||||
EXPOSE 20830
|
||||
EXPOSE 20831
|
||||
CMD {env_vars_str} serai-ethereum-relayer
|
||||
"#
|
||||
);
|
||||
|
||||
let run = os(Os::Debian, "", "ethereumrelayer") + &run_ethereum_relayer;
|
||||
let res = setup + &run;
|
||||
|
||||
let mut ethereum_relayer_path = orchestration_path.to_path_buf();
|
||||
ethereum_relayer_path.push("coins");
|
||||
ethereum_relayer_path.push("ethereum-relayer");
|
||||
ethereum_relayer_path.push("Dockerfile");
|
||||
|
||||
write_dockerfile(ethereum_relayer_path, &res);
|
||||
}
|
||||
@@ -32,6 +32,9 @@ use mimalloc::mimalloc;
|
||||
mod coins;
|
||||
use coins::*;
|
||||
|
||||
mod ethereum_relayer;
|
||||
use ethereum_relayer::ethereum_relayer;
|
||||
|
||||
mod message_queue;
|
||||
use message_queue::message_queue;
|
||||
|
||||
@@ -137,13 +140,13 @@ WORKDIR /home/{user}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_serai_service(release: bool, features: &str, package: &str) -> String {
|
||||
fn build_serai_service(prelude: &str, release: bool, features: &str, package: &str) -> String {
|
||||
let profile = if release { "release" } else { "debug" };
|
||||
let profile_flag = if release { "--release" } else { "" };
|
||||
|
||||
format!(
|
||||
r#"
|
||||
FROM rust:1.77-slim-bookworm as builder
|
||||
FROM rust:1.79-slim-bookworm as builder
|
||||
|
||||
COPY --from=mimalloc-debian libmimalloc.so /usr/lib
|
||||
RUN echo "/usr/lib/libmimalloc.so" >> /etc/ld.so.preload
|
||||
@@ -159,6 +162,8 @@ RUN apt install -y make protobuf-compiler
|
||||
# Add the wasm toolchain
|
||||
RUN rustup target add wasm32-unknown-unknown
|
||||
|
||||
{prelude}
|
||||
|
||||
# Add files for build
|
||||
ADD patches /serai/patches
|
||||
ADD common /serai/common
|
||||
@@ -278,6 +283,8 @@ fn dockerfiles(network: Network) {
|
||||
let ethereum_key = infrastructure_keys.remove("ethereum").unwrap();
|
||||
let monero_key = infrastructure_keys.remove("monero").unwrap();
|
||||
|
||||
ethereum_relayer(&orchestration_path, network);
|
||||
|
||||
message_queue(
|
||||
&orchestration_path,
|
||||
network,
|
||||
@@ -361,6 +368,7 @@ fn start(network: Network, services: HashSet<String>) {
|
||||
let name = match service.as_ref() {
|
||||
"serai" => "serai",
|
||||
"coordinator" => "coordinator",
|
||||
"ethereum-relayer" => "ethereum-relayer",
|
||||
"message-queue" => "message-queue",
|
||||
"bitcoin-daemon" => "bitcoin",
|
||||
"bitcoin-processor" => "bitcoin-processor",
|
||||
@@ -374,23 +382,17 @@ fn start(network: Network, services: HashSet<String>) {
|
||||
let serai_runtime_volume = format!("serai-{}-runtime-volume", network.label());
|
||||
if name == "serai" {
|
||||
// Check if it's built by checking if the volume has the expected runtime file
|
||||
let wasm_build_container_name = format!("serai-{}-runtime", network.label());
|
||||
let built = || {
|
||||
if let Ok(path) = Command::new("docker")
|
||||
.arg("volume")
|
||||
if let Ok(state_and_status) = Command::new("docker")
|
||||
.arg("inspect")
|
||||
.arg("-f")
|
||||
.arg("{{ .Mountpoint }}")
|
||||
.arg(&serai_runtime_volume)
|
||||
.arg("{{.State.Status}}:{{.State.ExitCode}}")
|
||||
.arg(&wasm_build_container_name)
|
||||
.output()
|
||||
{
|
||||
if let Ok(path) = String::from_utf8(path.stdout) {
|
||||
if let Ok(iter) = std::fs::read_dir(PathBuf::from(path.trim())) {
|
||||
for item in iter.flatten() {
|
||||
if item.file_name() == "serai.wasm" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(state_and_status) = String::from_utf8(state_and_status.stdout) {
|
||||
return state_and_status.trim() == "exited:0";
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -493,6 +495,10 @@ fn start(network: Network, services: HashSet<String>) {
|
||||
command
|
||||
}
|
||||
}
|
||||
"ethereum-relayer" => {
|
||||
// Expose the router command fetch server
|
||||
command.arg("-p").arg("20831:20831")
|
||||
}
|
||||
"monero" => {
|
||||
// Expose the RPC for tests
|
||||
if network == Network::Dev {
|
||||
@@ -559,6 +565,9 @@ Commands:
|
||||
- `message-queue`
|
||||
- `bitcoin-daemon`
|
||||
- `bitcoin-processor`
|
||||
- `ethereum-daemon`
|
||||
- `ethereum-processor`
|
||||
- `ethereum-relayer`
|
||||
- `monero-daemon`
|
||||
- `monero-processor`
|
||||
- `monero-wallet-rpc` (if "dev")
|
||||
@@ -591,6 +600,9 @@ Commands:
|
||||
Some("start") => {
|
||||
let mut services = HashSet::new();
|
||||
for arg in args {
|
||||
if arg == "ethereum-processor" {
|
||||
services.insert("ethereum-relayer".to_string());
|
||||
}
|
||||
if let Some(ext_network) = arg.strip_suffix("-processor") {
|
||||
services.insert(ext_network.to_string() + "-daemon");
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn message_queue(
|
||||
monero_key: <Ristretto as Ciphersuite>::G,
|
||||
) {
|
||||
let setup = mimalloc(Os::Debian).to_string() +
|
||||
&build_serai_service(network.release(), network.db(), "serai-message-queue");
|
||||
&build_serai_service("", network.release(), network.db(), "serai-message-queue");
|
||||
|
||||
let env_vars = [
|
||||
("COORDINATOR_KEY", hex::encode(coordinator_key.to_bytes())),
|
||||
|
||||
@@ -17,6 +17,15 @@ pub fn processor(
|
||||
) {
|
||||
let setup = mimalloc(Os::Debian).to_string() +
|
||||
&build_serai_service(
|
||||
if coin == "ethereum" {
|
||||
r#"
|
||||
RUN cargo install svm-rs
|
||||
RUN svm install 0.8.25
|
||||
RUN svm use 0.8.25
|
||||
"#
|
||||
} else {
|
||||
""
|
||||
},
|
||||
network.release(),
|
||||
&format!("binaries {} {coin}", network.db()),
|
||||
"serai-processor",
|
||||
@@ -32,24 +41,32 @@ RUN apt install -y ca-certificates
|
||||
const RPC_PASS: &str = "seraidex";
|
||||
// TODO: Isolate networks
|
||||
let hostname = format!("serai-{}-{coin}", network.label());
|
||||
let port = match coin {
|
||||
"bitcoin" => 8332,
|
||||
"ethereum" => return, // TODO
|
||||
"monero" => 18081,
|
||||
_ => panic!("unrecognized external network"),
|
||||
};
|
||||
let port = format!(
|
||||
"{}",
|
||||
match coin {
|
||||
"bitcoin" => 8332,
|
||||
"ethereum" => 8545,
|
||||
"monero" => 18081,
|
||||
_ => panic!("unrecognized external network"),
|
||||
}
|
||||
);
|
||||
|
||||
let env_vars = [
|
||||
let mut env_vars = vec![
|
||||
("MESSAGE_QUEUE_RPC", format!("serai-{}-message-queue", network.label())),
|
||||
("MESSAGE_QUEUE_KEY", hex::encode(coin_key.to_repr())),
|
||||
("ENTROPY", hex::encode(entropy.as_ref())),
|
||||
("NETWORK", coin.to_string()),
|
||||
("NETWORK_RPC_LOGIN", format!("{RPC_USER}:{RPC_PASS}")),
|
||||
("NETWORK_RPC_HOSTNAME", hostname),
|
||||
("NETWORK_RPC_PORT", format!("{port}")),
|
||||
("NETWORK_RPC_PORT", port),
|
||||
("DB_PATH", "/volume/processor-db".to_string()),
|
||||
("RUST_LOG", "info,serai_processor=debug".to_string()),
|
||||
];
|
||||
if coin == "ethereum" {
|
||||
env_vars
|
||||
.push(("ETHEREUM_RELAYER_HOSTNAME", format!("serai-{}-ethereum-relayer", network.label())));
|
||||
env_vars.push(("ETHEREUM_RELAYER_PORT", "20830".to_string()));
|
||||
}
|
||||
let mut env_vars_str = String::new();
|
||||
for (env_var, value) in env_vars {
|
||||
env_vars_str += &format!(r#"{env_var}=${{{env_var}:="{value}"}} "#);
|
||||
|
||||
@@ -11,9 +11,9 @@ pub fn serai(
|
||||
serai_key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||
) {
|
||||
// Always builds in release for performance reasons
|
||||
let setup = mimalloc(Os::Debian).to_string() + &build_serai_service(true, "", "serai-node");
|
||||
let setup = mimalloc(Os::Debian).to_string() + &build_serai_service("", true, "", "serai-node");
|
||||
let setup_fast_epoch =
|
||||
mimalloc(Os::Debian).to_string() + &build_serai_service(true, "fast-epoch", "serai-node");
|
||||
mimalloc(Os::Debian).to_string() + &build_serai_service("", true, "fast-epoch", "serai-node");
|
||||
|
||||
let env_vars = [("KEY", hex::encode(serai_key.to_repr()))];
|
||||
let mut env_vars_str = String::new();
|
||||
|
||||
11
orchestration/testnet/coins/ethereum-relayer/.folder
Normal file
11
orchestration/testnet/coins/ethereum-relayer/.folder
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
RPC_USER="${RPC_USER:=serai}"
|
||||
RPC_PASS="${RPC_PASS:=seraidex}"
|
||||
|
||||
# Run Monero
|
||||
monerod --non-interactive --regtest --offline --fixed-difficulty=1 \
|
||||
--no-zmq --rpc-bind-ip=0.0.0.0 --rpc-bind-port=18081 --confirm-external-bind \
|
||||
--rpc-access-control-origins "*" --disable-rpc-ban \
|
||||
--rpc-login=$RPC_USER:$RPC_PASS \
|
||||
$1
|
||||
17
patches/parking_lot/Cargo.toml
Normal file
17
patches/parking_lot/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
description = "parking_lot which patches to the latest update"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/patches/parking_lot"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = []
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
parking_lot = "0.12"
|
||||
1
patches/parking_lot/src/lib.rs
Normal file
1
patches/parking_lot/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub use parking_lot::*;
|
||||
17
patches/parking_lot_core/Cargo.toml
Normal file
17
patches/parking_lot_core/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.6"
|
||||
description = "parking_lot_core which patches to the latest update"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/patches/parking_lot_core"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = []
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
parking_lot_core = "0.9"
|
||||
1
patches/parking_lot_core/src/lib.rs
Normal file
1
patches/parking_lot_core/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub use parking_lot_core::*;
|
||||
@@ -45,7 +45,7 @@ frost-schnorrkel = { path = "../crypto/schnorrkel", default-features = false }
|
||||
k256 = { version = "^0.13.1", default-features = false, features = ["std"], optional = true }
|
||||
|
||||
# Bitcoin
|
||||
secp256k1 = { version = "0.28", default-features = false, features = ["std", "global-context", "rand-std"], optional = true }
|
||||
secp256k1 = { version = "0.29", default-features = false, features = ["std", "global-context", "rand-std"], optional = true }
|
||||
bitcoin-serai = { path = "../coins/bitcoin", default-features = false, features = ["std"], optional = true }
|
||||
|
||||
# Ethereum
|
||||
|
||||
@@ -512,6 +512,7 @@ impl<N: Network, D: Db> KeyGen<N, D> {
|
||||
ProcessorMessage::GeneratedKeyPair {
|
||||
id,
|
||||
substrate_key: generated_substrate_key.unwrap().to_bytes(),
|
||||
// TODO: This can be made more efficient since tweaked keys may be a subset of keys
|
||||
network_key: generated_network_key.unwrap().to_bytes().as_ref().to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,7 +748,15 @@ async fn main() {
|
||||
#[cfg(feature = "bitcoin")]
|
||||
NetworkId::Bitcoin => run(db, Bitcoin::new(url).await, coordinator).await,
|
||||
#[cfg(feature = "ethereum")]
|
||||
NetworkId::Ethereum => run(db.clone(), Ethereum::new(db, url).await, coordinator).await,
|
||||
NetworkId::Ethereum => {
|
||||
let relayer_hostname = env::var("ETHEREUM_RELAYER_HOSTNAME")
|
||||
.expect("ethereum relayer hostname wasn't specified")
|
||||
.to_string();
|
||||
let relayer_port =
|
||||
env::var("ETHEREUM_RELAYER_PORT").expect("ethereum relayer port wasn't specified");
|
||||
let relayer_url = relayer_hostname + ":" + &relayer_port;
|
||||
run(db.clone(), Ethereum::new(db, url, relayer_url).await, coordinator).await
|
||||
}
|
||||
#[cfg(feature = "monero")]
|
||||
NetworkId::Monero => run(db, Monero::new(url).await, coordinator).await,
|
||||
_ => panic!("spawning a processor for an unsupported network"),
|
||||
|
||||
@@ -231,7 +231,7 @@ impl ForwardedOutputDb {
|
||||
let res = InInstructionWithBalance::decode(&mut outputs_ref).unwrap();
|
||||
assert!(outputs_ref.len() < outputs.len());
|
||||
if outputs_ref.is_empty() {
|
||||
txn.del(&Self::key(balance));
|
||||
txn.del(Self::key(balance));
|
||||
} else {
|
||||
Self::set(txn, balance, &outputs);
|
||||
}
|
||||
|
||||
@@ -63,9 +63,22 @@ fn instruction_from_output<N: Network>(
|
||||
return (presumed_origin, None);
|
||||
}
|
||||
|
||||
let Ok(shorthand) = Shorthand::decode(&mut data) else { return (presumed_origin, None) };
|
||||
let Ok(instruction) = RefundableInInstruction::try_from(shorthand) else {
|
||||
return (presumed_origin, None);
|
||||
let shorthand = match Shorthand::decode(&mut data) {
|
||||
Ok(shorthand) => shorthand,
|
||||
Err(e) => {
|
||||
info!("data in output {} wasn't valid shorthand: {e:?}", hex::encode(output.id()));
|
||||
return (presumed_origin, None);
|
||||
}
|
||||
};
|
||||
let instruction = match RefundableInInstruction::try_from(shorthand) {
|
||||
Ok(instruction) => instruction,
|
||||
Err(e) => {
|
||||
info!(
|
||||
"shorthand in output {} wasn't convertible to a RefundableInInstruction: {e:?}",
|
||||
hex::encode(output.id())
|
||||
);
|
||||
return (presumed_origin, None);
|
||||
}
|
||||
};
|
||||
|
||||
let mut balance = output.balance();
|
||||
|
||||
@@ -279,6 +279,8 @@ impl<N: Network, D: Db> ScannerHandle<N, D> {
|
||||
activation_number: usize,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
) {
|
||||
info!("Registering key {} in scanner at {activation_number}", hex::encode(key.to_bytes()));
|
||||
|
||||
let mut scanner_lock = self.scanner.write().await;
|
||||
let scanner = scanner_lock.as_mut().unwrap();
|
||||
assert!(
|
||||
@@ -286,8 +288,6 @@ impl<N: Network, D: Db> ScannerHandle<N, D> {
|
||||
"activation block of new keys was already scanned",
|
||||
);
|
||||
|
||||
info!("Registering key {} in scanner at {activation_number}", hex::encode(key.to_bytes()));
|
||||
|
||||
if scanner.keys.is_empty() {
|
||||
assert!(scanner.ram_scanned.is_none());
|
||||
scanner.ram_scanned = Some(activation_number);
|
||||
|
||||
@@ -116,7 +116,7 @@ impl<N: Network<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
||||
assert!(self.coins.contains(&utxo.balance().coin));
|
||||
}
|
||||
|
||||
let mut nonce = LastNonce::get(txn).map_or(1, |nonce| nonce + 1);
|
||||
let mut nonce = LastNonce::get(txn).unwrap_or(1);
|
||||
let mut plans = vec![];
|
||||
for chunk in payments.as_slice().chunks(N::MAX_OUTPUTS) {
|
||||
// Once we rotate, all further payments should be scheduled via the new multisig
|
||||
|
||||
@@ -432,7 +432,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> Scheduler<N> {
|
||||
}
|
||||
|
||||
// If there's a UTXO to restore, restore it
|
||||
// This is down now as if there is a to_restore output, and it was inserted into self.utxos
|
||||
// This is done now as if there is a to_restore output, and it was inserted into self.utxos
|
||||
// earlier, self.utxos.len() may become `N::MAX_INPUTS + 1`
|
||||
// The prior block requires the len to be `<= N::MAX_INPUTS`
|
||||
if let Some(to_restore) = to_restore {
|
||||
@@ -442,9 +442,10 @@ impl<N: UtxoNetwork<Scheduler = Self>> Scheduler<N> {
|
||||
txn.put(scheduler_key::<D, _>(&self.key), self.serialize());
|
||||
|
||||
log::info!(
|
||||
"created {} plans containing {} payments to sign",
|
||||
"created {} plans containing {} payments to sign, with {} payments pending scheduling",
|
||||
plans.len(),
|
||||
payments_at_start - self.payments.len(),
|
||||
self.payments.len(),
|
||||
);
|
||||
plans
|
||||
}
|
||||
@@ -589,7 +590,8 @@ impl<N: UtxoNetwork<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
||||
output: N::Output,
|
||||
refund_to: N::Address,
|
||||
) -> Plan<N> {
|
||||
Plan {
|
||||
let output_id = output.id().as_ref().to_vec();
|
||||
let res = Plan {
|
||||
key: output.key(),
|
||||
// Uses a payment as this will still be successfully sent due to fee amortization,
|
||||
// and because change is currently always a Serai key
|
||||
@@ -597,7 +599,9 @@ impl<N: UtxoNetwork<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
||||
inputs: vec![output],
|
||||
change: None,
|
||||
scheduler_addendum: (),
|
||||
}
|
||||
};
|
||||
log::info!("refund plan for {} has ID {}", hex::encode(output_id), hex::encode(res.id()));
|
||||
res
|
||||
}
|
||||
|
||||
fn shim_forward_plan(output: N::Output, to: <N::Curve as Ciphersuite>::G) -> Option<Plan<N>> {
|
||||
|
||||
@@ -20,12 +20,11 @@ use bitcoin_serai::{
|
||||
key::{Parity, XOnlyPublicKey},
|
||||
consensus::{Encodable, Decodable},
|
||||
script::Instruction,
|
||||
address::{NetworkChecked, Address as BAddress},
|
||||
Transaction, Block, Network as BNetwork, ScriptBuf,
|
||||
Transaction, Block, ScriptBuf,
|
||||
opcodes::all::{OP_SHA256, OP_EQUALVERIFY},
|
||||
},
|
||||
wallet::{
|
||||
tweak_keys, address_payload, ReceivedOutput, Scanner, TransactionError,
|
||||
tweak_keys, p2tr_script_buf, ReceivedOutput, Scanner, TransactionError,
|
||||
SignableTransaction as BSignableTransaction, TransactionMachine,
|
||||
},
|
||||
rpc::{RpcError, Rpc},
|
||||
@@ -175,7 +174,7 @@ pub struct Fee(u64);
|
||||
impl TransactionTrait<Bitcoin> for Transaction {
|
||||
type Id = [u8; 32];
|
||||
fn id(&self) -> Self::Id {
|
||||
let mut hash = *self.txid().as_raw_hash().as_byte_array();
|
||||
let mut hash = *self.compute_txid().as_raw_hash().as_byte_array();
|
||||
hash.reverse();
|
||||
hash
|
||||
}
|
||||
@@ -243,7 +242,8 @@ impl EventualityTrait for Eventuality {
|
||||
buf
|
||||
}
|
||||
fn read_completion<R: io::Read>(reader: &mut R) -> io::Result<Transaction> {
|
||||
Transaction::consensus_decode(reader).map_err(|e| io::Error::other(format!("{e}")))
|
||||
Transaction::consensus_decode(&mut io::BufReader::with_capacity(0, reader))
|
||||
.map_err(|e| io::Error::other(format!("{e}")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,7 +453,7 @@ impl Bitcoin {
|
||||
match BSignableTransaction::new(
|
||||
inputs.iter().map(|input| input.output.clone()).collect(),
|
||||
&payments,
|
||||
change.as_ref().map(AsRef::as_ref),
|
||||
change.clone().map(Into::into),
|
||||
None,
|
||||
fee.0,
|
||||
) {
|
||||
@@ -534,12 +534,14 @@ impl Bitcoin {
|
||||
input_index: usize,
|
||||
private_key: &PrivateKey,
|
||||
) -> ScriptBuf {
|
||||
use bitcoin_serai::bitcoin::{Network as BNetwork, Address as BAddress};
|
||||
|
||||
let public_key = PublicKey::from_private_key(SECP256K1, private_key);
|
||||
let main_addr = BAddress::p2pkh(&public_key, BNetwork::Regtest);
|
||||
let main_addr = BAddress::p2pkh(public_key, BNetwork::Regtest);
|
||||
|
||||
let mut der = SECP256K1
|
||||
.sign_ecdsa_low_r(
|
||||
&Message::from(
|
||||
&Message::from_digest_slice(
|
||||
SighashCache::new(tx)
|
||||
.legacy_signature_hash(
|
||||
input_index,
|
||||
@@ -547,8 +549,10 @@ impl Bitcoin {
|
||||
EcdsaSighashType::All.to_u32(),
|
||||
)
|
||||
.unwrap()
|
||||
.to_raw_hash(),
|
||||
),
|
||||
.to_raw_hash()
|
||||
.as_ref(),
|
||||
)
|
||||
.unwrap(),
|
||||
&private_key.inner,
|
||||
)
|
||||
.serialize_der()
|
||||
@@ -577,8 +581,10 @@ const MAX_INPUTS: usize = 520;
|
||||
const MAX_OUTPUTS: usize = 520;
|
||||
|
||||
fn address_from_key(key: ProjectivePoint) -> Address {
|
||||
Address::new(BAddress::<NetworkChecked>::new(BNetwork::Bitcoin, address_payload(key).unwrap()))
|
||||
.unwrap()
|
||||
Address::new(
|
||||
p2tr_script_buf(key).expect("creating address from key which isn't properly tweaked"),
|
||||
)
|
||||
.expect("couldn't create Serai-representable address for P2TR script")
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -724,9 +730,7 @@ impl Network for Bitcoin {
|
||||
}
|
||||
tx.unwrap().output.swap_remove(usize::try_from(input.previous_output.vout).unwrap())
|
||||
};
|
||||
BAddress::from_script(&spent_output.script_pubkey, BNetwork::Bitcoin)
|
||||
.ok()
|
||||
.and_then(Address::new)
|
||||
Address::new(spent_output.script_pubkey)
|
||||
};
|
||||
let data = Self::extract_serai_data(tx);
|
||||
for output in &mut outputs {
|
||||
@@ -858,7 +862,7 @@ impl Network for Bitcoin {
|
||||
Err(RpcError::ConnectionError) => Err(NetworkError::ConnectionError)?,
|
||||
// TODO: Distinguish already in pool vs double spend (other signing attempt succeeded) vs
|
||||
// invalid transaction
|
||||
Err(e) => panic!("failed to publish TX {}: {e}", tx.txid()),
|
||||
Err(e) => panic!("failed to publish TX {}: {e}", tx.compute_txid()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -894,6 +898,8 @@ impl Network for Bitcoin {
|
||||
|
||||
#[cfg(test)]
|
||||
async fn mine_block(&self) {
|
||||
use bitcoin_serai::bitcoin::{Network as BNetwork, Address as BAddress};
|
||||
|
||||
self
|
||||
.rpc
|
||||
.rpc_call::<Vec<String>>(
|
||||
@@ -906,10 +912,12 @@ impl Network for Bitcoin {
|
||||
|
||||
#[cfg(test)]
|
||||
async fn test_send(&self, address: Address) -> Block {
|
||||
use bitcoin_serai::bitcoin::{Network as BNetwork, Address as BAddress};
|
||||
|
||||
let secret_key = SecretKey::new(&mut rand_core::OsRng);
|
||||
let private_key = PrivateKey::new(secret_key, BNetwork::Regtest);
|
||||
let public_key = PublicKey::from_private_key(SECP256K1, &private_key);
|
||||
let main_addr = BAddress::p2pkh(&public_key, BNetwork::Regtest);
|
||||
let main_addr = BAddress::p2pkh(public_key, BNetwork::Regtest);
|
||||
|
||||
let new_block = self.get_latest_block_number().await.unwrap() + 1;
|
||||
self
|
||||
@@ -923,14 +931,14 @@ impl Network for Bitcoin {
|
||||
version: Version(2),
|
||||
lock_time: LockTime::ZERO,
|
||||
input: vec![TxIn {
|
||||
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
||||
previous_output: OutPoint { txid: tx.compute_txid(), vout: 0 },
|
||||
script_sig: Script::new().into(),
|
||||
sequence: Sequence(u32::MAX),
|
||||
witness: Witness::default(),
|
||||
}],
|
||||
output: vec![TxOut {
|
||||
value: tx.output[0].value - BAmount::from_sat(10000),
|
||||
script_pubkey: address.as_ref().script_pubkey(),
|
||||
script_pubkey: address.clone().into(),
|
||||
}],
|
||||
};
|
||||
tx.input[0].script_sig = Self::sign_btc_input_for_p2pkh(&tx, 0, &private_key);
|
||||
|
||||
@@ -13,7 +13,7 @@ use frost::ThresholdKeys;
|
||||
use ethereum_serai::{
|
||||
alloy::{
|
||||
primitives::U256,
|
||||
rpc_types::{BlockNumberOrTag, Transaction},
|
||||
rpc_types::{BlockTransactionsKind, BlockNumberOrTag, Transaction},
|
||||
simple_request_transport::SimpleRequest,
|
||||
rpc_client::ClientBuilder,
|
||||
provider::{Provider, RootProvider},
|
||||
@@ -31,6 +31,11 @@ use tokio::{
|
||||
time::sleep,
|
||||
sync::{RwLock, RwLockReadGuard},
|
||||
};
|
||||
#[cfg(not(test))]
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpStream,
|
||||
};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{Coin, Amount, Balance, NetworkId},
|
||||
@@ -290,6 +295,8 @@ pub struct Ethereum<D: Db> {
|
||||
// address. Accordingly, all methods present are consistent to a Serai chain with a finalized
|
||||
// first key (regardless of local state), and this is safe.
|
||||
db: D,
|
||||
#[cfg_attr(test, allow(unused))]
|
||||
relayer_url: String,
|
||||
provider: Arc<RootProvider<SimpleRequest>>,
|
||||
deployer: Deployer,
|
||||
router: Arc<RwLock<Option<Router>>>,
|
||||
@@ -309,9 +316,9 @@ impl<D: Db> fmt::Debug for Ethereum<D> {
|
||||
}
|
||||
}
|
||||
impl<D: Db> Ethereum<D> {
|
||||
pub async fn new(db: D, url: String) -> Self {
|
||||
pub async fn new(db: D, daemon_url: String, relayer_url: String) -> Self {
|
||||
let provider = Arc::new(RootProvider::new(
|
||||
ClientBuilder::default().transport(SimpleRequest::new(url), true),
|
||||
ClientBuilder::default().transport(SimpleRequest::new(daemon_url), true),
|
||||
));
|
||||
|
||||
let mut deployer = Deployer::new(provider.clone()).await;
|
||||
@@ -322,7 +329,9 @@ impl<D: Db> Ethereum<D> {
|
||||
}
|
||||
let deployer = deployer.unwrap().unwrap();
|
||||
|
||||
Ethereum { db, provider, deployer, router: Arc::new(RwLock::new(None)) }
|
||||
dbg!(&relayer_url);
|
||||
dbg!(relayer_url.len());
|
||||
Ethereum { db, relayer_url, provider, deployer, router: Arc::new(RwLock::new(None)) }
|
||||
}
|
||||
|
||||
// Obtain a reference to the Router, sleeping until it's deployed if it hasn't already been.
|
||||
@@ -423,10 +432,10 @@ impl<D: Db> Network for Ethereum<D> {
|
||||
async fn get_latest_block_number(&self) -> Result<usize, NetworkError> {
|
||||
let actual_number = self
|
||||
.provider
|
||||
.get_block(BlockNumberOrTag::Finalized.into(), false)
|
||||
.get_block(BlockNumberOrTag::Finalized.into(), BlockTransactionsKind::Hashes)
|
||||
.await
|
||||
.map_err(|_| NetworkError::ConnectionError)?
|
||||
.expect("no blocks were finalized")
|
||||
.ok_or(NetworkError::ConnectionError)?
|
||||
.header
|
||||
.number
|
||||
.unwrap();
|
||||
@@ -451,7 +460,7 @@ impl<D: Db> Network for Ethereum<D> {
|
||||
} else {
|
||||
self
|
||||
.provider
|
||||
.get_block(u64::try_from(start - 1).unwrap().into(), false)
|
||||
.get_block(u64::try_from(start - 1).unwrap().into(), BlockTransactionsKind::Hashes)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
@@ -464,7 +473,7 @@ impl<D: Db> Network for Ethereum<D> {
|
||||
|
||||
let end_header = self
|
||||
.provider
|
||||
.get_block(u64::try_from(start + 31).unwrap().into(), false)
|
||||
.get_block(u64::try_from(start + 31).unwrap().into(), BlockTransactionsKind::Hashes)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
@@ -714,8 +723,32 @@ impl<D: Db> Network for Ethereum<D> {
|
||||
// Publish this to the dedicated TX server for a solver to actually publish
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
let _ = completion;
|
||||
todo!("TODO");
|
||||
let mut msg = vec![];
|
||||
match completion.command() {
|
||||
RouterCommand::UpdateSeraiKey { nonce, .. } | RouterCommand::Execute { nonce, .. } => {
|
||||
msg.extend(&u32::try_from(nonce).unwrap().to_le_bytes());
|
||||
}
|
||||
}
|
||||
completion.write(&mut msg).unwrap();
|
||||
|
||||
let Ok(mut socket) = TcpStream::connect(&self.relayer_url).await else {
|
||||
log::warn!("couldn't connect to the relayer server");
|
||||
Err(NetworkError::ConnectionError)?
|
||||
};
|
||||
let Ok(()) = socket.write_all(&u32::try_from(msg.len()).unwrap().to_le_bytes()).await else {
|
||||
log::warn!("couldn't send the message's len to the relayer server");
|
||||
Err(NetworkError::ConnectionError)?
|
||||
};
|
||||
let Ok(()) = socket.write_all(&msg).await else {
|
||||
log::warn!("couldn't write the message to the relayer server");
|
||||
Err(NetworkError::ConnectionError)?
|
||||
};
|
||||
if socket.read_u8().await.ok() != Some(1) {
|
||||
log::warn!("didn't get the ack from the relayer server");
|
||||
Err(NetworkError::ConnectionError)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Publish this using a dummy account we fund with magic RPC commands
|
||||
@@ -774,7 +807,7 @@ impl<D: Db> Network for Ethereum<D> {
|
||||
async fn get_block_number(&self, id: &<Self::Block as Block<Self>>::Id) -> usize {
|
||||
self
|
||||
.provider
|
||||
.get_block(B256::from(*id).into(), false)
|
||||
.get_block(B256::from(*id).into(), BlockTransactionsKind::Hashes)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
|
||||
@@ -70,7 +70,7 @@ mod bitcoin {
|
||||
// btc key pair to send from
|
||||
let private_key = PrivateKey::new(SecretKey::new(&mut rand_core::OsRng), BNetwork::Regtest);
|
||||
let public_key = PublicKey::from_private_key(SECP256K1, &private_key);
|
||||
let main_addr = BAddress::p2pkh(&public_key, BNetwork::Regtest);
|
||||
let main_addr = BAddress::p2pkh(public_key, BNetwork::Regtest);
|
||||
|
||||
// get unlocked coins
|
||||
let new_block = btc.get_latest_block_number().await.unwrap() + 1;
|
||||
@@ -107,7 +107,7 @@ mod bitcoin {
|
||||
version: Version(2),
|
||||
lock_time: LockTime::ZERO,
|
||||
input: vec![TxIn {
|
||||
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
||||
previous_output: OutPoint { txid: tx.compute_txid(), vout: 0 },
|
||||
script_sig: Script::new().into(),
|
||||
sequence: Sequence(u32::MAX),
|
||||
witness: Witness::default(),
|
||||
@@ -128,14 +128,14 @@ mod bitcoin {
|
||||
version: Version(2),
|
||||
lock_time: LockTime::ZERO,
|
||||
input: vec![TxIn {
|
||||
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
||||
previous_output: OutPoint { txid: tx.compute_txid(), vout: 0 },
|
||||
script_sig: Script::new().into(),
|
||||
sequence: Sequence(u32::MAX),
|
||||
witness: Witness::new(),
|
||||
}],
|
||||
output: vec![TxOut {
|
||||
value: tx.output[0].value - BAmount::from_sat(10000),
|
||||
script_pubkey: serai_btc_address.as_ref().script_pubkey(),
|
||||
script_pubkey: serai_btc_address.into(),
|
||||
}],
|
||||
};
|
||||
|
||||
@@ -143,12 +143,14 @@ mod bitcoin {
|
||||
// This is the standard script with an extra argument of the InInstruction
|
||||
let mut sig = SECP256K1
|
||||
.sign_ecdsa_low_r(
|
||||
&Message::from(
|
||||
&Message::from_digest_slice(
|
||||
SighashCache::new(&tx)
|
||||
.p2wsh_signature_hash(0, &script, initial_output_value, EcdsaSighashType::All)
|
||||
.unwrap()
|
||||
.to_raw_hash(),
|
||||
),
|
||||
.to_raw_hash()
|
||||
.as_ref(),
|
||||
)
|
||||
.unwrap(),
|
||||
&private_key.inner,
|
||||
)
|
||||
.serialize_der()
|
||||
@@ -421,7 +423,7 @@ mod ethereum {
|
||||
});
|
||||
}
|
||||
|
||||
Ethereum::new(db, url.clone()).await
|
||||
Ethereum::new(db, url.clone(), String::new()).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,12 @@ pub async fn test_scanner<N: Network>(
|
||||
pub async fn test_no_deadlock_in_multisig_completed<N: Network>(
|
||||
new_network: impl Fn(MemDb) -> Pin<Box<dyn Send + Future<Output = N>>>,
|
||||
) {
|
||||
// This test scans two blocks then acknowledges one, yet a network with one confirm won't scan
|
||||
// two blocks before the first is acknowledged (due to the look-ahead limit)
|
||||
if N::CONFIRMATIONS <= 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut db = MemDb::new();
|
||||
let network = new_network(db.clone()).await;
|
||||
|
||||
@@ -139,6 +145,10 @@ pub async fn test_no_deadlock_in_multisig_completed<N: Network>(
|
||||
let mut txn = db.txn();
|
||||
NetworkKeyDb::set(&mut txn, Session(0), &key.to_bytes().as_ref().to_vec());
|
||||
txn.commit();
|
||||
|
||||
// Sleep for 5 seconds as setting the Network key value will trigger an async task for
|
||||
// Ethereum
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
key
|
||||
};
|
||||
@@ -158,6 +168,7 @@ pub async fn test_no_deadlock_in_multisig_completed<N: Network>(
|
||||
network.mine_block().await;
|
||||
}
|
||||
|
||||
// Block for the second set of keys registered
|
||||
let block_id =
|
||||
match timeout(Duration::from_secs(30), scanner.events.recv()).await.unwrap().unwrap() {
|
||||
ScannerEvent::Block { is_retirement_block, block, outputs: _ } => {
|
||||
@@ -170,6 +181,7 @@ pub async fn test_no_deadlock_in_multisig_completed<N: Network>(
|
||||
}
|
||||
};
|
||||
|
||||
// Block for the third set of keys registered
|
||||
match timeout(Duration::from_secs(30), scanner.events.recv()).await.unwrap().unwrap() {
|
||||
ScannerEvent::Block { .. } => {}
|
||||
ScannerEvent::Completed(_, _, _, _, _) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[toolchain]
|
||||
channel = "1.77"
|
||||
channel = "1.79"
|
||||
targets = ["wasm32-unknown-unknown"]
|
||||
profile = "minimal"
|
||||
components = ["rust-src", "rustfmt", "clippy"]
|
||||
|
||||
@@ -16,28 +16,50 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
scale = { package = "parity-scale-codec", version = "3", features = ["derive"] }
|
||||
scale-info = { version = "2", features = ["derive"] }
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
|
||||
borsh = { version = "1", features = ["derive", "de_strict_order"], optional = true }
|
||||
serde = { version = "1", features = ["derive", "alloc"], optional = true }
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||
|
||||
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/substrate" }
|
||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate" }
|
||||
sp-consensus-grandpa = { git = "https://github.com/serai-dex/substrate" }
|
||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-consensus-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../primitives", version = "0.1" }
|
||||
serai-coins-primitives = { path = "../coins/primitives", version = "0.1" }
|
||||
serai-validator-sets-primitives = { path = "../validator-sets/primitives", version = "0.1" }
|
||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }
|
||||
serai-coins-primitives = { path = "../coins/primitives", version = "0.1", default-features = false }
|
||||
serai-validator-sets-primitives = { path = "../validator-sets/primitives", version = "0.1", default-features = false }
|
||||
serai-genesis-liquidity-primitives = { path = "../genesis-liquidity/primitives", version = "0.1" }
|
||||
serai-in-instructions-primitives = { path = "../in-instructions/primitives", version = "0.1" }
|
||||
serai-signals-primitives = { path = "../signals/primitives", version = "0.1" }
|
||||
|
||||
frame-support = { git = "https://github.com/serai-dex/substrate" }
|
||||
serai-in-instructions-primitives = { path = "../in-instructions/primitives", version = "0.1", default-features = false }
|
||||
serai-signals-primitives = { path = "../signals/primitives", version = "0.1", default-features = false }
|
||||
|
||||
[features]
|
||||
std = [
|
||||
"scale/std",
|
||||
"scale-info/std",
|
||||
|
||||
"borsh?/std",
|
||||
"serde?/std",
|
||||
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
|
||||
"sp-consensus-babe/std",
|
||||
"sp-consensus-grandpa/std",
|
||||
|
||||
"frame-support/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
"serai-coins-primitives/std",
|
||||
"serai-validator-sets-primitives/std",
|
||||
"serai-genesis-liquidity-primitives/std",
|
||||
"serai-in-instructions-primitives/std",
|
||||
"serai-signals-primitives/std",
|
||||
]
|
||||
borsh = [
|
||||
"dep:borsh",
|
||||
"serai-primitives/borsh",
|
||||
@@ -56,3 +78,4 @@ serde = [
|
||||
"serai-in-instructions-primitives/serde",
|
||||
"serai-signals-primitives/serde",
|
||||
]
|
||||
default = ["std"]
|
||||
|
||||
@@ -4,7 +4,7 @@ use serai_primitives::{Header, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub struct ReportEquivocation {
|
||||
pub equivocation_proof: Box<EquivocationProof<Header>>,
|
||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<Header>>,
|
||||
pub key_owner_proof: SeraiAddress,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ use primitives::OutInstructionWithBalance;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
transfer { to: SeraiAddress, balance: Balance },
|
||||
burn { balance: Balance },
|
||||
@@ -14,7 +15,17 @@ pub enum Call {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum LiquidityTokensCall {
|
||||
transfer { to: SeraiAddress, balance: Balance },
|
||||
burn { balance: Balance },
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
Mint { to: SeraiAddress, balance: Balance },
|
||||
Burn { from: SeraiAddress, balance: Balance },
|
||||
|
||||
@@ -6,7 +6,8 @@ type PoolId = Coin;
|
||||
type MaxSwapPathLength = sp_core::ConstU32<3>;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
add_liquidity {
|
||||
coin: Coin,
|
||||
@@ -38,7 +39,8 @@ pub enum Call {
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
PoolCreated {
|
||||
pool_id: PoolId,
|
||||
|
||||
@@ -4,7 +4,7 @@ use serai_primitives::{BlockNumber, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub struct ReportEquivocation {
|
||||
pub equivocation_proof: Box<EquivocationProof<[u8; 32], BlockNumber>>,
|
||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<[u8; 32], BlockNumber>>,
|
||||
pub key_owner_proof: SeraiAddress,
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ pub enum Call {
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
NewAuthorities { authority_set: Vec<(SeraiAddress, u64)> },
|
||||
NewAuthorities { authority_set: alloc::vec::Vec<(SeraiAddress, u64)> },
|
||||
// TODO: Remove these
|
||||
Paused,
|
||||
Resumed,
|
||||
|
||||
@@ -5,14 +5,16 @@ use primitives::SignedBatch;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
execute_batch { batch: SignedBatch },
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
Batch { network: NetworkId, id: u32, block: BlockHash, instructions_hash: [u8; 32] },
|
||||
InstructionFailure { network: NetworkId, id: u32, index: u32 },
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub use serai_primitives as primitives;
|
||||
|
||||
pub mod system;
|
||||
|
||||
pub mod timestamp;
|
||||
@@ -16,15 +23,13 @@ pub mod genesis_liquidity;
|
||||
pub mod babe;
|
||||
pub mod grandpa;
|
||||
|
||||
pub use serai_primitives as primitives;
|
||||
pub mod tx;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub enum Call {
|
||||
System,
|
||||
Timestamp(timestamp::Call),
|
||||
TransactionPayment,
|
||||
Coins(coins::Call),
|
||||
LiquidityTokens(coins::Call),
|
||||
LiquidityTokens(coins::LiquidityTokensCall),
|
||||
Dex(dex::Call),
|
||||
GenesisLiquidity(genesis_liquidity::Call),
|
||||
ValidatorSets(validator_sets::Call),
|
||||
@@ -57,16 +62,20 @@ pub enum Event {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub struct Extra {
|
||||
pub era: sp_runtime::generic::Era,
|
||||
pub nonce: scale::Compact<u32>,
|
||||
pub tip: scale::Compact<u64>,
|
||||
#[codec(compact)]
|
||||
pub nonce: u32,
|
||||
#[codec(compact)]
|
||||
pub tip: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub struct SignedPayloadExtra {
|
||||
pub spec_version: u32,
|
||||
pub tx_version: u32,
|
||||
@@ -74,4 +83,4 @@ pub struct SignedPayloadExtra {
|
||||
pub mortality_checkpoint: [u8; 32],
|
||||
}
|
||||
|
||||
pub type Transaction = primitives::Transaction<Call, Extra>;
|
||||
pub type Transaction = tx::Transaction<Call, Extra>;
|
||||
|
||||
@@ -7,7 +7,8 @@ use primitives::SignalId;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
register_retirement_signal { in_favor_of: [u8; 32] },
|
||||
revoke_retirement_signal { retirement_signal_id: [u8; 32] },
|
||||
@@ -18,7 +19,8 @@ pub enum Call {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
RetirementSignalRegistered {
|
||||
signal_id: [u8; 32],
|
||||
|
||||
@@ -3,7 +3,6 @@ use frame_support::dispatch::{DispatchInfo, DispatchError};
|
||||
use serai_primitives::SeraiAddress;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Event {
|
||||
ExtrinsicSuccess { dispatch_info: DispatchInfo },
|
||||
ExtrinsicFailed { dispatch_error: DispatchError, dispatch_info: DispatchInfo },
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
set { now: scale::Compact<u64> },
|
||||
set {
|
||||
#[codec(compact)]
|
||||
now: u64,
|
||||
},
|
||||
}
|
||||
|
||||
183
substrate/abi/src/tx.rs
Normal file
183
substrate/abi/src/tx.rs
Normal file
@@ -0,0 +1,183 @@
|
||||
use scale::Encode;
|
||||
|
||||
use sp_core::sr25519::{Public, Signature};
|
||||
use sp_runtime::traits::Verify;
|
||||
|
||||
use serai_primitives::SeraiAddress;
|
||||
|
||||
use frame_support::dispatch::GetDispatchInfo;
|
||||
|
||||
pub trait TransactionMember:
|
||||
Clone + PartialEq + Eq + core::fmt::Debug + scale::Encode + scale::Decode + scale_info::TypeInfo
|
||||
{
|
||||
}
|
||||
impl<
|
||||
T: Clone
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ core::fmt::Debug
|
||||
+ scale::Encode
|
||||
+ scale::Decode
|
||||
+ scale_info::TypeInfo,
|
||||
> TransactionMember for T
|
||||
{
|
||||
}
|
||||
|
||||
type TransactionEncodeAs<'a, Extra> =
|
||||
(&'a crate::Call, &'a Option<(SeraiAddress, Signature, Extra)>);
|
||||
type TransactionDecodeAs<Extra> = (crate::Call, Option<(SeraiAddress, Signature, Extra)>);
|
||||
|
||||
// We use our own Transaction struct, over UncheckedExtrinsic, for more control, a bit more
|
||||
// simplicity, and in order to be immune to https://github.com/paritytech/polkadot-sdk/issues/2947
|
||||
#[allow(private_bounds)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Transaction<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> {
|
||||
call: crate::Call,
|
||||
mapped_call: Call,
|
||||
signature: Option<(SeraiAddress, Signature, Extra)>,
|
||||
}
|
||||
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
Transaction<Call, Extra>
|
||||
{
|
||||
pub fn new(call: crate::Call, signature: Option<(SeraiAddress, Signature, Extra)>) -> Self {
|
||||
Self { call: call.clone(), mapped_call: call.into(), signature }
|
||||
}
|
||||
|
||||
pub fn call(&self) -> &crate::Call {
|
||||
&self.call
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale::Encode for Transaction<Call, Extra>
|
||||
{
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
let tx: TransactionEncodeAs<Extra> = (&self.call, &self.signature);
|
||||
tx.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale::Decode for Transaction<Call, Extra>
|
||||
{
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let (call, signature) = TransactionDecodeAs::decode(input)?;
|
||||
let mapped_call = Call::from(call.clone());
|
||||
Ok(Self { call, mapped_call, signature })
|
||||
}
|
||||
}
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale_info::TypeInfo for Transaction<Call, Extra>
|
||||
{
|
||||
type Identity = TransactionDecodeAs<Extra>;
|
||||
|
||||
// Define the type info as the info of the type equivalent to what we encode as
|
||||
fn type_info() -> scale_info::Type {
|
||||
TransactionDecodeAs::<Extra>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod _serde {
|
||||
use scale::Encode;
|
||||
use serde::{ser::*, de::*};
|
||||
use super::*;
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
Serialize for Transaction<Call, Extra>
|
||||
{
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded = self.encode();
|
||||
serializer.serialize_bytes(&encoded)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "std")]
|
||||
impl<
|
||||
'a,
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> Deserialize<'a> for Transaction<Call, Extra>
|
||||
{
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
let bytes = sp_core::bytes::deserialize(de)?;
|
||||
<Self as scale::Decode>::decode(&mut &bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid transaction: {e}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> sp_runtime::traits::Extrinsic for Transaction<Call, Extra>
|
||||
{
|
||||
type Call = Call;
|
||||
type SignaturePayload = (SeraiAddress, Signature, Extra);
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.signature.is_some())
|
||||
}
|
||||
fn new(call: Call, signature: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(Self { call: call.clone().try_into().ok()?, mapped_call: call, signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> frame_support::traits::ExtrinsicCall for Transaction<Call, Extra>
|
||||
{
|
||||
fn call(&self) -> &Call {
|
||||
&self.mapped_call
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
||||
> sp_runtime::traits::ExtrinsicMetadata for Transaction<Call, Extra>
|
||||
{
|
||||
type SignedExtensions = Extra;
|
||||
|
||||
const VERSION: u8 = 0;
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + GetDispatchInfo,
|
||||
Extra: 'static + TransactionMember,
|
||||
> GetDispatchInfo for Transaction<Call, Extra>
|
||||
{
|
||||
fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
|
||||
self.mapped_call.get_dispatch_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
||||
> sp_runtime::traits::BlindCheckable for Transaction<Call, Extra>
|
||||
{
|
||||
type Checked = sp_runtime::generic::CheckedExtrinsic<Public, Call, Extra>;
|
||||
|
||||
fn check(
|
||||
self,
|
||||
) -> Result<Self::Checked, sp_runtime::transaction_validity::TransactionValidityError> {
|
||||
Ok(match self.signature {
|
||||
Some((signer, signature, extra)) => {
|
||||
if !signature.verify(
|
||||
(&self.call, &extra, extra.additional_signed()?).encode().as_slice(),
|
||||
&signer.into(),
|
||||
) {
|
||||
Err(sp_runtime::transaction_validity::InvalidTransaction::BadProof)?
|
||||
}
|
||||
|
||||
sp_runtime::generic::CheckedExtrinsic {
|
||||
signed: Some((signer.into(), extra)),
|
||||
function: self.mapped_call,
|
||||
}
|
||||
}
|
||||
None => sp_runtime::generic::CheckedExtrinsic { signed: None, function: self.mapped_call },
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use sp_core::{ConstU32, bounded_vec::BoundedVec};
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
|
||||
pub use serai_validator_sets_primitives as primitives;
|
||||
|
||||
@@ -6,11 +6,12 @@ use serai_primitives::*;
|
||||
use serai_validator_sets_primitives::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
set_keys {
|
||||
network: NetworkId,
|
||||
removed_participants: Vec<SeraiAddress>,
|
||||
removed_participants: BoundedVec<SeraiAddress, ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
||||
key_pair: KeyPair,
|
||||
signature: Signature,
|
||||
},
|
||||
@@ -35,7 +36,8 @@ pub enum Call {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
NewSet {
|
||||
set: ValidatorSet,
|
||||
|
||||
@@ -36,7 +36,7 @@ async-lock = "3"
|
||||
|
||||
simple-request = { path = "../../common/request", version = "0.1", optional = true }
|
||||
|
||||
bitcoin = { version = "0.31", optional = true }
|
||||
bitcoin = { version = "0.32", optional = true }
|
||||
|
||||
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", optional = true }
|
||||
monero-serai = { path = "../../coins/monero", version = "0.1.4-alpha", optional = true }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user