Skip to content

Commit 0d765be

Browse files
committed
Merge #357: Batch the exchange of signatures with the watchtowers
003a308 ci: remove most of the cache across runs (Antoine Poinsot) 67f4fc2 tests/servers: update miradord to latest master (Antoine Poinsot) 662a1bb commands, sigfetcher: batch the rev signatures exchange with watchtowers (Antoine Poinsot) Pull request description: As per revault/practical-revault#115. This greatly simplifies the implementation, too! ACKs for top commit: darosior: self-ACK 003a308 Tree-SHA512: 96351256bd06775498133d28ec68527f7e724378e2f0c3c4c8a044f47d3429a44c5291ba8bf31cdbcc28f2a1fca51e1d8420919be9b3c853298beea573237466
2 parents 478d6a5 + 003a308 commit 0d765be

File tree

11 files changed

+103
-174
lines changed

11 files changed

+103
-174
lines changed

.cirrus.yml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,6 @@ task:
2828
folder: $CARGO_HOME/registry
2929
fingerprint_script: cat Cargo.lock
3030
before_cache_script: rm -rf $CARGO_HOME/registry/index
31-
target_cache:
32-
folder: target
33-
fingerprint_script:
34-
- rustc --version
35-
- cat Cargo.lock
36-
bitcoind_cache:
37-
folder: $BITCOIND_DIR_NAME
38-
coordinatord_cache:
39-
folder: tests/servers/coordinatord
40-
fingerprint_script:
41-
- cd tests/servers/coordinatord && git rev-parse HEAD
42-
cosignerd_cache:
43-
folder: tests/servers/cosignerd
44-
fingerprint_script:
45-
- cd tests/servers/cosignerd && git rev-parse HEAD
4631

4732
test_script: |
4833
set -xe

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/mod.rs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ use crate::{
1414
communication::{
1515
announce_spend_transaction, check_spend_transaction_size, coord_share_rev_signatures,
1616
coordinator_status, cosigners_status, fetch_cosigs_signatures, share_unvault_signatures,
17-
watchtowers_status, wts_share_emer_signatures, wts_share_second_stage_signatures,
18-
CommunicationError,
17+
watchtowers_status, wts_share_rev_signatures, CommunicationError,
1918
},
2019
database::{
2120
actions::{
@@ -503,19 +502,40 @@ impl DaemonControl {
503502
.expect("The database must be available");
504503
db_mark_securing_vault(&db_path, db_vault.id).expect("The database must be available");
505504

506-
// If this made the Emergency fully signed and we are a stakeholder, share
507-
// it with our watchtowers.
508-
let emer_db_tx = db_emer_transaction(&db_path, db_vault.id)
509-
.expect("The database must be available")
505+
// Now, check whether this made all revocation transactions fully signed
506+
let emer_tx = db_emer_transaction(&db_path, db_vault.id)
507+
.expect("Database must be available")
508+
.ok_or(CommandError::Race)?;
509+
let cancel_tx = db_cancel_transaction(&db_path, db_vault.id)
510+
.expect("Database must be available")
510511
.ok_or(CommandError::Race)?;
511-
if !db_vault.emer_shared && emer_db_tx.psbt.unwrap_emer().is_finalizable(secp_ctx) {
512+
let unemer_tx = db_unvault_emer_transaction(&db_path, db_vault.id)
513+
.expect("Database must be available")
514+
.ok_or(CommandError::Race)?;
515+
let all_rev_fully_signed = emer_tx
516+
.psbt
517+
.unwrap_emer()
518+
.is_finalizable(&revaultd.secp_ctx)
519+
&& cancel_tx
520+
.psbt
521+
.unwrap_cancel()
522+
.is_finalizable(&revaultd.secp_ctx)
523+
&& unemer_tx
524+
.psbt
525+
.unwrap_unvault_emer()
526+
.is_finalizable(&revaultd.secp_ctx);
527+
528+
// If it did, share their signatures with our watchtowers
529+
if all_rev_fully_signed {
512530
if let Some(ref watchtowers) = revaultd.watchtowers {
513-
wts_share_emer_signatures(
531+
wts_share_rev_signatures(
514532
&revaultd.noise_secret,
515533
&watchtowers,
516534
db_vault.deposit_outpoint,
517535
db_vault.derivation_index,
518-
&emer_db_tx,
536+
&emer_tx,
537+
&cancel_tx,
538+
&unemer_tx,
519539
)?;
520540
}
521541
}
@@ -675,25 +695,6 @@ impl DaemonControl {
675695
})?;
676696
}
677697

678-
// The watchtower(s) MUST have our second-stage (UnvaultEmer, Cancel) signatures
679-
// before we share the Unvault one.
680-
if let Some(ref watchtowers) = revaultd.watchtowers {
681-
let unemer_db_tx = db_unvault_emer_transaction(&db_path, db_vault.id)
682-
.expect("The database must be available")
683-
.ok_or(CommandError::Race)?;
684-
let cancel_db_tx = db_cancel_transaction(&db_path, db_vault.id)
685-
.expect("The database must be available")
686-
.ok_or(CommandError::Race)?;
687-
wts_share_second_stage_signatures(
688-
&revaultd.noise_secret,
689-
&watchtowers,
690-
db_vault.deposit_outpoint,
691-
db_vault.derivation_index,
692-
&cancel_db_tx,
693-
&unemer_db_tx,
694-
)?;
695-
}
696-
697698
// Sanity checks passed. Store it then share it.
698699
db_update_presigned_txs(&db_path, &db_vault, vec![unvault_db_tx.clone()], secp_ctx)
699700
.expect("The database must be available");

src/communication.rs

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -87,24 +87,29 @@ impl From<revault_net::Error> for CommunicationError {
8787
}
8888
}
8989

90-
// Send a `sig` (https://github.com/revault/practical-revault/blob/master/messages.md#sig)
90+
// Send a `sigs` (https://github.com/revault/practical-revault/blob/master/messages.md#sigs)
9191
// message to a watchtower.
92-
fn send_wt_sig_msg(
92+
fn send_wt_sigs_msg(
9393
transport: &mut KKTransport,
9494
deposit_outpoint: OutPoint,
9595
derivation_index: ChildNumber,
96-
txid: Txid,
97-
signatures: BTreeMap<secp256k1::PublicKey, secp256k1::Signature>,
96+
emer_tx: &DbTransaction,
97+
cancel_tx: &DbTransaction,
98+
unemer_tx: &DbTransaction,
9899
) -> Result<(), CommunicationError> {
99-
let sig_msg = watchtower::Sig {
100+
let signatures = watchtower::Signatures {
101+
emergency: emer_tx.psbt.signatures(),
102+
cancel: cancel_tx.psbt.signatures(),
103+
unvault_emergency: unemer_tx.psbt.signatures(),
104+
};
105+
let sig_msg = watchtower::Sigs {
100106
signatures,
101-
txid,
102107
deposit_outpoint,
103108
derivation_index,
104109
};
105110

106111
log::debug!("Sending signatures to watchtower: '{:?}'", sig_msg);
107-
let sig_result: watchtower::SigResult = transport.send_req(&sig_msg.into())?;
112+
let sig_result: watchtower::SigsResult = transport.send_req(&sig_msg.into())?;
108113
log::debug!(
109114
"Got response to signatures for '{}' from watchtower: '{:?}'",
110115
deposit_outpoint,
@@ -149,63 +154,26 @@ pub fn send_coord_sig_msg(
149154
Ok(())
150155
}
151156

152-
/// Send the signatures for the Emergency transaction to the Watchtower.
153-
/// Only sharing the Emergency signature tells the watchtower to be aware of, but
154-
/// not watch, the vault. Later sharing the UnvaultEmergency and Cancel signatures
155-
/// will trigger it to watch it.
156-
// FIXME: better to share all signatures early and to then have another message
157-
// asking permission before delegating.
158-
pub fn wts_share_emer_signatures(
157+
/// Share the revocation transactions' signatures with all our watchtowers.
158+
pub fn wts_share_rev_signatures(
159159
noise_secret: &revault_net::noise::SecretKey,
160160
watchtowers: &[(std::net::SocketAddr, revault_net::noise::PublicKey)],
161161
deposit_outpoint: OutPoint,
162162
derivation_index: ChildNumber,
163163
emer_tx: &DbTransaction,
164-
) -> Result<(), CommunicationError> {
165-
for (wt_host, wt_noisekey) in watchtowers {
166-
let mut transport = KKTransport::connect(*wt_host, noise_secret, wt_noisekey)?;
167-
168-
send_wt_sig_msg(
169-
&mut transport,
170-
deposit_outpoint,
171-
derivation_index,
172-
emer_tx.psbt.txid(),
173-
emer_tx.psbt.signatures(),
174-
)?;
175-
}
176-
177-
Ok(())
178-
}
179-
180-
/// Send the signatures for the UnvaultEmergency and Cancel transactions to all
181-
/// our watchtowers. This serves as a signal for watchtowers to start watching for
182-
/// this vault and they may therefore NACK that we delegate it (eg if they don't
183-
/// have enough fee reserve).
184-
pub fn wts_share_second_stage_signatures(
185-
noise_secret: &revault_net::noise::SecretKey,
186-
watchtowers: &[(std::net::SocketAddr, revault_net::noise::PublicKey)],
187-
deposit_outpoint: OutPoint,
188-
derivation_index: ChildNumber,
189164
cancel_tx: &DbTransaction,
190-
unvault_emer_tx: &DbTransaction,
165+
unemer_tx: &DbTransaction,
191166
) -> Result<(), CommunicationError> {
192167
for (wt_host, wt_noisekey) in watchtowers {
193168
let mut transport = KKTransport::connect(*wt_host, noise_secret, wt_noisekey)?;
194169

195-
// The watchtower enforces a specific sequence in which to exchange transactions
196-
send_wt_sig_msg(
197-
&mut transport,
198-
deposit_outpoint,
199-
derivation_index,
200-
unvault_emer_tx.psbt.txid(),
201-
unvault_emer_tx.psbt.signatures(),
202-
)?;
203-
send_wt_sig_msg(
170+
send_wt_sigs_msg(
204171
&mut transport,
205172
deposit_outpoint,
206173
derivation_index,
207-
cancel_tx.psbt.txid(),
208-
cancel_tx.psbt.signatures(),
174+
emer_tx,
175+
cancel_tx,
176+
unemer_tx,
209177
)?;
210178
}
211179

src/database/actions.rs

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ pub fn db_insert_new_unconfirmed_vault(
262262
tx.execute(
263263
"INSERT INTO vaults ( \
264264
wallet_id, status, blockheight, deposit_txid, deposit_vout, amount, derivation_index, \
265-
funded_at, secured_at, delegated_at, moved_at, final_txid, emer_shared \
265+
funded_at, secured_at, delegated_at, moved_at, final_txid \
266266
) \
267-
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, NULL, NULL, NULL, NULL, NULL, 0)",
267+
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, NULL, NULL, NULL, NULL, NULL)",
268268
params![
269269
wallet_id,
270270
VaultStatus::Unconfirmed as u32,
@@ -657,18 +657,6 @@ pub fn db_mark_activating_vault(db_path: &Path, vault_id: u32) -> Result<(), Dat
657657
})
658658
}
659659

660-
/// Mark a vault as having its Emergency signature already shared with the watchtowers.
661-
pub fn db_mark_emer_shared(db_path: &Path, db_vault: &DbVault) -> Result<(), DatabaseError> {
662-
db_exec(db_path, |tx| {
663-
tx.execute(
664-
"UPDATE vaults SET emer_shared = 1 WHERE vaults.id = (?1)",
665-
params![db_vault.id],
666-
)
667-
.map(|_| ())
668-
.map_err(DatabaseError::from)
669-
})
670-
}
671-
672660
// Merge the partial sigs of two transactions of the same type into the first one
673661
//
674662
// Returns true if this made the transaction "valid" (fully signed).
@@ -1431,21 +1419,6 @@ mod test {
14311419
assert!(db_signed_emer_txs(&db_path).unwrap().is_empty());
14321420
assert_eq!(db_signed_unemer_txs(&db_path).unwrap().len(), 1);
14331421

1434-
// Sanity check we can mark the Emergency as shared with the watchtowers
1435-
assert!(
1436-
!db_vault_by_deposit(&db_path, &db_vault.deposit_outpoint)
1437-
.unwrap()
1438-
.unwrap()
1439-
.emer_shared
1440-
);
1441-
db_mark_emer_shared(&db_path, &db_vault).unwrap();
1442-
assert!(
1443-
db_vault_by_deposit(&db_path, &db_vault.deposit_outpoint)
1444-
.unwrap()
1445-
.unwrap()
1446-
.emer_shared
1447-
);
1448-
14491422
fs::remove_dir_all(&datadir).unwrap_or_else(|_| ());
14501423
}
14511424

src/database/interface.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ impl TryFrom<&Row<'_>> for DbVault {
234234
let final_txid = row
235235
.get::<_, Option<Vec<u8>>>(12)?
236236
.map(|raw_txid| encode::deserialize(&raw_txid).expect("We only store valid txids"));
237-
let emer_shared: bool = row.get(13)?;
238237

239238
Ok(DbVault {
240239
id,
@@ -249,7 +248,6 @@ impl TryFrom<&Row<'_>> for DbVault {
249248
delegated_at,
250249
moved_at,
251250
final_txid,
252-
emer_shared,
253251
})
254252
}
255253
}
@@ -321,7 +319,7 @@ pub fn db_unvaulted_vaults(
321319
],
322320
|row| {
323321
let db_vault: DbVault = row.try_into()?;
324-
let unvault_tx: Vec<u8> = row.get(14)?;
322+
let unvault_tx: Vec<u8> = row.get(13)?;
325323
let unvault_tx = UnvaultTransaction::from_psbt_serialized(&unvault_tx)
326324
.expect("We store it with as_psbt_serialized");
327325

@@ -345,7 +343,7 @@ pub fn db_spending_vaults(
345343
],
346344
|row| {
347345
let db_vault: DbVault = row.try_into()?;
348-
let unvault_tx: Vec<u8> = row.get(14)?;
346+
let unvault_tx: Vec<u8> = row.get(13)?;
349347
let unvault_tx = UnvaultTransaction::from_psbt_serialized(&unvault_tx)
350348
.expect("We store it with as_psbt_serialized");
351349

@@ -370,7 +368,7 @@ pub fn db_canceling_vaults(
370368
],
371369
|row| {
372370
let db_vault: DbVault = row.try_into()?;
373-
let cancel_tx: Vec<u8> = row.get(14)?;
371+
let cancel_tx: Vec<u8> = row.get(13)?;
374372
let cancel_tx = CancelTransaction::from_psbt_serialized(&cancel_tx)
375373
.expect("We store it with as_psbt_serialized");
376374

@@ -395,7 +393,7 @@ pub fn db_emering_vaults(
395393
],
396394
|row| {
397395
let db_vault: DbVault = row.try_into()?;
398-
let emer_tx: Vec<u8> = row.get(14)?;
396+
let emer_tx: Vec<u8> = row.get(13)?;
399397
let emer_tx = EmergencyTransaction::from_psbt_serialized(&emer_tx)
400398
.expect("We store it with to_psbt_serialized");
401399

@@ -420,7 +418,7 @@ pub fn db_unemering_vaults(
420418
],
421419
|row| {
422420
let db_vault: DbVault = row.try_into()?;
423-
let unemer_tx: Vec<u8> = row.get(14)?;
421+
let unemer_tx: Vec<u8> = row.get(13)?;
424422
let unemer_tx = UnvaultEmergencyTransaction::from_psbt_serialized(&unemer_tx)
425423
.expect("We store it with to_psbt_serialized");
426424

@@ -626,7 +624,7 @@ pub fn db_vault_by_unvault_txid(
626624
params![txid.to_vec(), TransactionType::Unvault as u32],
627625
|row| {
628626
let db_vault: DbVault = row.try_into()?;
629-
let offset = 14;
627+
let offset = 13;
630628

631629
// FIXME: there is probably a more extensible way to implement the from()s so we don't
632630
// have to change all those when adding a column
@@ -669,7 +667,7 @@ pub fn db_sig_missing(
669667
],
670668
|row| {
671669
let db_vault: DbVault = row.try_into()?;
672-
let db_tx: DbTransaction = db_tx_from_row(row, 14)?;
670+
let db_tx: DbTransaction = db_tx_from_row(row, 13)?;
673671

674672
if let Some(db_txs) = vault_map.get_mut(&db_vault) {
675673
db_txs.push(db_tx);
@@ -841,7 +839,7 @@ pub fn db_vaults_from_spend(
841839
params![spend_txid.to_vec()],
842840
|row| {
843841
let db_vault: DbVault = row.try_into()?;
844-
let txid: Txid = encode::deserialize(&row.get::<_, Vec<u8>>(14)?).expect("We store it");
842+
let txid: Txid = encode::deserialize(&row.get::<_, Vec<u8>>(13)?).expect("We store it");
845843
db_vaults.insert(txid, db_vault);
846844
Ok(())
847845
},

src/database/schema.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ CREATE TABLE wallets (
4545
* spending txid or the canceling txid out of a deposit outpoint.
4646
* It MUST be NOT NULL if status is 'spending', 'spent', 'canceling'
4747
* or 'canceled'.
48-
* The emer_shared field indicates wether we already shared the Emergency
49-
* transaction for this vault with the watchtower.
5048
*/
5149
CREATE TABLE vaults (
5250
id INTEGER PRIMARY KEY NOT NULL,
@@ -62,7 +60,6 @@ CREATE TABLE vaults (
6260
delegated_at INTEGER,
6361
moved_at INTEGER,
6462
final_txid BLOB,
65-
emer_shared BOOLEAN NOT NULL CHECK (emer_shared IN (0,1)),
6663
FOREIGN KEY (wallet_id) REFERENCES wallets (id)
6764
ON UPDATE RESTRICT
6865
ON DELETE RESTRICT
@@ -151,7 +148,6 @@ pub struct DbVault {
151148
pub delegated_at: Option<u32>,
152149
pub moved_at: Option<u32>,
153150
pub final_txid: Option<Txid>,
154-
pub emer_shared: bool,
155151
}
156152

157153
// FIXME: naming it "db transaction" was ambiguous..

0 commit comments

Comments
 (0)