Skip to content

Commit

Permalink
Add teleburn command to generate Ethereum teleburn addresses (ordinal…
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored and lige committed Dec 16, 2023
1 parent b784e9e commit 4b69f19
Show file tree
Hide file tree
Showing 11 changed files with 454 additions and 449 deletions.
768 changes: 321 additions & 447 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ rustls-acme = { version = "0.7.1", features = ["axum"] }
serde = { version = "1.0.137", features = ["derive"] }
serde_json = { version = "1.0.81", features = ["preserve_order"] }
serde_yaml = "0.9.17"
sha3 = "0.10.8"
sysinfo = "0.29.2"
tempfile = "3.2.0"
tokio = { version = "1.17.0", features = ["rt-multi-thread"] }
Expand Down
2 changes: 1 addition & 1 deletion src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use {

pub(crate) use self::entry::RuneEntry;

mod entry;
pub(crate) mod entry;
mod fetcher;
mod reorg;
mod rtx;
Expand Down
2 changes: 1 addition & 1 deletion src/index/entry.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;

pub(super) trait Entry: Sized {
pub(crate) trait Entry: Sized {
type Value;

fn load(value: Self::Value) -> Self;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub mod sat;
mod sat_point;
pub mod subcommand;
mod tally;
mod teleburn;
pub mod templates;
mod wallet;

Expand Down
4 changes: 4 additions & 0 deletions src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod preview;
mod server;
pub mod subsidy;
pub mod supply;
pub mod teleburn;
pub mod traits;
pub mod wallet;

Expand Down Expand Up @@ -38,6 +39,8 @@ pub(crate) enum Subcommand {
Subsidy(subsidy::Subsidy),
#[command(about = "Display Bitcoin supply information")]
Supply,
#[command(about = "Generate teleburn addresses")]
Teleburn(teleburn::Teleburn),
#[command(about = "Display satoshi traits")]
Traits(traits::Traits),
#[command(subcommand, about = "Wallet commands")]
Expand All @@ -63,6 +66,7 @@ impl Subcommand {
}
Self::Subsidy(subsidy) => subsidy.run(),
Self::Supply => supply::run(),
Self::Teleburn(teleburn) => teleburn.run(),
Self::Traits(traits) => traits.run(),
Self::Wallet(wallet) => wallet.run(options),
}
Expand Down
20 changes: 20 additions & 0 deletions src/subcommand/teleburn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use {super::*, crate::teleburn};

#[derive(Debug, Parser)]
pub(crate) struct Teleburn {
#[arg(help = "Generate teleburn addresses for inscription <DESTINATION>.")]
destination: InscriptionId,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Output {
pub ethereum: teleburn::Ethereum,
}

impl Teleburn {
pub(crate) fn run(self) -> SubcommandResult {
Ok(Box::new(Output {
ethereum: self.destination.into(),
}))
}
}
90 changes: 90 additions & 0 deletions src/teleburn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use {super::*, crate::index::entry::Entry, sha3::Digest, sha3::Keccak256};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Ethereum(String);

impl From<InscriptionId> for Ethereum {
fn from(inscription_id: InscriptionId) -> Self {
let digest = bitcoin::hashes::sha256::Hash::hash(&inscription_id.store());
Self(create_address_with_checksum(&hex::encode(&digest[0..20])))
}
}

impl Display for Ethereum {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}

/// Given the hex digits of an Ethereum address, return that address with a
/// checksum as per https://eips.ethereum.org/EIPS/eip-55
fn create_address_with_checksum(address: &str) -> String {
assert_eq!(address.len(), 40);
assert!(address
.chars()
.all(|c| c.is_ascii_hexdigit() && (!c.is_alphabetic() || c.is_lowercase())));

let hash = hex::encode(&Keccak256::digest(address.as_bytes())[..20]);
assert_eq!(hash.len(), 40);

"0x"
.chars()
.chain(address.chars().zip(hash.chars()).map(|(a, h)| match h {
'0'..='7' => a,
'8'..='9' | 'a'..='f' => a.to_ascii_uppercase(),
_ => unreachable!(),
}))
.collect()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_eth_checksum_generation() {
// test addresses from https://eips.ethereum.org/EIPS/eip-55
for addr in &[
"0x27b1fdb04752bbc536007a920d24acb045561c26",
"0x52908400098527886E0F7030069857D2E4169EE7",
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
"0x8617E340B3D01FA5F11F306F4090FD50E238070D",
"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
"0xde709f2102306220921060314715629080e2fb77",
"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
] {
let lowercased = String::from(&addr[2..]).to_ascii_lowercase();
assert_eq!(addr.to_string(), create_address_with_checksum(&lowercased));
}
}

#[test]
fn test_inscription_id_to_teleburn_address() {
for (inscription_id, addr) in &[
(
InscriptionId {
txid: Txid::all_zeros(),
index: 0,
},
"0x6db65fD59fd356F6729140571B5BCd6bB3b83492",
),
(
InscriptionId::from_str(
"6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i7",
)
.unwrap(),
"0xEb26fEFA572a25F0ED7B41C5249bCba2Ca976475",
),
(
InscriptionId::from_str(
"6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0",
)
.unwrap(),
"0xe43A06530BdF8A4e067581f48Fae3b535559dA9e",
),
] {
assert_eq!(*addr, Ethereum::from(*inscription_id).0);
}
}
}
6 changes: 6 additions & 0 deletions src/templates/inscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ mod tests {
<dd><a class=monospace href=/output/1{64}:1>1{64}:1</a></dd>
<dt>offset</dt>
<dd>0</dd>
<dt>ethereum teleburn address</dt>
<dd>0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059</dd>
</dl>
"
.unindent()
Expand Down Expand Up @@ -344,6 +346,8 @@ mod tests {
<dd><a class=monospace href=/output/1{64}:1>1{64}:1</a></dd>
<dt>offset</dt>
<dd>0</dd>
<dt>ethereum teleburn address</dt>
<dd>0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059</dd>
</dl>
"
.unindent()
Expand Down Expand Up @@ -401,6 +405,8 @@ mod tests {
<dd><a class=monospace href=/output/1{64}:1>1{64}:1</a></dd>
<dt>offset</dt>
<dd>0</dd>
<dt>ethereum teleburn address</dt>
<dd>0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059</dd>
<dt>children</dt>
<dd>
<div class=thumbnails>
Expand Down
2 changes: 2 additions & 0 deletions templates/inscription.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ <h1>Inscription {{ self.inscription_number }}</h1>
<dd><a class=monospace href=/output/{{ self.satpoint.outpoint }}>{{ self.satpoint.outpoint }}</a></dd>
<dt>offset</dt>
<dd>{{ self.satpoint.offset }}</dd>
<dt>ethereum teleburn address</dt>
<dd>{{ teleburn::Ethereum::from(self.inscription_id) }}</dd>
%% if !self.children.is_empty() {
<dt>children</dt>
<dd>
Expand Down
7 changes: 7 additions & 0 deletions tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ fn inscription_page() {
..
} = inscribe(&rpc_server);

let ethereum_teleburn_address = CommandBuilder::new(format!("teleburn {inscription}"))
.rpc_server(&rpc_server)
.run_and_deserialize_output::<ord::subcommand::teleburn::Output>()
.ethereum;

TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex(
format!("/inscription/{inscription}"),
format!(
Expand Down Expand Up @@ -83,6 +88,8 @@ fn inscription_page() {
<dd><a class=monospace href=/output/{reveal}:0>{reveal}:0</a></dd>
<dt>offset</dt>
<dd>0</dd>
<dt>ethereum teleburn address</dt>
<dd>{ethereum_teleburn_address}</dd>
</dl>.*",
),
);
Expand Down

0 comments on commit 4b69f19

Please sign in to comment.