mirror of
https://github.com/serai-dex/serai.git
synced 2025-03-25 00:28:52 +00:00
Test Batches with Instructions
This commit is contained in:
parent
e00aa3031c
commit
64c309f8db
6 changed files with 74 additions and 14 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -8849,6 +8849,7 @@ dependencies = [
|
||||||
"dockertest",
|
"dockertest",
|
||||||
"hex",
|
"hex",
|
||||||
"monero-serai",
|
"monero-serai",
|
||||||
|
"parity-scale-codec",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"serai-docker-tests",
|
"serai-docker-tests",
|
||||||
"serai-in-instructions-primitives",
|
"serai-in-instructions-primitives",
|
||||||
|
|
|
@ -699,6 +699,8 @@ async fn run<C: Coin, D: Db, Co: Coordinator>(mut raw_db: D, coin: C, mut coordi
|
||||||
}).collect()
|
}).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info!("created batch {} ({} instructions)", batch.id, batch.instructions.len());
|
||||||
|
|
||||||
// Start signing this batch
|
// Start signing this batch
|
||||||
tributary_mutable
|
tributary_mutable
|
||||||
.substrate_signers
|
.substrate_signers
|
||||||
|
|
|
@ -162,7 +162,7 @@ impl<D: Db> SubstrateSigner<D> {
|
||||||
self.attempt.insert(id, attempt);
|
self.attempt.insert(id, attempt);
|
||||||
|
|
||||||
let id = SignId { key: self.keys.group_key().to_bytes().to_vec(), id, attempt };
|
let id = SignId { key: self.keys.group_key().to_bytes().to_vec(), id, attempt };
|
||||||
info!("signing batch {} with attempt #{}", hex::encode(id.id), id.attempt);
|
info!("signing batch {}, attempt #{}", hex::encode(id.id), id.attempt);
|
||||||
|
|
||||||
// If we reboot mid-sign, the current design has us abort all signs and wait for latter
|
// If we reboot mid-sign, the current design has us abort all signs and wait for latter
|
||||||
// attempts/new signing protocols
|
// attempts/new signing protocols
|
||||||
|
@ -178,7 +178,7 @@ impl<D: Db> SubstrateSigner<D> {
|
||||||
// Only run if this hasn't already been attempted
|
// Only run if this hasn't already been attempted
|
||||||
if SubstrateSignerDb::<D>::has_attempt(txn, &id) {
|
if SubstrateSignerDb::<D>::has_attempt(txn, &id) {
|
||||||
warn!(
|
warn!(
|
||||||
"already attempted {} #{}. this is an error if we didn't reboot",
|
"already attempted batch {}, attempt #{}. this is an error if we didn't reboot",
|
||||||
hex::encode(id.id),
|
hex::encode(id.id),
|
||||||
id.attempt
|
id.attempt
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,6 +28,7 @@ monero-serai = { path = "../../coins/monero" }
|
||||||
|
|
||||||
messages = { package = "serai-processor-messages", path = "../../processor/messages" }
|
messages = { package = "serai-processor-messages", path = "../../processor/messages" }
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3" }
|
||||||
serai-primitives = { path = "../../substrate/primitives" }
|
serai-primitives = { path = "../../substrate/primitives" }
|
||||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives" }
|
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives" }
|
||||||
serai-in-instructions-primitives = { path = "../../substrate/in-instructions/primitives" }
|
serai-in-instructions-primitives = { path = "../../substrate/in-instructions/primitives" }
|
||||||
|
|
|
@ -3,8 +3,11 @@ use std::collections::HashSet;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::{RngCore, OsRng};
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
use serai_primitives::NetworkId;
|
use scale::Encode;
|
||||||
|
|
||||||
|
use serai_primitives::{NetworkId, Amount};
|
||||||
use serai_validator_sets_primitives::ExternalKey;
|
use serai_validator_sets_primitives::ExternalKey;
|
||||||
|
use serai_in_instructions_primitives::{InInstruction, RefundableInInstruction, Shorthand};
|
||||||
|
|
||||||
use dockertest::{PullPolicy, Image, StartPolicy, Composition, DockerOperations};
|
use dockertest::{PullPolicy, Image, StartPolicy, Composition, DockerOperations};
|
||||||
|
|
||||||
|
@ -213,7 +216,12 @@ impl Wallet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_to_address(&mut self, ops: &DockerOperations, to: &ExternalKey) -> Vec<u8> {
|
pub async fn send_to_address(
|
||||||
|
&mut self,
|
||||||
|
ops: &DockerOperations,
|
||||||
|
to: &ExternalKey,
|
||||||
|
instruction: Option<InInstruction>,
|
||||||
|
) -> (Vec<u8>, Amount) {
|
||||||
match self {
|
match self {
|
||||||
Wallet::Bitcoin { private_key, public_key, ref mut input_tx } => {
|
Wallet::Bitcoin { private_key, public_key, ref mut input_tx } => {
|
||||||
use bitcoin_serai::bitcoin::{
|
use bitcoin_serai::bitcoin::{
|
||||||
|
@ -221,7 +229,7 @@ impl Wallet {
|
||||||
key::{XOnlyPublicKey, TweakedPublicKey},
|
key::{XOnlyPublicKey, TweakedPublicKey},
|
||||||
consensus::Encodable,
|
consensus::Encodable,
|
||||||
sighash::{EcdsaSighashType, SighashCache},
|
sighash::{EcdsaSighashType, SighashCache},
|
||||||
script::{PushBytesBuf, Script, Builder},
|
script::{PushBytesBuf, Script, ScriptBuf, Builder},
|
||||||
address::Payload,
|
address::Payload,
|
||||||
OutPoint, Sequence, Witness, TxIn, TxOut,
|
OutPoint, Sequence, Witness, TxIn, TxOut,
|
||||||
absolute::LockTime,
|
absolute::LockTime,
|
||||||
|
@ -253,6 +261,18 @@ impl Wallet {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(instruction) = instruction {
|
||||||
|
tx.output.push(TxOut {
|
||||||
|
value: 0,
|
||||||
|
script_pubkey: ScriptBuf::new_op_return(
|
||||||
|
&PushBytesBuf::try_from(
|
||||||
|
Shorthand::Raw(RefundableInInstruction { origin: None, instruction }).encode(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let mut der = SECP256K1
|
let mut der = SECP256K1
|
||||||
.sign_ecdsa_low_r(
|
.sign_ecdsa_low_r(
|
||||||
&Message::from(
|
&Message::from(
|
||||||
|
@ -278,7 +298,7 @@ impl Wallet {
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
tx.consensus_encode(&mut buf).unwrap();
|
tx.consensus_encode(&mut buf).unwrap();
|
||||||
*input_tx = tx;
|
*input_tx = tx;
|
||||||
buf
|
(buf, Amount(AMOUNT))
|
||||||
}
|
}
|
||||||
|
|
||||||
Wallet::Monero { handle, ref spend_key, ref view_pair, ref mut inputs } => {
|
Wallet::Monero { handle, ref spend_key, ref view_pair, ref mut inputs } => {
|
||||||
|
@ -329,13 +349,18 @@ impl Wallet {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create and sign the TX
|
// Create and sign the TX
|
||||||
|
const AMOUNT: u64 = 1_000_000_000_000;
|
||||||
|
let mut data = vec![];
|
||||||
|
if let Some(instruction) = instruction {
|
||||||
|
data.push(Shorthand::Raw(RefundableInInstruction { origin: None, instruction }).encode());
|
||||||
|
}
|
||||||
let tx = SignableTransaction::new(
|
let tx = SignableTransaction::new(
|
||||||
Protocol::v16,
|
Protocol::v16,
|
||||||
None,
|
None,
|
||||||
these_inputs.drain(..).zip(decoys.drain(..)).collect(),
|
these_inputs.drain(..).zip(decoys.drain(..)).collect(),
|
||||||
vec![(to_addr, 1_000_000_000_000)],
|
vec![(to_addr, AMOUNT)],
|
||||||
Some(Change::new(view_pair, false)),
|
Some(Change::new(view_pair, false)),
|
||||||
vec![],
|
data,
|
||||||
rpc.get_fee(Protocol::v16, FeePriority::Low).await.unwrap(),
|
rpc.get_fee(Protocol::v16, FeePriority::Low).await.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -351,7 +376,7 @@ impl Wallet {
|
||||||
.remove(0),
|
.remove(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
tx.serialize()
|
(tx.serialize(), Amount(AMOUNT))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,12 @@ use dkg::{Participant, tests::clone_without};
|
||||||
|
|
||||||
use messages::sign::SignId;
|
use messages::sign::SignId;
|
||||||
|
|
||||||
use serai_primitives::{NetworkId, BlockHash, crypto::RuntimePublic, PublicKey};
|
use serai_primitives::{
|
||||||
use serai_in_instructions_primitives::{SignedBatch, batch_message};
|
BlockHash, crypto::RuntimePublic, PublicKey, SeraiAddress, NetworkId, Coin, Balance,
|
||||||
|
};
|
||||||
|
use serai_in_instructions_primitives::{
|
||||||
|
InInstruction, InInstructionWithBalance, SignedBatch, batch_message,
|
||||||
|
};
|
||||||
|
|
||||||
use dockertest::DockerTest;
|
use dockertest::DockerTest;
|
||||||
|
|
||||||
|
@ -168,9 +172,16 @@ fn batch_test() {
|
||||||
}
|
}
|
||||||
coordinators[0].sync(&ops, &coordinators[1 ..]).await;
|
coordinators[0].sync(&ops, &coordinators[1 ..]).await;
|
||||||
|
|
||||||
|
// Run twice, once with an instruction and once without
|
||||||
for i in 0 .. 2 {
|
for i in 0 .. 2 {
|
||||||
|
let mut serai_address = [0; 32];
|
||||||
|
OsRng.fill_bytes(&mut serai_address);
|
||||||
|
let instruction =
|
||||||
|
if i == 1 { Some(InInstruction::Transfer(SeraiAddress(serai_address))) } else { None };
|
||||||
|
|
||||||
// Send into the processor's wallet
|
// Send into the processor's wallet
|
||||||
let tx = wallet.send_to_address(&ops, &key_pair.1).await;
|
let (tx, amount_sent) =
|
||||||
|
wallet.send_to_address(&ops, &key_pair.1, instruction.clone()).await;
|
||||||
for coordinator in &mut coordinators {
|
for coordinator in &mut coordinators {
|
||||||
coordinator.publish_transacton(&ops, &tx).await;
|
coordinator.publish_transacton(&ops, &tx).await;
|
||||||
}
|
}
|
||||||
|
@ -215,9 +226,29 @@ fn batch_test() {
|
||||||
assert_eq!(batch.batch.network, network);
|
assert_eq!(batch.batch.network, network);
|
||||||
assert_eq!(batch.batch.id, i);
|
assert_eq!(batch.batch.id, i);
|
||||||
assert_eq!(batch.batch.block, BlockHash(block_with_tx.unwrap()));
|
assert_eq!(batch.batch.block, BlockHash(block_with_tx.unwrap()));
|
||||||
|
if let Some(instruction) = instruction {
|
||||||
|
assert_eq!(
|
||||||
|
batch.batch.instructions,
|
||||||
|
vec![InInstructionWithBalance {
|
||||||
|
instruction,
|
||||||
|
balance: Balance {
|
||||||
|
coin: match network {
|
||||||
|
NetworkId::Bitcoin => Coin::Bitcoin,
|
||||||
|
NetworkId::Ethereum => todo!(),
|
||||||
|
NetworkId::Monero => Coin::Monero,
|
||||||
|
NetworkId::Serai => panic!("running processor tests on Serai"),
|
||||||
|
},
|
||||||
|
amount: amount_sent,
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
// This shouldn't have an instruction as we didn't add any data into the TX we sent
|
// This shouldn't have an instruction as we didn't add any data into the TX we sent
|
||||||
|
// Empty batches remain valuable as they let us achieve consensus on the block and spend
|
||||||
|
// contained outputs
|
||||||
assert!(batch.batch.instructions.is_empty());
|
assert!(batch.batch.instructions.is_empty());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue