Skip to content

Commit

Permalink
Merge pull request #11 from bitfinity-network/fix/append-reveal-script
Browse files Browse the repository at this point in the history
Fix: Append reveal script to builder
  • Loading branch information
kobby-pentangeli authored Mar 18, 2024
2 parents 9fad379 + 1bc88a6 commit 1884eaa
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 36 deletions.
15 changes: 14 additions & 1 deletion src/inscription.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
pub mod brc20;
pub mod nft;

use bitcoin::script::PushBytesBuf;
use bitcoin::script::{Builder as ScriptBuilder, PushBytesBuf};
use serde::de::DeserializeOwned;
use serde::Serialize;

use crate::wallet::RedeemScriptPubkey;
use crate::{OrdError, OrdResult};

/// The `Inscription` trait defines the behavior necessary for handling
Expand All @@ -13,6 +14,18 @@ use crate::{OrdError, OrdResult};
/// These are methods for encoding, decoding, and managing
/// the inscriptions, tailored to specific types (e.g. `Brc20`, `Nft`).
pub trait Inscription: DeserializeOwned {
/// Generates the redeem script from a script pubkey and the inscription.
///
/// # Errors
///
/// May return an `OrdError` if (de)serialization of any of the inscription fields
/// fails while appending the script to the builder.
fn generate_redeem_script(
&self,
builder: ScriptBuilder,
pubkey: RedeemScriptPubkey,
) -> OrdResult<ScriptBuilder>;

/// Encodes the inscription object into a JSON string.
///
/// # Errors
Expand Down
36 changes: 35 additions & 1 deletion src/inscription/brc20.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use std::str::FromStr;

use bitcoin::script::PushBytesBuf;
use bitcoin::opcodes::all::{OP_CHECKSIG, OP_ENDIF, OP_IF};
use bitcoin::opcodes::{OP_0, OP_FALSE};
use bitcoin::script::{Builder as ScriptBuilder, PushBytesBuf};
use serde_with::{serde_as, DisplayFromStr};

use crate::utils::bytes_to_push_bytes;
use crate::wallet::RedeemScriptPubkey;
use crate::{Inscription, InscriptionParseError, OrdError, OrdResult};

const PROTOCOL: &str = "brc-20";
Expand Down Expand Up @@ -52,6 +55,29 @@ impl Brc20 {
amt,
})
}

fn append_reveal_script_to_builder(
&self,
builder: ScriptBuilder,
pubkey: RedeemScriptPubkey,
) -> OrdResult<ScriptBuilder> {
let encoded_pubkey = match pubkey {
RedeemScriptPubkey::Ecdsa(pubkey) => bytes_to_push_bytes(&pubkey.to_bytes())?,
RedeemScriptPubkey::XPublickey(pubkey) => bytes_to_push_bytes(&pubkey.serialize())?,
};

Ok(builder
.push_slice(encoded_pubkey.as_push_bytes())
.push_opcode(OP_CHECKSIG)
.push_opcode(OP_FALSE)
.push_opcode(OP_IF)
.push_slice(b"ord")
.push_slice(b"\x01")
.push_slice(bytes_to_push_bytes(self.content_type().as_bytes())?.as_push_bytes())
.push_opcode(OP_0)
.push_slice(self.data()?.as_push_bytes())
.push_opcode(OP_ENDIF))
}
}

impl FromStr for Brc20 {
Expand All @@ -63,6 +89,14 @@ impl FromStr for Brc20 {
}

impl Inscription for Brc20 {
fn generate_redeem_script(
&self,
builder: ScriptBuilder,
pubkey: RedeemScriptPubkey,
) -> OrdResult<ScriptBuilder> {
self.append_reveal_script_to_builder(builder, pubkey)
}

fn content_type(&self) -> String {
"text/plain;charset=utf-8".to_string()
}
Expand Down
57 changes: 45 additions & 12 deletions src/inscription/nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ mod nft_tests;
use std::io::Cursor;
use std::str::FromStr;

use bitcoin::opcodes;
use bitcoin::opcodes::all::OP_CHECKSIG;
use bitcoin::opcodes::{self};
use bitcoin::script::{Builder as ScriptBuilder, PushBytesBuf, ScriptBuf};
use http::HeaderValue;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use crate::utils::{self, bytes_to_push_bytes, constants};
use crate::wallet::RedeemScriptPubkey;
use crate::{Inscription, InscriptionParseError, OrdError, OrdResult};

/// Represents an arbitrary Ordinal inscription. We're "unofficially" referring to this as an NFT
Expand Down Expand Up @@ -66,6 +68,23 @@ pub struct Nft {
}

impl Inscription for Nft {
fn generate_redeem_script(
&self,
builder: ScriptBuilder,
pubkey: RedeemScriptPubkey,
) -> OrdResult<ScriptBuilder> {
let encoded_pubkey = match pubkey {
RedeemScriptPubkey::Ecdsa(pubkey) => bytes_to_push_bytes(&pubkey.to_bytes())?,
RedeemScriptPubkey::XPublickey(pubkey) => bytes_to_push_bytes(&pubkey.serialize())?,
};

let builder = builder
.push_slice(encoded_pubkey.as_push_bytes())
.push_opcode(OP_CHECKSIG);

self.append_reveal_script_to_builder(builder)
}

fn content_type(&self) -> String {
match self.content_type() {
Some(t) => t.to_string(),
Expand Down Expand Up @@ -115,7 +134,10 @@ impl Nft {
Ok(self.clone())
}

pub fn append_reveal_script_to_builder(&self, mut builder: ScriptBuilder) -> ScriptBuilder {
fn append_reveal_script_to_builder(
&self,
mut builder: ScriptBuilder,
) -> OrdResult<ScriptBuilder> {
builder = builder
.push_opcode(opcodes::OP_FALSE)
.push_opcode(opcodes::all::OP_IF)
Expand All @@ -124,48 +146,48 @@ impl Nft {
if let Some(content_type) = self.content_type.clone() {
builder = builder
.push_slice(constants::CONTENT_TYPE_TAG)
.push_slice(PushBytesBuf::try_from(content_type).unwrap());
.push_slice(PushBytesBuf::try_from(content_type)?);
}

if let Some(content_encoding) = self.content_encoding.clone() {
builder = builder
.push_slice(constants::CONTENT_ENCODING_TAG)
.push_slice(PushBytesBuf::try_from(content_encoding).unwrap());
.push_slice(PushBytesBuf::try_from(content_encoding)?);
}

if let Some(protocol) = self.metaprotocol.clone() {
builder = builder
.push_slice(constants::METAPROTOCOL_TAG)
.push_slice(PushBytesBuf::try_from(protocol).unwrap());
.push_slice(PushBytesBuf::try_from(protocol)?);
}

if let Some(parent) = self.parent.clone() {
builder = builder
.push_slice(constants::PARENT_TAG)
.push_slice(PushBytesBuf::try_from(parent).unwrap());
.push_slice(PushBytesBuf::try_from(parent)?);
}

if let Some(pointer) = self.pointer.clone() {
builder = builder
.push_slice(constants::POINTER_TAG)
.push_slice(PushBytesBuf::try_from(pointer).unwrap());
.push_slice(PushBytesBuf::try_from(pointer)?);
}

if let Some(metadata) = &self.metadata {
for chunk in metadata.chunks(520) {
builder = builder.push_slice(constants::METADATA_TAG);
builder = builder.push_slice(PushBytesBuf::try_from(chunk.to_vec()).unwrap());
builder = builder.push_slice(PushBytesBuf::try_from(chunk.to_vec())?);
}
}

if let Some(body) = &self.body {
builder = builder.push_slice(constants::BODY_TAG);
for chunk in body.chunks(520) {
builder = builder.push_slice(PushBytesBuf::try_from(chunk.to_vec()).unwrap());
builder = builder.push_slice(PushBytesBuf::try_from(chunk.to_vec())?);
}
}

builder.push_opcode(opcodes::all::OP_ENDIF)
Ok(builder.push_opcode(opcodes::all::OP_ENDIF))
}

/// Creates a new `Nft` from JSON-encoded string.
Expand Down Expand Up @@ -219,8 +241,8 @@ impl Nft {
bytes
}

pub fn reveal_script_as_scriptbuf(&self, builder: ScriptBuilder) -> ScriptBuf {
self.append_reveal_script_to_builder(builder).into_script()
pub fn reveal_script_as_scriptbuf(&self, builder: ScriptBuilder) -> OrdResult<ScriptBuf> {
Ok(self.append_reveal_script_to_builder(builder)?.into_script())
}
}

Expand Down Expand Up @@ -279,6 +301,7 @@ mod tests {
assert_eq!(
create_nft("btc", [])
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
7
Expand All @@ -287,6 +310,7 @@ mod tests {
assert_eq!(
create_nft("btc", [0; 1])
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
8
Expand All @@ -295,6 +319,7 @@ mod tests {
assert_eq!(
create_nft("btc", [0; 520])
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
8
Expand All @@ -303,6 +328,7 @@ mod tests {
assert_eq!(
create_nft("btc", [0; 521])
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
9
Expand All @@ -311,6 +337,7 @@ mod tests {
assert_eq!(
create_nft("btc", [0; 1040])
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
9
Expand All @@ -319,6 +346,7 @@ mod tests {
assert_eq!(
create_nft("btc", [0; 1041])
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
10
Expand All @@ -333,6 +361,7 @@ mod tests {
..Default::default()
}
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
4
Expand All @@ -344,6 +373,7 @@ mod tests {
..Default::default()
}
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
4
Expand All @@ -355,6 +385,7 @@ mod tests {
..Default::default()
}
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
6
Expand All @@ -366,6 +397,7 @@ mod tests {
..Default::default()
}
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
6
Expand All @@ -377,6 +409,7 @@ mod tests {
..Default::default()
}
.reveal_script_as_scriptbuf(ScriptBuilder::new())
.unwrap()
.instructions()
.count(),
8
Expand Down
2 changes: 1 addition & 1 deletion src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ mod parser;
pub use builder::signer::{ExternalSigner, Wallet, WalletType};
pub use builder::{
CreateCommitTransaction, CreateCommitTransactionArgs, OrdTransactionBuilder,
RevealTransactionArgs, ScriptType, Utxo,
RedeemScriptPubkey, RevealTransactionArgs, ScriptType, Utxo,
};
pub use parser::OrdParser;
25 changes: 4 additions & 21 deletions src/wallet/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ pub mod signer;
mod taproot;

use bitcoin::absolute::LockTime;
use bitcoin::opcodes::all::{OP_CHECKSIG, OP_ENDIF, OP_IF};
use bitcoin::opcodes::{OP_0, OP_FALSE};
use bitcoin::script::Builder as ScriptBuilder;
use bitcoin::transaction::Version;
use bitcoin::{
Expand All @@ -14,7 +12,6 @@ use signer::Wallet;

use super::builder::taproot::{generate_keypair, TaprootPayload};
use crate::inscription::Inscription;
use crate::utils::bytes_to_push_bytes;
use crate::{OrdError, OrdResult};

const POSTAGE: u64 = 333;
Expand Down Expand Up @@ -78,7 +75,7 @@ pub enum ScriptType {
}

#[derive(Debug)]
enum RedeemScriptPubkey {
pub enum RedeemScriptPubkey {
Ecdsa(PublicKey),
XPublickey(XOnlyPublicKey),
}
Expand Down Expand Up @@ -258,7 +255,7 @@ impl OrdTransactionBuilder {
Ok(tx)
}

/// Generate redeem script from private key and inscription
/// Generate redeem script from script pubkey and inscription
fn generate_redeem_script<T>(
&self,
inscription: &T,
Expand All @@ -267,22 +264,8 @@ impl OrdTransactionBuilder {
where
T: Inscription,
{
let encoded_pubkey = match pubkey {
RedeemScriptPubkey::Ecdsa(pubkey) => bytes_to_push_bytes(&pubkey.to_bytes())?,
RedeemScriptPubkey::XPublickey(pubkey) => bytes_to_push_bytes(&pubkey.serialize())?,
};

Ok(ScriptBuilder::new()
.push_slice(encoded_pubkey.as_push_bytes())
.push_opcode(OP_CHECKSIG)
.push_opcode(OP_FALSE)
.push_opcode(OP_IF)
.push_slice(b"ord")
.push_slice(b"\x01")
.push_slice(bytes_to_push_bytes(inscription.content_type().as_bytes())?.as_push_bytes())
.push_opcode(OP_0)
.push_slice(inscription.data()?.as_push_bytes())
.push_opcode(OP_ENDIF)
Ok(inscription
.generate_redeem_script(ScriptBuilder::new(), pubkey)?
.into_script())
}

Expand Down

0 comments on commit 1884eaa

Please sign in to comment.