Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow reinscribing with wallet #2432

Merged
merged 11 commits into from
Sep 11, 2023
1 change: 1 addition & 0 deletions src/subcommand/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl Preview {
satpoint: None,
dry_run: false,
no_limit: false,
reinscribe: false,
destination: None,
parent: None,
postage: Some(TransactionBuilder::TARGET_POSTAGE),
Expand Down
170 changes: 167 additions & 3 deletions src/subcommand/wallet/inscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub(crate) struct Inscribe {
pub(crate) postage: Option<Amount>,
#[clap(long, help = "Make inscription a child of <PARENT>.")]
pub(crate) parent: Option<InscriptionId>,
#[clap(long, help = "Allow reinscription.")]
pub(crate) reinscribe: bool,
}

impl Inscribe {
Expand Down Expand Up @@ -127,6 +129,7 @@ impl Inscribe {
self.commit_fee_rate.unwrap_or(self.fee_rate),
self.fee_rate,
self.no_limit,
self.reinscribe,
match self.postage {
Some(postage) => postage,
_ => TransactionBuilder::TARGET_POSTAGE,
Expand Down Expand Up @@ -209,6 +212,7 @@ impl Inscribe {
commit_fee_rate: FeeRate,
reveal_fee_rate: FeeRate,
no_limit: bool,
reinscribe: bool,
postage: Amount,
) -> Result<(Transaction, Transaction, TweakedKeyPair, u64)> {
let satpoint = if let Some(satpoint) = satpoint {
Expand All @@ -229,9 +233,29 @@ impl Inscribe {
.ok_or_else(|| anyhow!("wallet contains no cardinal utxos"))?
};

// should work with:
// 1. passing a satpoint :check:
// 2. passing an inscription id?
//
// should fail if:
// 1. not a reinscription :check:
//
// questions:
// - What if satpoint not inscribed but utxo is? Should it split utxo? -> no
// - What if inscription not on first sat of utxo?
// - What if satpoint not on first sat of utxo?
// -

for (inscribed_satpoint, inscription_id) in &inscriptions {
if inscribed_satpoint == &satpoint {
return Err(anyhow!("sat at {} already inscribed", satpoint));
match (inscribed_satpoint == &satpoint, reinscribe) {
(true, true) => break,
(true, false) => return Err(anyhow!("sat at {} already inscribed", satpoint)),
(false, true) => {
return Err(anyhow!(
"reinscribe flag set but this would not be a reinscription"
))
}
_ => (),
}

if inscribed_satpoint.outpoint == satpoint.outpoint {
Expand Down Expand Up @@ -527,6 +551,7 @@ mod tests {
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap();
Expand All @@ -542,7 +567,7 @@ mod tests {
}

#[test]
fn inscript_tansactions_opt_in_to_rbf() {
fn inscribe_tansactions_opt_in_to_rbf() {
let utxos = vec![(outpoint(1), Amount::from_sat(20000))];
let inscription = inscription("text/plain", "ord");
let commit_address = change(0);
Expand All @@ -560,6 +585,7 @@ mod tests {
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap();
Expand Down Expand Up @@ -597,6 +623,7 @@ mod tests {
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap_err()
Expand Down Expand Up @@ -641,6 +668,7 @@ mod tests {
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.is_ok())
Expand Down Expand Up @@ -679,6 +707,7 @@ mod tests {
FeeRate::try_from(fee_rate).unwrap(),
FeeRate::try_from(fee_rate).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap();
Expand Down Expand Up @@ -750,6 +779,7 @@ mod tests {
FeeRate::try_from(fee_rate).unwrap(),
FeeRate::try_from(fee_rate).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap();
Expand Down Expand Up @@ -825,6 +855,7 @@ mod tests {
FeeRate::try_from(commit_fee_rate).unwrap(),
FeeRate::try_from(fee_rate).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap();
Expand Down Expand Up @@ -876,6 +907,7 @@ mod tests {
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap_err()
Expand Down Expand Up @@ -909,10 +941,142 @@ mod tests {
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
true,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap();

assert!(reveal_tx.size() >= MAX_STANDARD_TX_WEIGHT as usize);
}

#[test]
fn reinscribe_without_setting_flag_fails() {
let utxos = vec![
(outpoint(1), Amount::from_sat(20_000)),
(outpoint(2), Amount::from_sat(20_000)),
];
let mut inscriptions = BTreeMap::new();
inscriptions.insert(
SatPoint {
outpoint: outpoint(1),
offset: 0,
},
inscription_id(1),
);

let inscription = inscription("text/plain", "ord");
let satpoint = Some(SatPoint {
outpoint: outpoint(1),
offset: 0,
});
let commit_address = change(0);
let reveal_address = recipient();

let error = Inscribe::create_inscription_transactions(
satpoint,
None,
inscription,
inscriptions,
Network::Bitcoin,
utxos.into_iter().collect(),
[commit_address, change(1)],
reveal_address,
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
false,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap_err()
.to_string();

assert!(error.contains("sat at 1111111111111111111111111111111111111111111111111111111111111111:1:0 already inscribed"));
}

#[test]
fn reinscribe_with_setting_flag_works() {
let utxos = vec![
(outpoint(1), Amount::from_sat(20_000)),
(outpoint(2), Amount::from_sat(20_000)),
];
let mut inscriptions = BTreeMap::new();
inscriptions.insert(
SatPoint {
outpoint: outpoint(1),
offset: 0,
},
inscription_id(1),
);

let inscription = inscription("text/plain", "ord");
let satpoint = Some(SatPoint {
outpoint: outpoint(1),
offset: 0,
});
let commit_address = change(0);
let reveal_address = recipient();

let (_commit_tx, reveal_tx, _, _) = Inscribe::create_inscription_transactions(
satpoint,
None,
inscription.clone(),
inscriptions,
Network::Bitcoin,
utxos.into_iter().collect(),
[commit_address, change(1)],
reveal_address,
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
true,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap();

pretty_assert_eq!(
Inscription::from_transaction(&reveal_tx)[0].inscription,
inscription
);
}

#[test]
fn setting_reinscribe_flag_but_not_actually_reinscribing_fails() {
let utxos = vec![
(outpoint(1), Amount::from_sat(20_000)),
(outpoint(2), Amount::from_sat(20_000)),
];
let mut inscriptions = BTreeMap::new();
inscriptions.insert(
SatPoint {
outpoint: outpoint(1),
offset: 0,
},
inscription_id(1),
);

let inscription = inscription("text/plain", "ord");
let satpoint = None;
let commit_address = change(0);
let reveal_address = recipient();

let error = Inscribe::create_inscription_transactions(
satpoint,
None,
inscription.clone(),
inscriptions,
Network::Bitcoin,
utxos.into_iter().collect(),
[commit_address, change(1)],
reveal_address,
FeeRate::try_from(1.0).unwrap(),
FeeRate::try_from(1.0).unwrap(),
false,
true,
TransactionBuilder::TARGET_POSTAGE,
)
.unwrap_err()
.to_string();

assert!(error.contains("reinscribe flag set but this would not be a reinscription"));
}
}
102 changes: 102 additions & 0 deletions tests/wallet/inscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,3 +532,105 @@ fn inscribe_with_parent_inscription_and_fee_rate() {
),
);
}

#[test]
fn reinscribe_with_flag() {
let rpc_server = test_bitcoincore_rpc::spawn();
rpc_server.mine_blocks(1);

assert_eq!(rpc_server.descriptors().len(), 0);

create_wallet(&rpc_server);

let inscribe = CommandBuilder::new("wallet inscribe tulip.png --fee-rate 5.0 ")
.write("tulip.png", [1; 520])
.rpc_server(&rpc_server)
.run_and_deserialize_output::<Inscribe>();

assert_eq!(rpc_server.descriptors().len(), 3);

let txid = rpc_server.mine_blocks(1)[0].txdata[2].txid();

let ord_server = TestServer::spawn_with_args(&rpc_server, &[]);
let request = ord_server.request(format!("/content/{}", inscribe.inscription));

assert_eq!(request.status(), 200);

let reinscribe = CommandBuilder::new(format!(
"wallet inscribe orchid.png --fee-rate 1.1 --reinscribe --satpoint {txid}:0:0"
))
.write("orchid.png", [1; 520])
.rpc_server(&rpc_server)
.run_and_deserialize_output::<Inscribe>();

rpc_server.mine_blocks(1);

let ord_server = TestServer::spawn_with_args(&rpc_server, &["--index-sats"]);
let request = ord_server.request(format!("/content/{}", reinscribe.inscription));

assert_eq!(request.status(), 200);
ord_server.assert_response_regex(
format!("/sat/{}", 50 * COIN_VALUE),
format!(
".*<dt>inscriptions</dt>.*<a href=/inscription/{}>.*<a href=/inscription/{}>.*",
inscribe.inscription, reinscribe.inscription
),
);
}

#[test]
fn with_reinscribe_flag_but_not_actually_a_reinscription() {
let rpc_server = test_bitcoincore_rpc::spawn();
rpc_server.mine_blocks(1);

assert_eq!(rpc_server.descriptors().len(), 0);

create_wallet(&rpc_server);

CommandBuilder::new("wallet inscribe tulip.png --fee-rate 5.0 ")
.write("tulip.png", [1; 520])
.rpc_server(&rpc_server)
.run_and_deserialize_output::<Inscribe>();

let txid = rpc_server.mine_blocks(1)[0].txdata[2].txid();

CommandBuilder::new(format!(
"wallet inscribe orchid.png --fee-rate 1.1 --reinscribe --satpoint {txid}:0:1000"
))
.write("orchid.png", [1; 520])
.rpc_server(&rpc_server)
.expected_exit_code(1)
.stderr_regex("error: reinscribe flag set but this would not be a reinscription.*")
.run_and_extract_stdout();
}

#[test]
fn try_reinscribe_without_flag() {
let rpc_server = test_bitcoincore_rpc::spawn();
rpc_server.mine_blocks(1);

assert_eq!(rpc_server.descriptors().len(), 0);

create_wallet(&rpc_server);

let reveal_txid = CommandBuilder::new("wallet inscribe tulip.png --fee-rate 5.0 ")
.write("tulip.png", [1; 520])
.rpc_server(&rpc_server)
.run_and_deserialize_output::<Inscribe>()
.reveal;

assert_eq!(rpc_server.descriptors().len(), 3);

rpc_server.mine_blocks(1);

CommandBuilder::new(format!(
"wallet inscribe orchid.png --fee-rate 1.1 --satpoint {reveal_txid}:0:0"
))
.write("orchid.png", [1; 520])
.rpc_server(&rpc_server)
.expected_exit_code(1)
.stderr_regex(format!(
"error: sat at {reveal_txid}:0:0 already inscribed.*"
))
.run_and_extract_stdout();
}