Skip to content

Commit

Permalink
Resume cycles through all pending etchings (#3566)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph authored Apr 17, 2024
1 parent d40e8db commit 5311e24
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 94 deletions.
29 changes: 21 additions & 8 deletions src/subcommand/wallet/resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,26 @@ pub struct ResumeOutput {
}

pub(crate) fn run(wallet: Wallet) -> SubcommandResult {
let outputs: Result<Vec<batch::Output>> = wallet
.pending_etchings()?
.into_iter()
.map(|(rune, entry)| {
wallet.wait_for_maturation(&rune, entry.commit, entry.reveal, entry.output)
})
.collect();
let mut etchings = Vec::new();
loop {
if SHUTTING_DOWN.load(atomic::Ordering::Relaxed) {
break;
}

outputs.map(|etchings| Some(Box::new(ResumeOutput { etchings }) as Box<dyn Output>))
for (rune, entry) in wallet.pending_etchings()? {
if wallet.is_mature(&entry.commit)? {
etchings.push(wallet.send_etching(rune, &entry)?);
}
}

if wallet.pending_etchings()?.is_empty() {
break;
}

if !wallet.integration_test() {
thread::sleep(Duration::from_secs(5));
}
}

Ok(Some(Box::new(ResumeOutput { etchings }) as Box<dyn Output>))
}
82 changes: 47 additions & 35 deletions src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,57 +296,69 @@ impl Wallet {
self.settings.integration_test()
}

pub(crate) fn wait_for_maturation(
&self,
rune: &Rune,
commit: Transaction,
reveal: Transaction,
output: batch::Output,
) -> Result<batch::Output> {
eprintln!("Waiting for rune commitment {} to mature…", commit.txid());
pub(crate) fn is_mature(&self, commit: &Transaction) -> Result<bool> {
let transaction = self
.bitcoin_client()
.get_transaction(&commit.txid(), Some(true))
.into_option()?;

if let Some(transaction) = transaction {
if u32::try_from(transaction.info.confirmations).unwrap() + 1
>= Runestone::COMMIT_CONFIRMATIONS.into()
{
let tx_out = self
.bitcoin_client()
.get_tx_out(&commit.txid(), 0, Some(true))?;

if let Some(tx_out) = tx_out {
if tx_out.confirmations + 1 >= Runestone::COMMIT_CONFIRMATIONS.into() {
return Ok(true);
}
} else {
bail!("rune commitment spent, can't send reveal tx");
}
}
}

self.save_etching(rune, &commit, &reveal, output.clone())?;
Ok(false)
}

pub(crate) fn wait_for_maturation(&self, rune: Rune) -> Result<batch::Output> {
let Some(entry) = self.load_etching(rune)? else {
bail!("no etching found");
};

eprintln!(
"Waiting for rune {} commitment {} to mature…",
rune,
entry.commit.txid()
);

loop {
if SHUTTING_DOWN.load(atomic::Ordering::Relaxed) {
eprintln!("Suspending batch. Run `ord wallet resume` to continue.");
return Ok(output);
return Ok(entry.output);
}

let transaction = self
.bitcoin_client()
.get_transaction(&commit.txid(), Some(true))
.into_option()?;

if let Some(transaction) = transaction {
if u32::try_from(transaction.info.confirmations).unwrap() + 1
>= Runestone::COMMIT_CONFIRMATIONS.into()
{
let tx_out = self
.bitcoin_client()
.get_tx_out(&commit.txid(), 0, Some(true))?;

if let Some(tx_out) = tx_out {
if tx_out.confirmations + 1 >= Runestone::COMMIT_CONFIRMATIONS.into() {
break;
}
} else {
bail!("rune commitment spent, can't send reveal tx");
}
}
if self.is_mature(&entry.commit)? {
break;
}

if !self.integration_test() {
thread::sleep(Duration::from_secs(5));
}
}

match self.bitcoin_client().send_raw_transaction(&reveal) {
self.send_etching(rune, &entry)
}

pub(crate) fn send_etching(&self, rune: Rune, entry: &EtchingEntry) -> Result<batch::Output> {
match self.bitcoin_client().send_raw_transaction(&entry.reveal) {
Ok(txid) => txid,
Err(err) => {
return Err(anyhow!(
"Failed to send reveal transaction: {err}\nCommit tx {} will be recovered once mined",
commit.txid()
entry.commit.txid()
))
}
};
Expand All @@ -355,7 +367,7 @@ impl Wallet {

Ok(batch::Output {
reveal_broadcast: true,
..output
..entry.output.clone()
})
}

Expand Down Expand Up @@ -646,7 +658,7 @@ impl Wallet {
)
}

pub(crate) fn clear_etching(&self, rune: &Rune) -> Result {
pub(crate) fn clear_etching(&self, rune: Rune) -> Result {
let wtx = self.database.begin_write()?;

wtx.open_table(RUNE_TO_ETCHING)?.remove(rune.0)?;
Expand Down
12 changes: 8 additions & 4 deletions src/wallet/batch/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ impl Plan {
let commit = consensus::encode::deserialize::<Transaction>(&signed_commit_tx)?;
let reveal = consensus::encode::deserialize::<Transaction>(&signed_reveal_tx)?;

Ok(Some(Box::new(wallet.wait_for_maturation(
wallet.save_etching(
&rune_info.rune.rune,
commit.clone(),
reveal.clone(),
&commit,
&reveal,
self.output(
commit.txid(),
None,
Expand All @@ -151,7 +151,11 @@ impl Plan {
self.inscriptions.clone(),
rune.clone(),
),
)?)))
)?;

Ok(Some(Box::new(
wallet.wait_for_maturation(rune_info.rune.rune)?,
)))
} else {
let reveal = match wallet
.bitcoin_client()
Expand Down
20 changes: 12 additions & 8 deletions src/wallet/wallet_constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,18 @@ impl WalletConstructor {
let mut utxos = BTreeMap::new();

for outpoint in outpoints {
let txout = bitcoin_client
.get_raw_transaction(&outpoint.txid, None)?
.output
.get(TryInto::<usize>::try_into(outpoint.vout).unwrap())
.cloned()
.ok_or_else(|| anyhow!("Invalid output index"))?;

utxos.insert(OutPoint::new(outpoint.txid, outpoint.vout), txout);
let Some(tx_out) = bitcoin_client.get_tx_out(&outpoint.txid, outpoint.vout, Some(false))?
else {
continue;
};

utxos.insert(
OutPoint::new(outpoint.txid, outpoint.vout),
TxOut {
value: tx_out.value.to_sat(),
script_pubkey: ScriptBuf::from_bytes(tx_out.script_pub_key.hex),
},
);
}

Ok(utxos)
Expand Down
2 changes: 1 addition & 1 deletion tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ fn batch(core: &mockcore::Handle, ord: &TestServer, batchfile: batch::File) -> E

assert_regex_match!(
buffer,
"Waiting for rune commitment [[:xdigit:]]{64} to mature…\n"
"Waiting for rune .* commitment [[:xdigit:]]{64} to mature…\n"
);

core.mine_blocks(5);
Expand Down
2 changes: 1 addition & 1 deletion tests/wallet/batch_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2664,7 +2664,7 @@ fn batch_inscribe_errors_if_pending_etchings() {

assert_regex_match!(
buffer,
"Waiting for rune commitment [[:xdigit:]]{64} to mature…\n"
"Waiting for rune .* commitment [[:xdigit:]]{64} to mature…\n"
);

core.mine_blocks(1);
Expand Down
44 changes: 7 additions & 37 deletions tests/wallet/resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn wallet_resume() {

assert_regex_match!(
buffer,
"Waiting for rune commitment [[:xdigit:]]{64} to mature…\n"
"Waiting for rune AAAAAAAAAAAAA commitment [[:xdigit:]]{64} to mature…\n"
);

core.mine_blocks(1);
Expand All @@ -83,23 +83,11 @@ fn wallet_resume() {

core.mine_blocks(6);

let mut spawn = CommandBuilder::new("--regtest --index-runes wallet resume")
let output = CommandBuilder::new("--regtest --index-runes wallet resume")
.temp_dir(tempdir)
.core(&core)
.ord(&ord)
.spawn();

let mut buffer = String::new();
let mut reader = BufReader::new(spawn.child.stderr.as_mut().unwrap());

reader.read_line(&mut buffer).unwrap();

assert_regex_match!(
buffer,
"Waiting for rune commitment [[:xdigit:]]{64} to mature…\n"
);

let output = spawn.run_and_deserialize_output::<ord::subcommand::wallet::resume::ResumeOutput>();
.run_and_deserialize_output::<ord::subcommand::wallet::resume::ResumeOutput>();

assert_eq!(
output
Expand Down Expand Up @@ -167,7 +155,7 @@ fn resume_suspended() {

assert_regex_match!(
buffer,
"Waiting for rune commitment [[:xdigit:]]{64} to mature…\n"
"Waiting for rune AAAAAAAAAAAAA commitment [[:xdigit:]]{64} to mature…\n"
);

core.mine_blocks(1);
Expand Down Expand Up @@ -198,15 +186,7 @@ fn resume_suspended() {
.ord(&ord)
.spawn();

let mut buffer = String::new();

BufReader::new(spawn.child.stderr.as_mut().unwrap())
.read_line(&mut buffer)
.unwrap();

assert_regex_match!(buffer, "Waiting for rune commitment .* to mature…\n");

buffer.clear();
thread::sleep(Duration::from_secs(1));

signal::kill(
Pid::from_raw(spawn.child.id().try_into().unwrap()),
Expand All @@ -223,17 +203,7 @@ fn resume_suspended() {
"Shutting down gracefully. Press <CTRL-C> again to shutdown immediately.\n"
);

buffer.clear();
reader.read_line(&mut buffer).unwrap();

assert_eq!(
buffer,
"Suspending batch. Run `ord wallet resume` to continue.\n"
);

let output = spawn.run_and_deserialize_output::<ord::subcommand::wallet::resume::ResumeOutput>();

assert!(!output.etchings.first().unwrap().reveal_broadcast);
spawn.child.wait().unwrap();
}

#[test]
Expand Down Expand Up @@ -284,7 +254,7 @@ fn commitment_output_is_locked() {

assert_regex_match!(
buffer,
"Waiting for rune commitment [[:xdigit:]]{64} to mature…\n"
"Waiting for rune AAAAAAAAAAAAA commitment [[:xdigit:]]{64} to mature…\n"
);

let commitment = core.mempool()[0].txid();
Expand Down

0 comments on commit 5311e24

Please sign in to comment.