Skip to content

Commit

Permalink
Merge branch 'ordinals:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
benbuschmann authored May 17, 2024
2 parents 89f1803 + 301f256 commit 5d2ed18
Show file tree
Hide file tree
Showing 19 changed files with 492 additions and 160 deletions.
2 changes: 1 addition & 1 deletion docs/src/guides/teleburning.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The ENS domain name [rodarmor.eth](https://app.ens.domains/rodarmor.eth), was
teleburned to [inscription
zero](https://ordinals.com/inscription/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0).

Running the inscription ID of inscription zero is
The inscription ID of inscription zero is
`6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0`.

Passing `6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0` to
Expand Down
2 changes: 1 addition & 1 deletion docs/src/runes/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ ID `0:0` is used to mean the rune being etched in this transaction, if any.
An edict with `amount` zero allocates all remaining units of rune `id`.

An edict with `output` equal to the number of transaction outputs allocates
`amount` runes to each non-`OP_RETURN` output.
`amount` runes to each non-`OP_RETURN` output in order.

An edict with `amount` zero and `output` equal to the number of transaction
outputs divides all unallocated units of rune `id` between each non-`OP_RETURN`
Expand Down
31 changes: 26 additions & 5 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/runestone_decipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ fuzz_target!(|input: Vec<Vec<u8>>| {
version: 2,
};

Runestone::from_transaction(&tx);
Runestone::decipher(&tx);
});
30 changes: 15 additions & 15 deletions fuzz/fuzz_targets/transaction_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use {
arbitrary::Arbitrary,
bitcoin::{
address::{Address, NetworkUnchecked},
Amount, OutPoint,
Amount, OutPoint, TxOut,
},
libfuzzer_sys::fuzz_target,
ord::{FeeRate, Target, TransactionBuilder},
ord::{FeeRate, InscriptionId, Target, TransactionBuilder},
ordinals::SatPoint,
std::collections::{BTreeMap, BTreeSet},
};
Expand All @@ -16,7 +16,6 @@ use {
struct Input {
output_value: Option<u64>,
fee_rate: f64,
utxos: Vec<u64>,
}

fuzz_target!(|input: Input| {
Expand All @@ -29,23 +28,24 @@ fuzz_target!(|input: Input| {
.unwrap();

let inscription_id = "1111111111111111111111111111111111111111111111111111111111111111i1"
.parse()
.parse::<InscriptionId>()
.unwrap();

let mut inscriptions = BTreeMap::new();
inscriptions.insert(satpoint, inscription_id);
inscriptions.insert(satpoint, vec![inscription_id]);

let address = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse::<Address<NetworkUnchecked>>()
.unwrap()
.assume_checked();
let mut amounts = BTreeMap::new();
amounts.insert(outpoint, Amount::from_sat(1_000_000));

for (i, value) in input.utxos.into_iter().enumerate() {
amounts.insert(
format!("0000000000000000000000000000000000000000000000000000000000000000:{i}",)
.parse()
.unwrap(),
Amount::from_sat(value),
);
}
amounts.insert(
outpoint,
TxOut {
value: 50_000,
script_pubkey: address.script_pubkey(),
},
);

let recipient = "bc1pdqrcrxa8vx6gy75mfdfj84puhxffh4fq46h3gkp6jxdd0vjcsdyspfxcv6"
.parse::<Address<NetworkUnchecked>>()
Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/varint_decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fuzz_target!(|input: &[u8]| {
let mut i = 0;

while i < input.len() {
let Some((decoded, length)) = varint::decode(&input[i..]) else {
let Ok((decoded, length)) = varint::decode(&input[i..]) else {
break;
};
let mut encoded = Vec::new();
Expand Down
9 changes: 6 additions & 3 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ use {
serde_hex::{SerHex, Strict},
};

pub use crate::templates::{
BlocksHtml as Blocks, RuneHtml as Rune, RunesHtml as Runes, StatusHtml as Status,
TransactionHtml as Transaction,
pub use crate::{
subcommand::decode::RawOutput as Decode,
templates::{
BlocksHtml as Blocks, RuneHtml as Rune, RunesHtml as Runes, StatusHtml as Status,
TransactionHtml as Transaction,
},
};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
Expand Down
2 changes: 1 addition & 1 deletion src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1660,7 +1660,7 @@ impl Index {
return Ok(false);
};

if !info.in_active_chain.unwrap_or_default() {
if info.blockhash.is_none() {
return Ok(false);
}

Expand Down
26 changes: 26 additions & 0 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ impl Server {
.route("/static/*path", get(Self::static_asset))
.route("/status", get(Self::status))
.route("/tx/:txid", get(Self::transaction))
.route("/decode/:txid", get(Self::decode))
.route("/update", get(Self::update))
.fallback(Self::fallback)
.layer(Extension(index))
Expand Down Expand Up @@ -914,6 +915,31 @@ impl Server {
})
}

async fn decode(
Extension(index): Extension<Arc<Index>>,
Path(txid): Path<Txid>,
AcceptJson(accept_json): AcceptJson,
) -> ServerResult {
task::block_in_place(|| {
let transaction = index
.get_transaction(txid)?
.ok_or_not_found(|| format!("transaction {txid}"))?;

let inscriptions = ParsedEnvelope::from_transaction(&transaction);
let runestone = Runestone::decipher(&transaction);

Ok(if accept_json {
Json(api::Decode {
inscriptions,
runestone,
})
.into_response()
} else {
StatusCode::NOT_FOUND.into_response()
})
})
}

async fn update(
Extension(settings): Extension<Arc<Settings>>,
Extension(index): Extension<Arc<Index>>,
Expand Down
8 changes: 8 additions & 0 deletions src/subcommand/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ pub mod inscriptions;
mod label;
pub mod mint;
pub mod outputs;
pub mod pending;
pub mod receive;
pub mod restore;
pub mod resume;
pub mod runics;
pub mod sats;
pub mod send;
mod shared_args;
Expand Down Expand Up @@ -61,12 +63,16 @@ pub(crate) enum Subcommand {
Mint(mint::Mint),
#[command(about = "List all unspent outputs in wallet")]
Outputs,
#[command(about = "List pending etchings")]
Pending(pending::Pending),
#[command(about = "Generate receive address")]
Receive(receive::Receive),
#[command(about = "Restore wallet")]
Restore(restore::Restore),
#[command(about = "Resume pending etchings")]
Resume(resume::Resume),
#[command(about = "List unspent runic outputs in wallet")]
Runics,
#[command(about = "List wallet satoshis")]
Sats(sats::Sats),
#[command(about = "Send sat or inscription")]
Expand Down Expand Up @@ -108,8 +114,10 @@ impl WalletCommand {
Subcommand::Label => label::run(wallet),
Subcommand::Mint(mint) => mint.run(wallet),
Subcommand::Outputs => outputs::run(wallet),
Subcommand::Pending(pending) => pending.run(wallet),
Subcommand::Receive(receive) => receive.run(wallet),
Subcommand::Resume(resume) => resume.run(wallet),
Subcommand::Runics => runics::run(wallet),
Subcommand::Sats(sats) => sats.run(wallet),
Subcommand::Send(send) => send.run(wallet),
Subcommand::Transactions(transactions) => transactions.run(wallet),
Expand Down
2 changes: 1 addition & 1 deletion src/subcommand/wallet/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl Mint {
};

ensure!(
destination.script_pubkey().dust_value() < postage,
destination.script_pubkey().dust_value() <= postage,
"postage below dust limit of {}sat",
destination.script_pubkey().dust_value().to_sat()
);
Expand Down
28 changes: 28 additions & 0 deletions src/subcommand/wallet/pending.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use super::*;

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct PendingOutput {
pub commit: Txid,
pub rune: SpacedRune,
}
#[derive(Debug, Parser)]
pub(crate) struct Pending {}

impl Pending {
pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult {
let etchings = wallet
.pending_etchings()?
.into_iter()
.map(|(_, entry)| {
let spaced_rune = entry.output.rune.unwrap().rune;

PendingOutput {
rune: spaced_rune,
commit: entry.commit.txid(),
}
})
.collect::<Vec<PendingOutput>>();

Ok(Some(Box::new(etchings) as Box<dyn Output>))
}
}
19 changes: 18 additions & 1 deletion src/subcommand/wallet/resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub struct ResumeOutput {
pub(crate) struct Resume {
#[arg(long, help = "Don't broadcast transactions.")]
pub(crate) dry_run: bool,
#[arg(long, help = "Pending <RUNE> etching to resume.")]
pub(crate) rune: Option<SpacedRune>,
}

impl Resume {
Expand All @@ -18,7 +20,22 @@ impl Resume {
break;
}

for (rune, entry) in wallet.pending_etchings()? {
let spaced_rune = self.rune;

let pending_etchings = if let Some(spaced_rune) = spaced_rune {
let pending_etching = wallet.load_etching(spaced_rune.rune)?;

ensure!(
pending_etching.is_some(),
"rune {spaced_rune} does not correspond to any pending etching."
);

vec![(spaced_rune.rune, pending_etching.unwrap())]
} else {
wallet.pending_etchings()?
};

for (rune, entry) in pending_etchings {
if self.dry_run {
etchings.push(batch::Output {
reveal_broadcast: false,
Expand Down
44 changes: 44 additions & 0 deletions src/subcommand/wallet/runics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use super::*;

#[derive(Serialize, Deserialize)]
pub struct RunicUtxo {
pub output: OutPoint,
pub runes: BTreeMap<SpacedRune, Decimal>,
}

pub(crate) fn run(wallet: Wallet) -> SubcommandResult {
let unspent_outputs = wallet.utxos();
let runic_utxos = wallet.get_runic_outputs()?;

let runic_utxos = unspent_outputs
.iter()
.filter_map(|(output, _)| {
if runic_utxos.contains(output) {
let rune_balances = wallet.get_runes_balances_in_output(output).ok()?;
let mut runes = BTreeMap::new();

for (spaced_rune, pile) in rune_balances {
runes
.entry(spaced_rune)
.and_modify(|decimal: &mut Decimal| {
assert_eq!(decimal.scale, pile.divisibility);
decimal.value += pile.amount;
})
.or_insert(Decimal {
value: pile.amount,
scale: pile.divisibility,
});
}

Some(RunicUtxo {
output: *output,
runes,
})
} else {
None
}
})
.collect::<Vec<RunicUtxo>>();

Ok(Some(Box::new(runic_utxos)))
}
Loading

0 comments on commit 5d2ed18

Please sign in to comment.