Skip to content

Commit 281bc8c

Browse files
committed
fix pointer address decoder to follow cardano ledger logic
1 parent 3ce8632 commit 281bc8c

File tree

7 files changed

+341
-153
lines changed

7 files changed

+341
-153
lines changed

rust/src/protocol_types/address.rs

Lines changed: 2 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,6 @@ use crate::*;
33
use bech32::ToBase32;
44
use ed25519_bip32::XPub;
55

6-
// returns (Number represented, bytes read) if valid encoding
7-
// or None if decoding prematurely finished
8-
pub(crate) fn variable_nat_decode(bytes: &[u8], fallback_to_max: bool) -> Option<(u64, usize)> {
9-
let mut output = 0u128;
10-
let mut bytes_read = 0;
11-
for byte in bytes {
12-
if output < u64::MAX.into() || !fallback_to_max {
13-
output = (output << 7) | (byte & 0x7F) as u128;
14-
}
15-
//Since Conway era forbids pointer addresses, technically such large numbers would not make sense on mainnet
16-
if output > u64::MAX.into(){
17-
if !fallback_to_max {
18-
return None;
19-
}
20-
output = u64::MAX.into();
21-
}
22-
bytes_read += 1;
23-
if (byte & 0x80) == 0 {
24-
return Some((output as u64, bytes_read));
25-
}
26-
}
27-
None
28-
}
29-
30-
pub(crate) fn variable_nat_encode(mut num: u64) -> Vec<u8> {
31-
let mut output = vec![num as u8 & 0x7F];
32-
num /= 128;
33-
while num > 0 {
34-
output.push((num & 0x7F) as u8 | 0x80);
35-
num /= 128;
36-
}
37-
output.reverse();
38-
output
39-
}
40-
416
#[wasm_bindgen]
427
#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
438
pub enum AddressKind {
@@ -375,9 +340,7 @@ impl Address {
375340
0b0100_0000 | ((ptr.payment.kind() as u8) << 4) | (ptr.network & 0xF);
376341
buf.push(header);
377342
buf.extend(ptr.payment.to_raw_bytes());
378-
buf.extend(variable_nat_encode(ptr.stake.slot.into()));
379-
buf.extend(variable_nat_encode(ptr.stake.tx_index.into()));
380-
buf.extend(variable_nat_encode(ptr.stake.cert_index.into()));
343+
buf.extend(ptr.stake.to_bytes());
381344
}
382345
AddrType::Enterprise(enterprise) => {
383346
let header: u8 = 0b0110_0000
@@ -479,7 +442,7 @@ impl Address {
479442
let mut byte_index = 1;
480443
let payment_cred = read_addr_cred(4, 1);
481444
byte_index += HASH_LEN;
482-
match Self::decode_pointer(&data[byte_index..]) {
445+
match Pointer::from_bytes(&data[byte_index..]) {
483446
Ok((pointer, offset)) => {
484447
byte_index += offset;
485448
if byte_index < data.len() && !ignore_leftover_bytes {
@@ -547,35 +510,6 @@ impl Address {
547510
.map_err(|e| e.annotate("Address"))
548511
}
549512

550-
fn decode_pointer(data: &[u8]) -> Result<(Pointer, usize), DeserializeError> {
551-
let mut offset = 0;
552-
let (slot, slot_bytes) = variable_nat_decode(&data, true).ok_or(DeserializeError::new(
553-
"Address.Pointer.slot",
554-
DeserializeFailure::VariableLenNatDecodeFailed,
555-
))?;
556-
offset += slot_bytes;
557-
let (tx_index, tx_bytes) =
558-
variable_nat_decode(&data[offset..], true).ok_or(DeserializeError::new(
559-
"Address.Pointer.tx_index",
560-
DeserializeFailure::VariableLenNatDecodeFailed,
561-
))?;
562-
offset += tx_bytes;
563-
let (cert_index, cert_bytes) =
564-
variable_nat_decode(&data[offset..], true).ok_or(DeserializeError::new(
565-
"Address.Pointer.cert_index",
566-
DeserializeFailure::VariableLenNatDecodeFailed,
567-
))?;
568-
offset += cert_bytes;
569-
Ok((
570-
Pointer::new_pointer(
571-
&slot.into(),
572-
&tx_index.into(),
573-
&cert_index.into(),
574-
),
575-
offset,
576-
))
577-
}
578-
579513
pub fn to_bech32(&self, prefix: Option<String>) -> Result<String, JsError> {
580514
let final_prefix = match prefix {
581515
Some(prefix) => prefix,
@@ -817,64 +751,6 @@ impl Deserialize for RewardAddress {
817751
}
818752
}
819753

820-
#[wasm_bindgen]
821-
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
822-
pub struct Pointer {
823-
pub(crate) slot: BigNum,
824-
pub(crate) tx_index: BigNum,
825-
pub(crate) cert_index: BigNum,
826-
}
827-
828-
#[wasm_bindgen]
829-
impl Pointer {
830-
/// !!! DEPRECATED !!!
831-
/// This constructor uses outdated slot number format for the ttl value, tx_index and cert_index.
832-
/// Use `.new_pointer` instead
833-
#[deprecated(
834-
since = "10.1.0",
835-
note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use new_pointer instead."
836-
)]
837-
pub fn new(slot: Slot32, tx_index: TransactionIndex, cert_index: CertificateIndex) -> Self {
838-
Self {
839-
slot: slot.into(),
840-
tx_index: tx_index.into(),
841-
cert_index: cert_index.into(),
842-
}
843-
}
844-
845-
pub fn new_pointer(slot: &SlotBigNum, tx_index: &BigNum, cert_index: &BigNum) -> Self {
846-
Self {
847-
slot: slot.clone(),
848-
tx_index: tx_index.clone(),
849-
cert_index: cert_index.clone(),
850-
}
851-
}
852-
853-
pub fn slot(&self) -> Result<u32, JsError> {
854-
self.slot.clone().try_into()
855-
}
856-
857-
pub fn tx_index(&self) -> Result<u32, JsError> {
858-
self.tx_index.clone().try_into()
859-
}
860-
861-
pub fn cert_index(&self) -> Result<u32, JsError> {
862-
self.cert_index.clone().try_into()
863-
}
864-
865-
pub fn slot_bignum(&self) -> BigNum {
866-
self.slot.clone()
867-
}
868-
869-
pub fn tx_index_bignum(&self) -> BigNum {
870-
self.tx_index.clone()
871-
}
872-
873-
pub fn cert_index_bignum(&self) -> BigNum {
874-
self.cert_index.clone()
875-
}
876-
}
877-
878754
#[wasm_bindgen]
879755
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
880756
pub struct PointerAddress {

rust/src/protocol_types/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,6 @@ pub use script_ref::*;
5858

5959
mod block;
6060
pub use block::*;
61+
62+
mod pointer;
63+
pub use pointer::*;

rust/src/protocol_types/numeric/big_num.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ impl From<u32> for BigNum {
159159
}
160160
}
161161

162+
impl From<u16> for BigNum {
163+
fn from(value: u16) -> Self {
164+
return BigNum(value.into());
165+
}
166+
}
167+
162168
impl From<u8> for BigNum {
163169
fn from(value: u8) -> Self {
164170
return BigNum(value.into());

rust/src/protocol_types/pointer.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
use crate::wasm_bindgen;
2+
use crate::BigNum;
3+
use crate::DeserializeError;
4+
use crate::DeserializeFailure;
5+
use crate::JsError;
6+
use crate::{CertificateIndex, Slot32, SlotBigNum, TransactionIndex};
7+
8+
use std::convert::TryInto;
9+
use std::io::{Cursor, Read};
10+
11+
#[wasm_bindgen]
12+
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
13+
pub struct Pointer {
14+
pub(crate) slot: BigNum,
15+
pub(crate) tx_index: BigNum,
16+
pub(crate) cert_index: BigNum,
17+
}
18+
19+
#[wasm_bindgen]
20+
impl Pointer {
21+
/// !!! DEPRECATED !!!
22+
/// This constructor uses outdated slot number format for the ttl value, tx_index and cert_index.
23+
/// Use `.new_pointer` instead
24+
#[deprecated(
25+
since = "10.1.0",
26+
note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use new_pointer instead."
27+
)]
28+
pub fn new(slot: Slot32, tx_index: TransactionIndex, cert_index: CertificateIndex) -> Self {
29+
Self {
30+
slot: slot.into(),
31+
tx_index: tx_index.into(),
32+
cert_index: cert_index.into(),
33+
}
34+
}
35+
36+
pub fn new_pointer(slot: &SlotBigNum, tx_index: &BigNum, cert_index: &BigNum) -> Self {
37+
Self {
38+
slot: slot.clone(),
39+
tx_index: tx_index.clone(),
40+
cert_index: cert_index.clone(),
41+
}
42+
}
43+
44+
pub fn slot(&self) -> Result<u32, JsError> {
45+
self.slot.clone().try_into()
46+
}
47+
48+
pub fn tx_index(&self) -> Result<u32, JsError> {
49+
self.tx_index.clone().try_into()
50+
}
51+
52+
pub fn cert_index(&self) -> Result<u32, JsError> {
53+
self.cert_index.clone().try_into()
54+
}
55+
56+
pub fn slot_bignum(&self) -> BigNum {
57+
self.slot.clone()
58+
}
59+
60+
pub fn tx_index_bignum(&self) -> BigNum {
61+
self.tx_index.clone()
62+
}
63+
64+
pub fn cert_index_bignum(&self) -> BigNum {
65+
self.cert_index.clone()
66+
}
67+
68+
pub(crate) fn from_bytes(data: &[u8]) -> Result<(Pointer, usize), DeserializeError> {
69+
let (pointer, offset) = decode_pointer(data)?;
70+
Ok((pointer, offset))
71+
}
72+
73+
pub(crate) fn to_bytes(&self) -> Vec<u8> {
74+
let mut buf = Vec::new();
75+
buf.extend_from_slice(&encode_variable_length(self.slot.into()));
76+
buf.extend_from_slice(&encode_variable_length(self.tx_index.into()));
77+
buf.extend_from_slice(&encode_variable_length(self.cert_index.into()));
78+
buf
79+
}
80+
}
81+
82+
83+
fn decode_pointer(data: &[u8]) -> Result<(Pointer, usize), DeserializeError> {
84+
let mut cursor = Cursor::new(data);
85+
let mut offset = 0;
86+
let slot_no_64 = decode_variable_length_u64(&mut cursor, "SlotNo")?;
87+
let tx_ix_64 = decode_variable_length_u64(&mut cursor, "TxIx")?;
88+
let cert_ix_64 = decode_variable_length_u64(&mut cursor, "CertIx")?;
89+
90+
offset += cursor.position() as usize;
91+
92+
Ok((
93+
analyze_and_convert(slot_no_64, tx_ix_64, cert_ix_64),
94+
offset,
95+
))
96+
}
97+
98+
fn analyze_and_convert(slot_no: u64, tx_ix: u64, cert_ix: u64) -> Pointer {
99+
let needs_normalization =
100+
slot_no > u32::MAX as u64 || tx_ix > u16::MAX as u64 || cert_ix > u16::MAX as u64;
101+
102+
// Normalization is (0,0,0). Ha-ha-ha.
103+
if needs_normalization {
104+
Pointer {
105+
slot: 0u64.into(),
106+
tx_index: 0u64.into(),
107+
cert_index: 0u64.into(),
108+
}
109+
} else {
110+
Pointer {
111+
slot: slot_no.into(),
112+
tx_index: tx_ix.into(),
113+
cert_index: cert_ix.into(),
114+
}
115+
}
116+
}
117+
118+
/// Decodes variable-length u64 exactly like Haskell decodeVariableLengthWord64
119+
/// Non-recursive implementation that mimics: fix (decode7BitVarLength name buf) 0
120+
///
121+
/// Haskell code equivalent:
122+
/// decode7BitVarLength name buf cont !acc = do
123+
/// guardLength name 1 buf
124+
/// offset <- state (\off -> (off, off + 1))
125+
/// let b8 = bufUnsafeIndex buf offset
126+
/// if b8 `testBit` 7
127+
/// then cont (acc `shiftL` 7 .|. fromIntegral (b8 `clearBit` 7))
128+
/// else pure (acc `shiftL` 7 .|. fromIntegral b8)
129+
pub(crate) fn decode_variable_length_u64(
130+
cursor: &mut Cursor<&[u8]>,
131+
name: &'static str,
132+
) -> Result<u64, DeserializeError> {
133+
let mut acc = 0u64;
134+
135+
loop {
136+
// Equivalent of guardLength name 1 buf
137+
if cursor.position() >= cursor.get_ref().len() as u64 {
138+
return Err(DeserializeError::new(name, DeserializeFailure::VariableLenNatDecodeFailed));
139+
}
140+
141+
// Equivalent of: offset <- state (\off -> (off, off + 1))
142+
let mut byte = [0u8; 1];
143+
cursor
144+
.read_exact(&mut byte)
145+
.map_err(|e| DeserializeError::new(name, DeserializeFailure::IoError(e.to_string())))?;
146+
let b8 = byte[0];
147+
148+
// Equivalent of: acc `shiftL` 7 .|. fromIntegral (b8 `clearBit` 7)
149+
acc = (acc << 7) | ((b8 & 0x7F) as u64);
150+
151+
// Equivalent of: if b8 `testBit` 7
152+
if (b8 & 0x80) == 0 {
153+
// else pure (acc `shiftL` 7 .|. fromIntegral b8)
154+
return Ok(acc);
155+
}
156+
}
157+
}
158+
159+
pub(crate) fn encode_variable_length(value: u64) -> Vec<u8> {
160+
let mut tmp = value;
161+
if tmp == 0 {
162+
return vec![0];
163+
}
164+
165+
let mut bytes = Vec::new();
166+
while tmp > 0 {
167+
bytes.push((tmp & 0x7F) as u8);
168+
tmp >>= 7;
169+
}
170+
171+
bytes.reverse();
172+
173+
for i in 0..bytes.len() - 1 {
174+
bytes[i] |= 0x80;
175+
}
176+
177+
bytes
178+
}

0 commit comments

Comments
 (0)