Skip to content

Commit f856e38

Browse files
authored
Merge pull request #241 from input-output-hk/whankinsiv/setup-address-state
feat: address_state module
2 parents de1b541 + 2a334bd commit f856e38

File tree

27 files changed

+1945
-94
lines changed

27 files changed

+1945
-94
lines changed

Cargo.lock

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ bigdecimal = "0.4.8"
2020
bitmask-enum = "2.2"
2121
bs58 = "0.5"
2222
chrono = { workspace = true }
23+
crc = "3"
2324
gcd = "2.3"
2425
fraction = "0.15"
2526
hex = { workspace = true }

common/src/address.rs

Lines changed: 226 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,81 @@
44
use crate::cip19::{VarIntDecoder, VarIntEncoder};
55
use crate::types::{KeyHash, ScriptHash};
66
use anyhow::{anyhow, bail, Result};
7+
use crc::{Crc, CRC_32_ISO_HDLC};
8+
use minicbor::data::IanaTag;
79
use serde_with::{hex::Hex, serde_as};
810

911
/// a Byron-era address
10-
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
12+
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1113
pub struct ByronAddress {
1214
/// Raw payload
1315
pub payload: Vec<u8>,
1416
}
1517

18+
impl ByronAddress {
19+
fn compute_crc32(&self) -> u32 {
20+
const CRC32: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
21+
CRC32.checksum(&self.payload)
22+
}
23+
24+
pub fn to_string(&self) -> Result<String> {
25+
let crc = self.compute_crc32();
26+
27+
let mut buf = Vec::new();
28+
{
29+
let mut enc = minicbor::Encoder::new(&mut buf);
30+
enc.array(2)?;
31+
enc.tag(IanaTag::Cbor)?;
32+
enc.bytes(&self.payload)?;
33+
enc.u32(crc)?;
34+
}
35+
36+
Ok(bs58::encode(buf).into_string())
37+
}
38+
39+
pub fn from_string(s: &str) -> Result<Self> {
40+
let bytes = bs58::decode(s).into_vec()?;
41+
let mut dec = minicbor::Decoder::new(&bytes);
42+
43+
let len = dec.array()?.unwrap_or(0);
44+
if len != 2 {
45+
anyhow::bail!("Invalid Byron address CBOR array length");
46+
}
47+
48+
let tag = dec.tag()?;
49+
if tag != IanaTag::Cbor.into() {
50+
anyhow::bail!("Invalid Byron address CBOR tag, expected 24");
51+
}
52+
53+
let payload = dec.bytes()?.to_vec();
54+
let crc = dec.u32()?;
55+
56+
let address = ByronAddress { payload };
57+
let computed = address.compute_crc32();
58+
59+
if crc != computed {
60+
anyhow::bail!("Byron address CRC mismatch");
61+
}
62+
63+
Ok(address)
64+
}
65+
66+
pub fn to_bytes_key(&self) -> Result<Vec<u8>> {
67+
let crc = self.compute_crc32();
68+
69+
let mut buf = Vec::new();
70+
{
71+
let mut enc = minicbor::Encoder::new(&mut buf);
72+
enc.array(2)?;
73+
enc.tag(minicbor::data::IanaTag::Cbor)?;
74+
enc.bytes(&self.payload)?;
75+
enc.u32(crc)?;
76+
}
77+
78+
Ok(buf)
79+
}
80+
}
81+
1682
/// Address network identifier
1783
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1884
pub enum AddressNetwork {
@@ -170,11 +236,85 @@ impl ShelleyAddress {
170236
data.extend(delegation_hash);
171237
Ok(bech32::encode::<bech32::Bech32>(hrp, &data)?)
172238
}
239+
240+
pub fn to_bytes_key(&self) -> Result<Vec<u8>> {
241+
let network_bits = match self.network {
242+
AddressNetwork::Main => 1u8,
243+
AddressNetwork::Test => 0u8,
244+
};
245+
246+
let (payment_hash, payment_bits): (&Vec<u8>, u8) = match &self.payment {
247+
ShelleyAddressPaymentPart::PaymentKeyHash(data) => (data, 0),
248+
ShelleyAddressPaymentPart::ScriptHash(data) => (data, 1),
249+
};
250+
251+
let mut data = Vec::new();
252+
253+
match &self.delegation {
254+
ShelleyAddressDelegationPart::None => {
255+
let header = network_bits | (payment_bits << 4) | (3 << 5);
256+
data.push(header);
257+
data.extend(payment_hash);
258+
}
259+
ShelleyAddressDelegationPart::StakeKeyHash(hash) => {
260+
let header = network_bits | (payment_bits << 4) | (0 << 5);
261+
data.push(header);
262+
data.extend(payment_hash);
263+
data.extend(hash);
264+
}
265+
ShelleyAddressDelegationPart::ScriptHash(hash) => {
266+
let header = network_bits | (payment_bits << 4) | (1 << 5);
267+
data.push(header);
268+
data.extend(payment_hash);
269+
data.extend(hash);
270+
}
271+
ShelleyAddressDelegationPart::Pointer(pointer) => {
272+
let header = network_bits | (payment_bits << 4) | (2 << 5);
273+
data.push(header);
274+
data.extend(payment_hash);
275+
276+
let mut encoder = VarIntEncoder::new();
277+
encoder.push(pointer.slot);
278+
encoder.push(pointer.tx_index);
279+
encoder.push(pointer.cert_index);
280+
data.extend(encoder.to_vec());
281+
}
282+
}
283+
284+
Ok(data)
285+
}
286+
287+
pub fn stake_address_string(&self) -> Result<Option<String>> {
288+
let network_bit = match self.network {
289+
AddressNetwork::Main => 1,
290+
AddressNetwork::Test => 0,
291+
};
292+
293+
match &self.delegation {
294+
ShelleyAddressDelegationPart::StakeKeyHash(key_hash) => {
295+
let mut data = Vec::with_capacity(29);
296+
data.push(network_bit | (0b1110 << 4));
297+
data.extend_from_slice(key_hash);
298+
let stake = StakeAddress::from_binary(&data)?.to_string()?;
299+
Ok(Some(stake))
300+
}
301+
ShelleyAddressDelegationPart::ScriptHash(script_hash) => {
302+
let mut data = Vec::with_capacity(29);
303+
data.push(network_bit | (0b1111 << 4));
304+
data.extend_from_slice(script_hash);
305+
let stake = StakeAddress::from_binary(&data)?.to_string()?;
306+
Ok(Some(stake))
307+
}
308+
// TODO: Use chain store to resolve pointer delegation addresses
309+
ShelleyAddressDelegationPart::Pointer(_pointer) => Ok(None),
310+
ShelleyAddressDelegationPart::None => Ok(None),
311+
}
312+
}
173313
}
174314

175315
/// Payload of a stake address
176316
#[serde_as]
177-
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
317+
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
178318
pub enum StakeAddressPayload {
179319
/// Stake key
180320
StakeKeyHash(#[serde_as(as = "Hex")] Vec<u8>),
@@ -196,7 +336,7 @@ impl StakeAddressPayload {
196336
}
197337

198338
/// A stake address
199-
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
339+
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
200340
pub struct StakeAddress {
201341
/// Network id
202342
pub network: AddressNetwork,
@@ -271,10 +411,28 @@ impl StakeAddress {
271411
data.extend(stake_hash);
272412
Ok(bech32::encode::<bech32::Bech32>(hrp, &data)?)
273413
}
414+
415+
pub fn to_bytes_key(&self) -> Result<Vec<u8>> {
416+
let mut out = Vec::new();
417+
let (bits, hash): (u8, &[u8]) = match &self.payload {
418+
StakeAddressPayload::StakeKeyHash(h) => (0b1110, h),
419+
StakeAddressPayload::ScriptHash(h) => (0b1111, h),
420+
};
421+
422+
let net_bit = match self.network {
423+
AddressNetwork::Main => 1,
424+
AddressNetwork::Test => 0,
425+
};
426+
427+
let header = net_bit | (bits << 4);
428+
out.push(header);
429+
out.extend_from_slice(hash);
430+
Ok(out)
431+
}
274432
}
275433

276434
/// A Cardano address
277-
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
435+
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
278436
pub enum Address {
279437
None,
280438
Byron(ByronAddress),
@@ -306,10 +464,9 @@ impl Address {
306464
} else if text.starts_with("stake1") || text.starts_with("stake_test1") {
307465
Ok(Self::Stake(StakeAddress::from_string(text)?))
308466
} else {
309-
if let Ok(bytes) = bs58::decode(text).into_vec() {
310-
Ok(Self::Byron(ByronAddress { payload: bytes }))
311-
} else {
312-
Ok(Self::None)
467+
match ByronAddress::from_string(text) {
468+
Ok(byron) => Ok(Self::Byron(byron)),
469+
Err(_) => Ok(Self::None),
313470
}
314471
}
315472
}
@@ -318,11 +475,46 @@ impl Address {
318475
pub fn to_string(&self) -> Result<String> {
319476
match self {
320477
Self::None => Err(anyhow!("No address")),
321-
Self::Byron(byron) => Ok(bs58::encode(&byron.payload).into_string()),
478+
Self::Byron(byron) => byron.to_string(),
322479
Self::Shelley(shelley) => shelley.to_string(),
323480
Self::Stake(stake) => stake.to_string(),
324481
}
325482
}
483+
484+
pub fn to_bytes_key(&self) -> Result<Vec<u8>> {
485+
match self {
486+
Address::Byron(b) => b.to_bytes_key(),
487+
488+
Address::Shelley(s) => s.to_bytes_key(),
489+
490+
Address::Stake(stake) => stake.to_bytes_key(),
491+
492+
Address::None => Err(anyhow!("No address to convert")),
493+
}
494+
}
495+
496+
pub fn kind(&self) -> &'static str {
497+
match self {
498+
Address::Byron(_) => "byron",
499+
Address::Shelley(_) => "shelley",
500+
Address::Stake(_) => "stake",
501+
Address::None => "none",
502+
}
503+
}
504+
505+
pub fn is_script(&self) -> bool {
506+
match self {
507+
Address::Shelley(shelley) => match shelley.payment {
508+
ShelleyAddressPaymentPart::PaymentKeyHash(_) => false,
509+
ShelleyAddressPaymentPart::ScriptHash(_) => true,
510+
},
511+
Address::Stake(stake) => match stake.payload {
512+
StakeAddressPayload::StakeKeyHash(_) => false,
513+
StakeAddressPayload::ScriptHash(_) => true,
514+
},
515+
Address::Byron(_) | Address::None => false,
516+
}
517+
}
326518
}
327519

328520
// -- Tests --
@@ -336,7 +528,7 @@ mod tests {
336528
let payload = vec![42];
337529
let address = Address::Byron(ByronAddress { payload });
338530
let text = address.to_string().unwrap();
339-
assert_eq!(text, "j");
531+
assert_eq!(text, "8MMy4x9jE734Gz");
340532

341533
let unpacked = Address::from_string(&text).unwrap();
342534
assert_eq!(address, unpacked);
@@ -546,6 +738,30 @@ mod tests {
546738
assert_eq!(address, unpacked);
547739
}
548740

741+
#[test]
742+
fn shelley_to_stake_address_string_mainnet() {
743+
let normal_address = ShelleyAddress::from_string("addr1q82peck5fynytkgjsp9vnpul59zswsd4jqnzafd0mfzykma625r684xsx574ltpznecr9cnc7n9e2hfq9lyart3h5hpszffds5").expect("valid normal address");
744+
let script_address = ShelleyAddress::from_string("addr1zx0whlxaw4ksygvuljw8jxqlw906tlql06ern0gtvvzhh0c6409492020k6xml8uvwn34wrexagjh5fsk5xk96jyxk2qhlj6gf").expect("valid script address");
745+
746+
let normal_stake_address = normal_address
747+
.stake_address_string()
748+
.expect("stake_address_string should not fail")
749+
.expect("normal address should have stake credential");
750+
let script_stake_address = script_address
751+
.stake_address_string()
752+
.expect("stake_address_string should not fail")
753+
.expect("script address should have stake credential");
754+
755+
assert_eq!(
756+
normal_stake_address,
757+
"stake1uxa92par6ngr202l4s3fuupjufu0fju4t5szljw34cm6tscq40449"
758+
);
759+
assert_eq!(
760+
script_stake_address,
761+
"stake1uyd2hj6j4848mdrdln7x8fc6hpunw5ft6yct2rtzafzrt9qh0m28h"
762+
);
763+
}
764+
549765
#[test]
550766
fn stake_address_from_binary_mainnet_stake() {
551767
// First withdrawal on Mainnet

common/src/messages.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::ledger_state::SPOState;
88
use crate::protocol_params::{NonceHash, ProtocolParams};
99
use crate::queries::parameters::{ParametersStateQuery, ParametersStateQueryResponse};
1010
use crate::queries::spdd::{SPDDStateQuery, SPDDStateQueryResponse};
11+
use crate::queries::utxos::{UTxOStateQuery, UTxOStateQueryResponse};
1112
use crate::queries::{
1213
accounts::{AccountsStateQuery, AccountsStateQueryResponse},
1314
addresses::{AddressStateQuery, AddressStateQueryResponse},
@@ -394,10 +395,11 @@ pub enum StateQuery {
394395
Mempool(MempoolStateQuery),
395396
Metadata(MetadataStateQuery),
396397
Network(NetworkStateQuery),
398+
Parameters(ParametersStateQuery),
397399
Pools(PoolsStateQuery),
398400
Scripts(ScriptsStateQuery),
399401
Transactions(TransactionsStateQuery),
400-
Parameters(ParametersStateQuery),
402+
UTxOs(UTxOStateQuery),
401403
SPDD(SPDDStateQuery),
402404
}
403405

@@ -413,9 +415,10 @@ pub enum StateQueryResponse {
413415
Mempool(MempoolStateQueryResponse),
414416
Metadata(MetadataStateQueryResponse),
415417
Network(NetworkStateQueryResponse),
418+
Parameters(ParametersStateQueryResponse),
416419
Pools(PoolsStateQueryResponse),
417420
Scripts(ScriptsStateQueryResponse),
418421
Transactions(TransactionsStateQueryResponse),
419-
Parameters(ParametersStateQueryResponse),
422+
UTxOs(UTxOStateQueryResponse),
420423
SPDD(SPDDStateQueryResponse),
421424
}

0 commit comments

Comments
 (0)