Bug fixes and log statements

Also shims next nonce code with a fine-for-now piece of code which is unviable
in production, yet should survive testnet.
This commit is contained in:
Luke Parker
2023-08-13 02:21:56 -04:00
parent 049fefb5fd
commit 7e71450dc4
11 changed files with 217 additions and 72 deletions

View File

@@ -204,7 +204,7 @@ impl ReadWrite for SignData {
// It provides 4 commitments per input (128 bytes), a 64-byte proof for them, along with a
// key image and proof (96 bytes)
// Even with all of that, we could support 227 inputs in a single TX
// Monero is limited to 120 inputs per TX
// Monero is limited to ~120 inputs per TX
Err(io::Error::new(io::ErrorKind::Other, "signing data exceeded 65535 bytes"))?;
}
writer.write_all(&u16::try_from(self.data.len()).unwrap().to_le_bytes())?;
@@ -218,7 +218,7 @@ impl ReadWrite for SignData {
pub enum Transaction {
// Once this completes successfully, no more instances should be created.
DkgCommitments(u32, Vec<u8>, Signed),
DkgShares(u32, HashMap<Participant, Vec<u8>>, Signed),
DkgShares(u32, Participant, HashMap<Participant, Vec<u8>>, Signed),
// When an external block is finalized, we can allow the associated batch IDs
// Commits to the full block so eclipsed nodes don't continue on their eclipsed state
@@ -264,6 +264,10 @@ impl ReadWrite for Transaction {
reader.read_exact(&mut attempt)?;
let attempt = u32::from_le_bytes(attempt);
let mut sender_i = [0; 2];
reader.read_exact(&mut sender_i)?;
let sender_i = u16::from_le_bytes(sender_i);
let shares = {
let mut share_quantity = [0; 2];
reader.read_exact(&mut share_quantity)?;
@@ -274,7 +278,10 @@ impl ReadWrite for Transaction {
let mut shares = HashMap::new();
for i in 0 .. u16::from_le_bytes(share_quantity) {
let participant = Participant::new(i + 1).unwrap();
let mut participant = Participant::new(i + 1).unwrap();
if u16::from(participant) >= sender_i {
participant = Participant::new(u16::from(participant) + 1).unwrap();
}
let mut share = vec![0; share_len];
reader.read_exact(&mut share)?;
shares.insert(participant, share);
@@ -284,7 +291,13 @@ impl ReadWrite for Transaction {
let signed = Signed::read(reader)?;
Ok(Transaction::DkgShares(attempt, shares, signed))
Ok(Transaction::DkgShares(
attempt,
Participant::new(sender_i)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid sender participant"))?,
shares,
signed,
))
}
2 => {
@@ -336,14 +349,30 @@ impl ReadWrite for Transaction {
signed.write(writer)
}
Transaction::DkgShares(attempt, shares, signed) => {
Transaction::DkgShares(attempt, sender_i, shares, signed) => {
writer.write_all(&[1])?;
writer.write_all(&attempt.to_le_bytes())?;
// It's unfortunate to have this so duplicated, yet it avoids needing to pass a Spec to
// read in order to create a valid DkgShares
// TODO: Transform DkgShares to having a Vec of shares, with post-expansion to the proper
// HashMap
writer.write_all(&u16::from(*sender_i).to_le_bytes())?;
// Shares are indexed by non-zero u16s (Participants), so this can't fail
writer.write_all(&u16::try_from(shares.len()).unwrap().to_le_bytes())?;
let mut share_len = None;
for participant in 0 .. shares.len() {
let share = &shares[&Participant::new(u16::try_from(participant + 1).unwrap()).unwrap()];
let mut found_our_share = false;
for participant in 1 ..= (shares.len() + 1) {
let Some(share) =
&shares.get(&Participant::new(u16::try_from(participant).unwrap()).unwrap())
else {
assert!(!found_our_share);
found_our_share = true;
continue;
};
if let Some(share_len) = share_len {
if share.len() != share_len {
panic!("variable length shares");
@@ -405,7 +434,7 @@ impl TransactionTrait for Transaction {
fn kind(&self) -> TransactionKind<'_> {
match self {
Transaction::DkgCommitments(_, _, signed) => TransactionKind::Signed(signed),
Transaction::DkgShares(_, _, signed) => TransactionKind::Signed(signed),
Transaction::DkgShares(_, _, _, signed) => TransactionKind::Signed(signed),
Transaction::ExternalBlock(_) => TransactionKind::Provided("external"),
Transaction::SubstrateBlock(_) => TransactionKind::Provided("serai"),
@@ -463,7 +492,7 @@ impl Transaction {
fn signed(tx: &mut Transaction) -> &mut Signed {
match tx {
Transaction::DkgCommitments(_, _, ref mut signed) => signed,
Transaction::DkgShares(_, _, ref mut signed) => signed,
Transaction::DkgShares(_, _, _, ref mut signed) => signed,
Transaction::ExternalBlock(_) => panic!("signing ExternalBlock"),
Transaction::SubstrateBlock(_) => panic!("signing SubstrateBlock"),

View File

@@ -137,6 +137,15 @@ async fn handle_block<D: Db, Pro: Processors>(
}
assert_eq!(data.len(), usize::from(needed));
// Remove our own piece of data
assert!(data
.remove(
&spec
.i(Ristretto::generator() * key.deref())
.expect("handling a message for a Tributary we aren't part of")
)
.is_some());
return Some(data);
}
None
@@ -147,6 +156,7 @@ async fn handle_block<D: Db, Pro: Processors>(
if let Some(commitments) =
handle(Zone::Dkg, b"dkg_commitments", spec.n(), [0; 32], attempt, bytes, signed)
{
log::info!("got all DkgCommitments for {}", hex::encode(genesis));
processors
.send(
spec.set().network,
@@ -159,23 +169,33 @@ async fn handle_block<D: Db, Pro: Processors>(
}
}
Transaction::DkgShares(attempt, mut shares, signed) => {
if shares.len() != usize::from(spec.n()) {
Transaction::DkgShares(attempt, sender_i, mut shares, signed) => {
if sender_i !=
spec
.i(signed.signer)
.expect("transaction added to tributary by signer who isn't a participant")
{
// TODO: Full slash
todo!();
}
let bytes = shares
.remove(
&spec
.i(Ristretto::generator() * key.deref())
.expect("in a tributary we're not a validator for"),
)
.unwrap();
if shares.len() != (usize::from(spec.n()) - 1) {
// TODO: Full slash
todo!();
}
// Only save our share's bytes
let our_i = spec
.i(Ristretto::generator() * key.deref())
.expect("in a tributary we're not a validator for");
// This unwrap is safe since the length of shares is checked, the the only missing key
// within the valid range will be the sender's i
let bytes = if sender_i == our_i { vec![] } else { shares.remove(&our_i).unwrap() };
if let Some(shares) =
handle(Zone::Dkg, b"dkg_shares", spec.n(), [0; 32], attempt, bytes, signed)
{
log::info!("got all DkgShares for {}", hex::encode(genesis));
processors
.send(
spec.set().network,