serai/processor/src/scanner.rs

531 lines
18 KiB
Rust
Raw Normal View History

use core::marker::PhantomData;
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
use std::{
sync::Arc,
time::Duration,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
collections::{HashSet, HashMap},
};
use ciphersuite::group::GroupEncoding;
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
use frost::curve::Ciphersuite;
use log::{info, debug, warn};
use tokio::{
sync::{RwLock, mpsc},
time::sleep,
};
use serai_client::primitives::BlockHash;
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
use crate::{
Get, DbTxn, Db,
coins::{Output, Transaction, EventualitiesTracker, Block, Coin},
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
};
#[derive(Clone, Debug)]
pub enum ScannerEvent<C: Coin> {
// Block scanned
2023-04-11 12:47:15 +00:00
Block {
key: <C::Curve as Ciphersuite>::G,
block: <C::Block as Block<C>>::Id,
batch: u32,
outputs: Vec<C::Output>,
},
// Eventuality completion found on-chain
Completed([u8; 32], <C::Transaction as Transaction<C>>::Id),
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
pub type ScannerEventChannel<C> = mpsc::UnboundedReceiver<ScannerEvent<C>>;
#[derive(Clone, Debug)]
struct ScannerDb<C: Coin, D: Db>(PhantomData<C>, PhantomData<D>);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
impl<C: Coin, D: Db> ScannerDb<C, D> {
fn scanner_key(dst: &'static [u8], key: impl AsRef<[u8]>) -> Vec<u8> {
D::key(b"SCANNER", dst, key)
}
fn block_key(number: usize) -> Vec<u8> {
Self::scanner_key(b"block_id", u64::try_from(number).unwrap().to_le_bytes())
}
fn block_number_key(id: &<C::Block as Block<C>>::Id) -> Vec<u8> {
Self::scanner_key(b"block_number", id)
}
fn save_block(txn: &mut D::Transaction<'_>, number: usize, id: &<C::Block as Block<C>>::Id) {
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
txn.put(Self::block_number_key(id), u64::try_from(number).unwrap().to_le_bytes());
txn.put(Self::block_key(number), id);
}
fn block<G: Get>(getter: &G, number: usize) -> Option<<C::Block as Block<C>>::Id> {
getter.get(Self::block_key(number)).map(|id| {
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut res = <C::Block as Block<C>>::Id::default();
res.as_mut().copy_from_slice(&id);
res
})
}
fn block_number<G: Get>(getter: &G, id: &<C::Block as Block<C>>::Id) -> Option<usize> {
getter
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
.get(Self::block_number_key(id))
.map(|number| u64::from_le_bytes(number.try_into().unwrap()).try_into().unwrap())
}
fn active_keys_key() -> Vec<u8> {
Self::scanner_key(b"active_keys", b"")
}
fn add_active_key(txn: &mut D::Transaction<'_>, key: <C::Curve as Ciphersuite>::G) {
let mut keys = txn.get(Self::active_keys_key()).unwrap_or(vec![]);
let key_bytes = key.to_bytes();
let key_len = key_bytes.as_ref().len();
2023-04-11 10:06:17 +00:00
assert_eq!(keys.len() % key_len, 0);
// Don't add this key if it's already present
let mut i = 0;
while i < keys.len() {
if &keys[i .. (i + key_len)] == key_bytes.as_ref() {
debug!("adding {} as an active key yet it was already present", hex::encode(key_bytes));
return;
}
i += key_len;
}
keys.extend(key_bytes.as_ref());
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
txn.put(Self::active_keys_key(), keys);
}
fn active_keys<G: Get>(getter: &G) -> Vec<<C::Curve as Ciphersuite>::G> {
let bytes_vec = getter.get(Self::active_keys_key()).unwrap_or(vec![]);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut bytes: &[u8] = bytes_vec.as_ref();
// Assumes keys will be 32 bytes when calculating the capacity
// If keys are larger, this may allocate more memory than needed
// If keys are smaller, this may require additional allocations
// Either are fine
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut res = Vec::with_capacity(bytes.len() / 32);
while !bytes.is_empty() {
res.push(C::Curve::read_G(&mut bytes).unwrap());
}
res
}
fn seen_key(id: &<C::Output as Output>::Id) -> Vec<u8> {
Self::scanner_key(b"seen", id)
}
fn seen<G: Get>(getter: &G, id: &<C::Output as Output>::Id) -> bool {
getter.get(Self::seen_key(id)).is_some()
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
2023-04-11 12:47:15 +00:00
fn next_batch_key() -> Vec<u8> {
Self::scanner_key(b"next_batch", [])
}
fn batch_key(key: &<C::Curve as Ciphersuite>::G, block: &<C::Block as Block<C>>::Id) -> Vec<u8> {
Self::scanner_key(b"batch", [key.to_bytes().as_ref(), block.as_ref()].concat())
}
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
fn outputs_key(
key: &<C::Curve as Ciphersuite>::G,
block: &<C::Block as Block<C>>::Id,
) -> Vec<u8> {
2023-04-11 12:47:15 +00:00
Self::scanner_key(b"outputs", [key.to_bytes().as_ref(), block.as_ref()].concat())
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
fn save_outputs(
txn: &mut D::Transaction<'_>,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
key: &<C::Curve as Ciphersuite>::G,
block: &<C::Block as Block<C>>::Id,
outputs: &[C::Output],
2023-04-11 12:47:15 +00:00
) -> u32 {
let batch_key = Self::batch_key(key, block);
if let Some(batch) = txn.get(batch_key) {
return u32::from_le_bytes(batch.try_into().unwrap());
}
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut bytes = Vec::with_capacity(outputs.len() * 64);
for output in outputs {
output.write(&mut bytes).unwrap();
}
txn.put(Self::outputs_key(key, block), bytes);
2023-04-11 12:47:15 +00:00
// This is a new set of outputs, which are expected to be handled in a perfectly ordered
// fashion
// TODO2: This is not currently how this works
// There may be new blocks 0 .. 5, which A will scan, yet then B may be activated at block 4
// This would cause
// 0a, 1a, 2a, 3a, 4a, 5a, 4b, 5b
// when it should be
// 0a, 1a, 2a, 3a, 4a, 4b, 5a, 5b
// Because it's a new set of outputs, allocate a batch ID for it
let next_bytes = txn.get(Self::next_batch_key()).unwrap_or(vec![0; 4]).try_into().unwrap();
let next = u32::from_le_bytes(next_bytes);
txn.put(Self::next_batch_key(), (next + 1).to_le_bytes());
txn.put(Self::batch_key(key, block), next_bytes);
next
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
fn outputs(
txn: &D::Transaction<'_>,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
key: &<C::Curve as Ciphersuite>::G,
block: &<C::Block as Block<C>>::Id,
) -> Option<Vec<C::Output>> {
let bytes_vec = txn.get(Self::outputs_key(key, block))?;
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut bytes: &[u8] = bytes_vec.as_ref();
let mut res = vec![];
while !bytes.is_empty() {
res.push(C::Output::read(&mut bytes).unwrap());
}
Some(res)
}
fn scanned_block_key(key: &<C::Curve as Ciphersuite>::G) -> Vec<u8> {
Self::scanner_key(b"scanned_block", key.to_bytes())
}
#[allow(clippy::type_complexity)]
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
fn save_scanned_block(
txn: &mut D::Transaction<'_>,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
key: &<C::Curve as Ciphersuite>::G,
block: usize,
) -> (Option<<C::Block as Block<C>>::Id>, Vec<C::Output>) {
let id = Self::block(txn, block); // It may be None for the first key rotated to
let outputs = if let Some(id) = id.as_ref() {
Self::outputs(txn, key, id).unwrap_or(vec![])
} else {
vec![]
};
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
// Mark all the outputs from this block as seen
for output in &outputs {
txn.put(Self::seen_key(&output.id()), b"");
}
txn.put(Self::scanned_block_key(key), u64::try_from(block).unwrap().to_le_bytes());
// Return this block's outputs so they can be pruned from the RAM cache
(id, outputs)
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
fn latest_scanned_block<G: Get>(getter: &G, key: <C::Curve as Ciphersuite>::G) -> usize {
let bytes = getter
.get(Self::scanned_block_key(&key))
.expect("asking for latest scanned block of key which wasn't rotated to");
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
u64::from_le_bytes(bytes.try_into().unwrap()).try_into().unwrap()
}
}
/// The Scanner emits events relating to the blockchain, notably received outputs.
/// It WILL NOT fail to emit an event, even if it reboots at selected moments.
/// It MAY fire the same event multiple times.
#[derive(Debug)]
pub struct Scanner<C: Coin, D: Db> {
coin: C,
db: D,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
keys: Vec<<C::Curve as Ciphersuite>::G>,
eventualities: EventualitiesTracker<C::Eventuality>,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
ram_scanned: HashMap<Vec<u8>, usize>,
ram_outputs: HashSet<Vec<u8>>,
events: mpsc::UnboundedSender<ScannerEvent<C>>,
}
#[derive(Debug)]
pub struct ScannerHandle<C: Coin, D: Db> {
scanner: Arc<RwLock<Scanner<C, D>>>,
pub events: ScannerEventChannel<C>,
}
impl<C: Coin, D: Db> ScannerHandle<C, D> {
pub async fn ram_scanned(&self) -> usize {
let mut res = None;
for scanned in self.scanner.read().await.ram_scanned.values() {
if res.is_none() {
res = Some(*scanned);
}
// Returns the lowest scanned value so no matter the keys interacted with, this is
// sufficiently scanned
res = Some(res.unwrap().min(*scanned));
}
res.unwrap_or(0)
}
pub async fn register_eventuality(
&mut self,
block_number: usize,
id: [u8; 32],
eventuality: C::Eventuality,
) {
self.scanner.write().await.eventualities.register(block_number, id, eventuality)
}
pub async fn drop_eventuality(&mut self, id: [u8; 32]) {
self.scanner.write().await.eventualities.drop(id);
}
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
/// Rotate the key being scanned for.
///
/// If no key has been prior set, this will become the key with no further actions.
///
/// If a key has been prior set, both keys will be scanned for as detailed in the Multisig
/// documentation. The old key will eventually stop being scanned for, leaving just the
/// updated-to key.
pub async fn rotate_key(
&mut self,
txn: &mut D::Transaction<'_>,
activation_number: usize,
key: <C::Curve as Ciphersuite>::G,
) {
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut scanner = self.scanner.write().await;
if !scanner.keys.is_empty() {
// Protonet will have a single, static validator set
// TODO2
panic!("only a single key is supported at this time");
}
info!("Rotating to key {}", hex::encode(key.to_bytes()));
let (_, outputs) = ScannerDb::<C, D>::save_scanned_block(txn, &key, activation_number);
scanner.ram_scanned.insert(key.to_bytes().as_ref().to_vec(), activation_number);
assert!(outputs.is_empty());
ScannerDb::<C, D>::add_active_key(txn, key);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
scanner.keys.push(key);
}
// This perform a database read which isn't safe with regards to if the value is set or not
// It may be set, when it isn't expected to be set, or not set, when it is expected to be set
// Since the value is static, if it's set, it's correctly set
pub async fn block_number(&self, id: &<C::Block as Block<C>>::Id) -> Option<usize> {
ScannerDb::<C, D>::block_number(&self.scanner.read().await.db, id)
}
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
/// Acknowledge having handled a block for a key.
pub async fn ack_up_to_block(
&mut self,
txn: &mut D::Transaction<'_>,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
key: <C::Curve as Ciphersuite>::G,
id: <C::Block as Block<C>>::Id,
) -> (Vec<BlockHash>, Vec<C::Output>) {
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut scanner = self.scanner.write().await;
debug!("Block {} acknowledged", hex::encode(&id));
// Get the number for this block
let number = ScannerDb::<C, D>::block_number(txn, &id)
.expect("main loop trying to operate on data we haven't scanned");
// Get the number of the last block we acknowledged
let prior = ScannerDb::<C, D>::latest_scanned_block(txn, key);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let mut blocks = vec![];
let mut outputs = vec![];
for number in (prior + 1) ..= number {
let (block, these_outputs) = ScannerDb::<C, D>::save_scanned_block(txn, &key, number);
let block = BlockHash(block.unwrap().as_ref().try_into().unwrap());
blocks.push(block);
outputs.extend(these_outputs);
}
assert_eq!(blocks.last().unwrap().as_ref(), id.as_ref());
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
for output in &outputs {
assert!(scanner.ram_outputs.remove(output.id().as_ref()));
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
(blocks, outputs)
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
}
impl<C: Coin, D: Db> Scanner<C, D> {
#[allow(clippy::new_ret_no_self)]
pub fn new(coin: C, db: D) -> (ScannerHandle<C, D>, Vec<<C::Curve as Ciphersuite>::G>) {
let (events_send, events_recv) = mpsc::unbounded_channel();
let keys = ScannerDb::<C, D>::active_keys(&db);
let mut ram_scanned = HashMap::new();
for key in keys.clone() {
ram_scanned.insert(
key.to_bytes().as_ref().to_vec(),
ScannerDb::<C, D>::latest_scanned_block(&db, key),
);
}
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let scanner = Arc::new(RwLock::new(Scanner {
coin,
db,
keys: keys.clone(),
eventualities: EventualitiesTracker::new(),
ram_scanned,
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
ram_outputs: HashSet::new(),
events: events_send,
}));
tokio::spawn(Scanner::run(scanner.clone()));
(ScannerHandle { scanner, events: events_recv }, keys)
}
fn emit(&mut self, event: ScannerEvent<C>) -> bool {
if self.events.send(event).is_err() {
info!("Scanner handler was dropped. Shutting down?");
return false;
}
true
}
// An async function, to be spawned on a task, to discover and report outputs
async fn run(scanner: Arc<RwLock<Self>>) {
loop {
// Only check every five seconds for new blocks
sleep(Duration::from_secs(5)).await;
// Scan new blocks
{
let mut scanner = scanner.write().await;
let latest = scanner.coin.get_latest_block_number().await;
let latest = match latest {
// Only scan confirmed blocks, which we consider effectively finalized
// CONFIRMATIONS - 1 as whatever's in the latest block already has 1 confirm
Ok(latest) => latest.saturating_sub(C::CONFIRMATIONS.saturating_sub(1)),
Err(_) => {
2023-04-11 10:06:17 +00:00
warn!("couldn't get latest block number");
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
sleep(Duration::from_secs(60)).await;
continue;
}
};
for key in scanner.keys.clone() {
let key_vec = key.to_bytes().as_ref().to_vec();
let latest_scanned = scanner.ram_scanned[&key_vec];
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
for i in (latest_scanned + 1) ..= latest {
// TODO2: Check for key deprecation
let block = match scanner.coin.get_block(i).await {
Ok(block) => block,
Err(_) => {
2023-04-11 10:06:17 +00:00
warn!("couldn't get block {i}");
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
break;
}
};
let block_id = block.id();
// These block calls are safe, despite not having a txn, since they're static values
// only written to/read by this thread
// There's also no error caused by them being unexpectedly written (if the commit is
// made and then the processor suddenly reboots)
if let Some(id) = ScannerDb::<C, D>::block(&scanner.db, i) {
if id != block_id {
2023-04-11 10:06:17 +00:00
panic!("reorg'd from finalized {} to {}", hex::encode(id), hex::encode(block_id));
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
}
} else {
info!("Found new block: {}", hex::encode(&block_id));
2023-04-16 05:03:32 +00:00
if let Some(id) = ScannerDb::<C, D>::block(&scanner.db, i.saturating_sub(1)) {
2023-04-16 05:03:32 +00:00
if id != block.parent() {
panic!(
"block {} doesn't build off expected parent {}",
hex::encode(block_id),
hex::encode(id),
);
}
}
let mut txn = scanner.db.txn();
ScannerDb::<C, D>::save_block(&mut txn, i, &block_id);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
txn.commit();
}
// Clone coin because we can't borrow it while also mutably borrowing the eventualities
// Thankfully, coin is written to be a cheap clone
let coin = scanner.coin.clone();
for (id, tx) in
coin.get_eventuality_completions(&mut scanner.eventualities, &block).await
{
// This should only happen if there's a P2P net desync or there's a malicious
// validator
warn!(
"eventuality {} resolved by {}, as found on chain. this should not happen",
hex::encode(id),
hex::encode(&tx)
);
if !scanner.emit(ScannerEvent::Completed(id, tx)) {
return;
}
}
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let outputs = match scanner.coin.get_outputs(&block, key).await {
Ok(outputs) => outputs,
Err(_) => {
2023-04-11 10:06:17 +00:00
warn!("Couldn't scan block {i}");
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
break;
}
};
// Panic if we've already seen these outputs
for output in &outputs {
let id = output.id();
info!(
"block {} had output {} worth {}",
hex::encode(&block_id),
hex::encode(&id),
2023-03-25 08:06:25 +00:00
output.amount(),
);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
// On Bitcoin, the output ID should be unique for a given chain
// On Monero, it's trivial to make an output sharing an ID with another
// We should only scan outputs with valid IDs however, which will be unique
/*
The safety of this code must satisfy the following conditions:
1) seen is not set for the first occurrence
2) seen is set for any future occurrence
seen is only written to after this code completes. Accordingly, it cannot be set
before the first occurrence UNLESSS it's set, yet the last scanned block isn't.
They are both written in the same database transaction, preventing this.
As for future occurrences, the RAM entry ensures they're handled properly even if
the database has yet to be set.
On reboot, which will clear the RAM, if seen wasn't set, neither was latest scanned
block. Accordingly, this will scan from some prior block, re-populating the RAM.
If seen was set, then this will be successfully read.
There's also no concern ram_outputs was pruned, yet seen wasn't set, as pruning
from ram_outputs will acquire a write lock (preventing this code from acquiring
its own write lock and running), and during its holding of the write lock, it
commits the transaction setting seen and the latest scanned block.
This last case isn't true. Committing seen/latest_scanned_block happens after
relinquishing the write lock.
TODO: Only update ram_outputs after committing the TXN in question.
*/
let seen = ScannerDb::<C, D>::seen(&scanner.db, &id);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
let id = id.as_ref().to_vec();
if seen || scanner.ram_outputs.contains(&id) {
panic!("scanned an output multiple times");
}
scanner.ram_outputs.insert(id);
}
if outputs.is_empty() {
continue;
}
// Save the outputs to disk
let mut txn = scanner.db.txn();
let batch = ScannerDb::<C, D>::save_outputs(&mut txn, &key, &block_id, &outputs);
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
txn.commit();
// Send all outputs
if !scanner.emit(ScannerEvent::Block { key, block: block_id, batch, outputs }) {
Processor (#259) * Initial work on a message box * Finish message-box (untested) * Expand documentation * Embed the recipient in the signature challenge Prevents a message from A -> B from being read as from A -> C. * Update documentation by bifurcating sender/receiver * Panic on receiving an invalid signature If we've received an invalid signature in an authenticated system, a service is malicious, critically faulty (equivalent to malicious), or the message layer has been compromised (or is otherwise critically faulty). Please note a receiver who handles a message they shouldn't will trigger this. That falls under being critically faulty. * Documentation and helper methods SecureMessage::new and SecureMessage::serialize. Secure Debug for MessageBox. * Have SecureMessage not be serialized by default Allows passing around in-memory, if desired, and moves the error from decrypt to new (which performs deserialization). Decrypt no longer has an error since it panics if given an invalid signature, due to this being intranet code. * Explain and improve nonce handling Includes a missing zeroize call. * Rebase to latest develop Updates to transcript 0.2.0. * Add a test for the MessageBox * Export PrivateKey and PublicKey * Also test serialization * Add a key_gen binary to message_box * Have SecureMessage support Serde * Add encrypt_to_bytes and decrypt_from_bytes * Support String ser via base64 * Rename encrypt/decrypt to encrypt_bytes/decrypt_to_bytes * Directly operate with values supporting Borsh * Use bincode instead of Borsh By staying inside of serde, we'll support many more structs. While bincode isn't canonical, we don't need canonicity on an authenticated, internal system. * Turn PrivateKey, PublicKey into structs Uses Zeroizing for the PrivateKey per #150. * from_string functions intended for loading from an env * Use &str for PublicKey from_string (now from_str) The PrivateKey takes the String to take ownership of its memory and zeroize it. That isn't needed with PublicKeys. * Finish updating from develop * Resolve warning * Use ZeroizingAlloc on the key_gen binary * Move message-box from crypto/ to common/ * Move key serialization functions to ser * add/remove functions in MessageBox * Implement Hash on dalek_ff_group Points * Make MessageBox generic to its key Exposes a &'static str variant for internal use and a RistrettoPoint variant for external use. * Add Private to_string as deprecated Stub before more competent tooling is deployed. * Private to_public * Test both Internal and External MessageBox, only use PublicKey in the pub API * Remove panics on invalid signatures Leftover from when this was solely internal which is now unsafe. * Chicken scratch a Scanner task * Add a write function to the DKG library Enables writing directly to a file. Also modifies serialize to return Zeroizing<Vec<u8>> instead of just Vec<u8>. * Make dkg::encryption pub * Remove encryption from MessageBox * Use a 64-bit block number in Substrate We use a 64-bit block number in general since u32 only works for 120 years (with a 1 second block time). As some chains even push the 1 second threshold, especially ones based on DAG consensus, this becomes potentially as low as 60 years. While that should still be plenty, it's not worth wondering/debating. Since Serai uses 64-bit block numbers elsewhere, this ensures consistency. * Misc crypto lints * Get the scanner scratch to compile * Initial scanner test * First few lines of scheduler * Further work on scheduler, solidify API * Define Scheduler TX format * Branch creation algorithm * Document when the branch algorithm isn't perfect * Only scanned confirmed blocks * Document Coin * Remove Canonical/ChainNumber from processor The processor should be abstracted from canonical numbers thanks to the coordinator, making this unnecessary. * Add README documenting processor flow * Use Zeroize on substrate primitives * Define messages from/to the processor * Correct over-specified versioning * Correct build re: in_instructions::primitives * Debug/some serde in crypto/ * Use a struct for ValidatorSetInstance * Add a processor key_gen task Redos DB handling code. * Replace trait + impl with wrapper struct * Add a key confirmation flow to the key gen task * Document concerns on key_gen * Start on a signer task * Add Send to FROST traits * Move processor lib.rs to main.rs Adds a dummy main to reduce clippy dead_code warnings. * Further flesh out main.rs * Move the DB trait to AsRef<[u8]> * Signer task * Remove a panic in bitcoin when there's insufficient funds Unchecked underflow. * Have Monero's mine_block mine one block, not 10 It was initially a nicety to deal with the 10 block lock. C::CONFIRMATIONS should be used for that instead. * Test signer * Replace channel expects with log statements The expects weren't problematic and had nicer code. They just clutter test output. * Remove the old wallet file It predates the coordinator design and shouldn't be used. * Rename tests/scan.rs to tests/scanner.rs * Add a wallet test Complements the recently removed wallet file by adding a test for the scanner, scheduler, and signer together. * Work on a run function Triggers a clippy ICE. * Resolve clippy ICE The issue was the non-fully specified lambda in signer. * Add KeyGenEvent and KeyGenOrder Needed so we get KeyConfirmed messages from the key gen task. While we could've read the CoordinatorMessage to see that, routing through the key gen tasks ensures we only handle it once it's been successfully saved to disk. * Expand scanner test * Clarify processor documentation * Have the Scanner load keys on boot/save outputs to disk * Use Vec<u8> for Block ID Much more flexible. * Panic if we see the same output multiple times * Have the Scanner DB mark itself as corrupt when doing a multi-put This REALLY should be a TX. Since we don't have a TX API right now, this at least offers detection. * Have DST'd DB keys accept AsRef<[u8]> * Restore polling all signers Writes a custom future to do so. Also loads signers on boot using what the scanner claims are active keys. * Schedule OutInstructions Adds a data field to Payment. Also cleans some dead code. * Panic if we create an invalid transaction Saves the TX once it's successfully signed so if we do panic, we have a copy. * Route coordinator messages to their respective signer Requires adding key to the SignId. * Send SignTransaction orders for all plans * Add a timer to retry sign_plans when prepare_send fails * Minor fmt'ing * Basic Fee API * Move the change key into Plan * Properly route activation_number * Remove ScannerEvent::Block It's not used under current designs * Nicen logs * Add utilities to get a block's number * Have main issue AckBlock Also has a few misc lints. * Parse instructions out of outputs * Tweak TODOs and remove an unwrap * Update Bitcoin max input/output quantity * Only read one piece of data from Monero Due to output randomization, it's infeasible. * Embed plan IDs into the TXs they create We need to stop attempting signing if we've already signed a protocol. Ideally, any one of the participating signers should be able to provide a proof the TX was successfully signed. We can't just run a second signing protocol though as a single malicious signer could complete the TX signature, and publish it, yet not complete the secondary signature. The TX itself has to be sufficient to show that the TX matches the plan. This is done by embedding the ID, so matching addresses/amounts plans are distinguished, and by allowing verification a TX actually matches a set of addresses/amounts. For Monero, this will need augmenting with the ephemeral keys (or usage of a static seed for them). * Don't use OP_RETURN to encode the plan ID on Bitcoin We can use the inputs to distinguih identical-output plans without issue. * Update OP_RETURN data access It's not required to be the last output. * Add Eventualities to Monero An Eventuality is an effective equivalent to a SignableTransaction. That is declared not by the inputs it spends, yet the outputs it creates. Eventualities are also bound to a 32-byte RNG seed, enabling usage of a hash-based identifier in a SignableTransaction, allowing multiple SignableTransactions with the same output set to have different Eventualities. In order to prevent triggering the burning bug, the RNG seed is hashed with the planned-to-be-used inputs' output keys. While this does bind to them, it's only loosely bound. The TX actually created may use different inputs entirely if a forgery is crafted (which requires no brute forcing). Binding to the key images would provide a strong binding, yet would require knowing the key images, which requires active communication with the spend key. The purpose of this is so a multisig can identify if a Transaction the entire group planned has been executed by a subset of the group or not. Once a plan is created, it can have an Eventuality made. The Eventuality's extra is able to be inserted into a HashMap, so all new on-chain transactions can be trivially checked as potential candidates. Once a potential candidate is found, a check involving ECC ops can be performed. While this is arguably a DoS vector, the underlying Monero blockchain would need to be spammed with transactions to trigger it. Accordingly, it becomes a Monero blockchain DoS vector, when this code is written on the premise of the Monero blockchain functioning. Accordingly, it is considered handled. If a forgery does match, it must have created the exact same outputs the multisig would've. Accordingly, it's argued the multisig shouldn't mind. This entire suite of code is only necessary due to the lack of outgoing view keys, yet it's able to avoid an interactive protocol to communicate key images on every single received output. While this could be locked to the multisig feature, there's no practical benefit to doing so. * Add support for encoding Monero address to instructions * Move Serai's Monero address encoding into serai-client serai-client is meant to be a single library enabling using Serai. While it was originally written as an RPC client for Serai, apps actually using Serai will primarily be sending transactions on connected networks. Sending those transactions require proper {In, Out}Instructions, including proper address encoding. Not only has address encoding been moved, yet the subxt client is now behind a feature. coin integrations have their own features, which are on by default. primitives are always exposed. * Reorganize file layout a bit, add feature flags to processor * Tidy up ETH Dockerfile * Add Bitcoin address encoding * Move Bitcoin::Address to serai-client's * Comment where tweaking needs to happen * Add an API to check if a plan was completed in a specific TX This allows any participating signer to submit the TX ID to prevent further signing attempts. Also performs some API cleanup. * Minimize FROST dependencies * Use a seeded RNG for key gen * Tweak keys from Key gen * Test proper usage of Branch/Change addresses Adds a more descriptive error to an error case in decoys, and pads Monero payments as needed. * Also test spending the change output * Add queued_plans to the Scheduler queued_plans is for payments to be issued when an amount appears, yet the amount is currently pre-fee. One the output is actually created, the Scheduler should be notified of the amount it was created with, moving from queued_plans to plans under the actual amount. Also tightens debug_asserts to asserts for invariants which may are at risk of being exclusive to prod. * Add missing tweak_keys call * Correct decoy selection height handling * Add a few log statements to the scheduler * Simplify test's get_block_number * Simplify, while making more robust, branch address handling in Scheduler * Have fees deducted from payments Corrects Monero's handling of fees when there's no change address. Adds a DUST variable, as needed due to 1_00_000_000 not being enough to pay its fee on Monero. * Add comment to Monero * Consolidate BTC/XMR prepare_send code These aren't fully consolidated. We'd need a SignableTransaction trait for that. This is a lot cleaner though. * Ban integrated addresses The reasoning why is accordingly documented. * Tidy TODOs/dust handling * Update README TODO * Use a determinisitic protocol version in Monero * Test rebuilt KeyGen machines function as expected * Use a more robust KeyGen entropy system * Add DB TXNs Also load entropy from env * Add a loop for processing messages from substrate Allows detecting if we're behind, and if so, waiting to handle the message * Set Monero MAX_INPUTS properly The previous number was based on an old hard fork. With the ring size having increased, transactions have since got larger. * Distinguish TODOs into TODO and TODO2s TODO2s are for after protonet * Zeroize secret share repr in ThresholdCore write * Work on Eventualities Adds serialization and stops signing when an eventuality is proven. * Use a more robust DB key schema * Update to {k, p}256 0.12 * cargo +nightly clippy * cargo update * Slight message-box tweaks * Update to recent Monero merge * Add a Coordinator trait for communication with coordinator * Remove KeyGenHandle for just KeyGen While KeyGen previously accepted instructions over a channel, this breaks the ack flow needed for coordinator communication. Now, KeyGen is the direct object with a handle() function for messages. Thankfully, this ended up being rather trivial for KeyGen as it has no background tasks. * Add a handle function to Signer Enables determining when it's finished handling a CoordinatorMessage and therefore creating an acknowledgement. * Save transactions used to complete eventualities * Use a more intelligent sleep in the signer * Emit SignedTransaction with the first ID *we can still get from our node* * Move Substrate message handling into the new coordinator recv loop * Add handle function to Scanner * Remove the plans timer Enables ensuring the ordring on the handling of plans. * Remove the outputs function which panicked if a precondition wasn't met The new API only returns outputs upon satisfaction of the precondition. * Convert SignerOrder::SignTransaction to a function * Remove the key_gen object from sign_plans * Refactor out get_fee/prepare_send into dedicated functions * Save plans being signed to the DB * Reload transactions being signed on boot * Stop reloading TXs being signed (and report it to peers) * Remove message-box from the processor branch We don't use it here yet. * cargo +nightly fmt * Move back common/zalloc * Update subxt to 0.27 * Zeroize ^1.5, not 1 * Update GitHub workflow * Remove usage of SignId in completed
2023-03-17 02:59:40 +00:00
return;
}
// Write this number as scanned so we won't re-fire these outputs
scanner.ram_scanned.insert(key_vec.clone(), i);
}
}
}
}
}
}