anoma / namada

Rust implementation of Namada, a Proof-of-Stake L1 for interchain asset-agnostic privacy
https://namada.net
GNU General Public License v3.0
2.4k stars 951 forks source link

First byte (enum discriminant) changes when encoding/decoding `struct Address` with Borsh #2731

Closed egasimus closed 7 months ago

egasimus commented 7 months ago

Querying /vp/governance/proposal/200, I get author address as the following 21 byte value:

[1,6,116,253,180,174,49,102,45,88,194,178,127,8,176,246,123,45,197,85,228]

Here's what happens if I try to convert it to bech32m and back again, like so:

use namada_core::types::address::Address;
use namada_core::types::string_encoding::Format;
use namada_core::borsh::BorshDeserialize;
fn main() {
    let a = [1u8,6,116,253,180,174,49,102,45,88,194,178,127,8,176,246,123,45,197,85,228];
    let b = Address::try_from_slice(&a).unwrap();
    let c = b.to_bytes();
    let d = Address::try_from_slice(&c).unwrap();
    println!("{a}\n{b}\n{c}\n{d}");
}

Output:

[1, 6, 116, 253, 180, 174, 49, 102, 45, 88, 194, 178, 127, 8, 176, 246, 123, 45, 197, 85, 228]
Implicit: tnam1qqr8fld54cckvt2cc2e87z9s7eajm324usq5vkm9
[0, 6, 116, 253, 180, 174, 49, 102, 45, 88, 194, 178, 127, 8, 176, 246, 123, 45, 197, 85, 228]
Established: tnam1qyr8fld54cckvt2cc2e87z9s7eajm324us9hznxq

Looks like the 1st byte is switched around between Implicit and Established.

Address: tnam1qqr8fld54cckvt2cc2e87z9s7eajm324usq5vkm9
Threshold: 1
Public keys:
- tpknam1qpz6094hw42ylh2e8rxad9cm8620q5hxgzhxks7ncyms4zxg52wgs260vks
No account exists for tnam1qyr8fld54cckvt2cc2e87z9s7eajm324us9hznxq
sug0 commented 7 months ago

you're mixing and matching encoding formats which happen to have the same underlying representation.

use namada_core::types::string_encoding::Format;

this API encodes addresses as "raw" addresses, which (tbh, confusingly) have discriminants different from borsh's for implicit and established addresses. try running this program:

use namada_core::types::address::Address;
use namada_core::types::string_encoding::Format;
use namada_core::borsh::*;

fn main() {
    const ADDR: &str = "tnam1qqr8fld54cckvt2cc2e87z9s7eajm324usq5vkm9";
    let addr = Address::decode(ADDR).unwrap();

    let borsh_encoded_bytes = addr.serialize_to_vec();
    let raw_addr_encoded_bytes = addr.to_bytes();

    assert!(borsh_encoded_bytes != raw_addr_encoded_bytes);

    println!("borsh_encoded_bytes:    {borsh_encoded_bytes:02X?}");
    println!("raw_addr_encoded_bytes: {raw_addr_encoded_bytes:02X?}");
}

and you'll see this output:

borsh_encoded_bytes:    [01, 06, 74, FD, B4, AE, 31, 66, 2D, 58, C2, B2, 7F, 08, B0, F6, 7B, 2D, C5, 55, E4]
raw_addr_encoded_bytes: [00, 06, 74, FD, B4, AE, 31, 66, 2D, 58, C2, B2, 7F, 08, B0, F6, 7B, 2D, C5, 55, E4]

notice how the first bytes differ.

-- sugo