Skip to content

Commit

Permalink
fix: Make bech32 encoding network-aware
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega committed Jan 26, 2022
1 parent 876ac56 commit af3c15b
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 32 deletions.
7 changes: 5 additions & 2 deletions src/mapper/crawl.rs
Expand Up @@ -5,7 +5,7 @@ use pallas::ledger::alonzo::{

use crate::framework::{Error, EventContext, EventData};

use super::{map::ToBech32, EventWriter};
use super::EventWriter;

impl EventWriter {
fn crawl_metadata(&self, metadata: &Metadata) -> Result<(), Error> {
Expand Down Expand Up @@ -81,7 +81,10 @@ impl EventWriter {
self.append(record.into())?;

let child = &self.child_writer(EventContext {
output_address: output.address.try_to_bech32("addr")?.into(),
output_address: self
.bech32_provider
.encode_address(output.address.as_slice())?
.into(),
..EventContext::default()
});

Expand Down
31 changes: 3 additions & 28 deletions src/mapper/map.rs
Expand Up @@ -7,8 +7,6 @@ use pallas::ledger::alonzo::{
};
use pallas::ledger::alonzo::{NetworkId, TransactionBody, TransactionBodyComponent};

use bech32::{self, ToBase32};

use serde_json::{json, Value as JsonValue};

use crate::framework::{
Expand Down Expand Up @@ -39,17 +37,6 @@ impl From<&alonzo::StakeCredential> for StakeCredential {
}
}

pub trait ToBech32 {
fn try_to_bech32(&self, hrp: &str) -> Result<String, Error>;
}

impl ToBech32 for Vec<u8> {
fn try_to_bech32(&self, hrp: &str) -> Result<String, Error> {
let enc = bech32::encode(hrp, self.to_base32(), bech32::Variant::Bech32)?;
Ok(enc)
}
}

fn ip_string_from_bytes(bytes: &[u8]) -> String {
format!("{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3])
}
Expand Down Expand Up @@ -157,8 +144,9 @@ impl EventWriter {
output: &TransactionOutput,
) -> Result<TxOutputRecord, Error> {
Ok(TxOutputRecord {
// TODO: add correct bech32 prefix depending on network
address: output.address.try_to_bech32("addr")?,
address: self
.bech32_provider
.encode_address(output.address.as_slice())?,
amount: get_tx_output_coin_value(&output.amount),
assets: self.collect_asset_records(&output.amount).into(),
})
Expand Down Expand Up @@ -360,16 +348,3 @@ impl EventWriter {
})
}
}

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

#[test]
fn beach32_encodes_ok() {
let bytes = hex::decode("01ec6ad5daee9febbe300c6160a36d4daf0c5266ae2fe8245cbb581390629814d8165fd547b6f3f6f55842a5f042bcb113e8e86627bc071f37").unwrap();
let bech32 = bytes.try_to_bech32("addr").unwrap();

assert_eq!(bech32, "addr1q8kx44w6a607h03sp3skpgmdfkhsc5nx4ch7sfzuhdvp8yrznq2ds9jl64rmdulk74vy9f0sg27tzylgapnz00q8rumsuhj834");
}
}
25 changes: 23 additions & 2 deletions src/mapper/prelude.rs
@@ -1,7 +1,10 @@
use crate::{
framework::{Event, EventContext, EventData},
pipelining::StageSender,
utils::time::{NaiveConfig as TimeConfig, NaiveProvider as NaiveTime, TimeProvider},
utils::{
bech32::{Bech32Config, Bech32Provider},
time::{NaiveConfig as TimeConfig, NaiveProvider as NaiveTime, TimeProvider},
},
};
use merge::Merge;
use serde::Deserialize;
Expand All @@ -20,6 +23,7 @@ pub struct ChainWellKnownInfo {
pub shelley_known_slot: u64,
pub shelley_known_hash: String,
pub shelley_known_time: u64,
pub address_hrp: String,
}

impl ChainWellKnownInfo {
Expand All @@ -31,13 +35,15 @@ impl ChainWellKnownInfo {
shelley_known_hash:
"aa83acbf5904c0edfe4d79b3689d3d00fcfc553cf360fd2229b98d464c28e9de".to_string(),
shelley_known_time: 1596059091,
address_hrp: "addr".to_string(),
}),
TESTNET_MAGIC => Ok(ChainWellKnownInfo {
shelley_slot_length: 1,
shelley_known_slot: 1598400,
shelley_known_hash:
"02b1c561715da9e540411123a6135ee319b02f60b9a11a603d3305556c04329f".to_string(),
shelley_known_time: 1595967616,
address_hrp: "addr_test".to_string(),
}),
_ => Err("can't infer well-known chain infro from specified magic".into()),
}
Expand Down Expand Up @@ -68,6 +74,14 @@ impl TryFrom<ChainWellKnownInfo> for Point {
}
}

impl From<ChainWellKnownInfo> for Bech32Config {
fn from(other: ChainWellKnownInfo) -> Self {
Bech32Config {
address_hrp: other.address_hrp,
}
}
}

#[derive(Deserialize, Clone, Debug, Default)]
pub struct Config {
#[serde(default)]
Expand All @@ -85,6 +99,7 @@ pub(crate) struct EventWriter {
context: EventContext,
output: StageSender,
time_provider: Option<NaiveTime>,
pub(crate) bech32_provider: Bech32Provider,
pub(crate) config: Config,
}

Expand All @@ -97,7 +112,11 @@ impl EventWriter {
EventWriter {
context: EventContext::default(),
output,
time_provider: well_known.map(|x| NaiveTime::new(x.into())),
time_provider: well_known.clone().map(|x| NaiveTime::new(x.into())),
bech32_provider: well_known.map_or_else(
|| Bech32Provider::default(),
|x| Bech32Provider::new(x.into()),
),
config,
}
}
Expand Down Expand Up @@ -126,10 +145,12 @@ impl EventWriter {
pub fn child_writer(&self, mut extra_context: EventContext) -> EventWriter {
extra_context.merge(self.context.clone());

// TODO: too much cloning, lets move to lifecycles here
EventWriter {
context: extra_context,
output: self.output.clone(),
time_provider: self.time_provider.clone(),
bech32_provider: self.bech32_provider.clone(),
config: self.config.clone(),
}
}
Expand Down
65 changes: 65 additions & 0 deletions src/utils/bech32.rs
@@ -0,0 +1,65 @@
//! Bech32 encoding utils
//!
//! Provides artifacts for encoding different values into bech32 format taking
//! into account the context provided via configuration.

use bech32::{self, ToBase32};
use serde::Deserialize;

use crate::Error;

#[derive(Clone, Deserialize)]
pub struct Bech32Config {
pub address_hrp: String,
}

impl Default for Bech32Config {
fn default() -> Self {
Self {
address_hrp: "addr".to_string(),
}
}
}

#[derive(Clone)]
pub struct Bech32Provider(Bech32Config);

impl Bech32Provider {
pub fn new(config: Bech32Config) -> Self {
Bech32Provider(config)
}

pub fn encode_address(&self, data: &[u8]) -> Result<String, Error> {
let enc = bech32::encode(
&self.0.address_hrp,
data.to_base32(),
bech32::Variant::Bech32,
)?;

Ok(enc)
}
}

impl Default for Bech32Provider {
fn default() -> Self {
Self(Default::default())
}
}

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

#[test]
fn beach32_encodes_ok() {
let provider = Bech32Provider::new(Bech32Config {
address_hrp: "addr".to_string(),
});

let bytes = hex::decode("01ec6ad5daee9febbe300c6160a36d4daf0c5266ae2fe8245cbb581390629814d8165fd547b6f3f6f55842a5f042bcb113e8e86627bc071f37").unwrap();

let bech32 = provider.encode_address(bytes.as_slice()).unwrap();

assert_eq!(bech32, "addr1q8kx44w6a607h03sp3skpgmdfkhsc5nx4ch7sfzuhdvp8yrznq2ds9jl64rmdulk74vy9f0sg27tzylgapnz00q8rumsuhj834");
}
}
1 change: 1 addition & 0 deletions src/utils/mod.rs
Expand Up @@ -2,6 +2,7 @@ use crate::framework::Error;

pub mod throttle;

pub(crate) mod bech32;
pub(crate) mod time;

pub(crate) trait SwallowResult {
Expand Down

0 comments on commit af3c15b

Please sign in to comment.