1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#![cfg(any(test, feature = "hazmat-address"))]
#![cfg_attr(feature = "docs", doc(cfg(feature = "hazmat-address")))]
//! Address payload extraction and construction.
//!
//! This module provides types and functions for extracting raw payloads from
//! addresses and constructing addresses from raw payloads. This is useful for
//! cross-chain interoperability where raw addresses need to be passed between
//! different systems.
//!
//! # Warning
//!
//! For account addresses, the returned Ed25519 public key corresponds to
//! the account's master key, which depending on the configuration of that
//! account may or may not be a signer of the account. Do not use this for
//! custom Ed25519 signature verification as a form of authentication
//! because the master key may not be configured the signer of the account.
use crate::{unwrap::UnwrapOptimized, Address, Bytes, BytesN, Env};
/// The payload contained in an [`Address`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AddressPayload {
/// A 32-byte Ed25519 public key from an account address (G...).
///
/// # Warning
///
/// The Ed25519 public key corresponds to the account's master key, which
/// depending on the configuration of that account may or may not be a
/// signer of the account. Do not use this for custom Ed25519 signature
/// verification as a form of authentication.
AccountIdPublicKeyEd25519(BytesN<32>),
/// A 32-byte contract hash from a contract address (C...).
ContractIdHash(BytesN<32>),
}
impl AddressPayload {
/// Constructs an [`Address`] from this payload.
///
/// This is the inverse of [`from_address`][AddressPayload::from_address].
///
/// # Warning
///
/// For account addresses, the returned Ed25519 public key corresponds to
/// the account's master key, which depending on the configuration of that
/// account may or may not be a signer of the account. Do not use this for
/// custom Ed25519 signature verification as a form of authentication
/// because the master key may not be configured the signer of the account.
pub fn to_address(&self, env: &Env) -> Address {
use crate::xdr::FromXdr;
// Build XDR header and get payload bytes based on payload type:
let (header, payload_bytes) = match self {
AddressPayload::AccountIdPublicKeyEd25519(bytes) => (
[
0, 0, 0, 18, // ScVal::Address
0, 0, 0, 0, // ScAddress::Account
0, 0, 0, 0, // PublicKey::PublicKeyTypeEd25519
]
.as_slice(),
bytes.as_bytes(),
),
AddressPayload::ContractIdHash(bytes) => (
[
0, 0, 0, 18, // ScVal::Address
0, 0, 0, 1, // ScAddress::Contract
]
.as_slice(),
bytes.as_bytes(),
),
};
let mut xdr = Bytes::from_slice(env, header);
xdr.append(payload_bytes);
Address::from_xdr(env, &xdr).unwrap_optimized()
}
/// Extracts an [`AddressPayload`] from an [`Address`].
///
/// Returns:
/// - For contract addresses (C...), returns [`AddressPayload::ContractIdHash`]
/// containing the 32-byte contract hash.
/// - For account addresses (G...), returns [`AddressPayload::AccountIdPublicKeyEd25519`]
/// containing the 32-byte Ed25519 public key.
///
/// Returns `None` if the address type is not recognized. This may occur if
/// a new address type has been introduced to the network that this version
/// of this library is not aware of.
///
/// # Warning
///
/// For account addresses, the returned Ed25519 public key corresponds to
/// the account's master key, which depending on the configuration of that
/// account may or may not be a signer of the account. Do not use this for
/// custom Ed25519 signature verification as a form of authentication
/// because the master key may not be configured the signer of the account.
pub fn from_address(address: &Address) -> Option<Self> {
use crate::xdr::ToXdr;
let xdr = address.to_xdr(address.env());
// Skip over ScVal discriminant because we know it is an ScAddress.
let xdr = xdr.slice(4..);
// Decode ScAddress
let addr_type: BytesN<4> = xdr.slice(0..4).try_into().unwrap_optimized();
match addr_type.to_array() {
// Decode ScAddress::Account
[0, 0, 0, 0] => {
// Decode PublicKey
let public_key_type: BytesN<4> = xdr.slice(4..8).try_into().unwrap_optimized();
match public_key_type.to_array() {
// Decode PublicKey::PublicKeyTypeEd25519
[0, 0, 0, 0] => {
let ed25519: BytesN<32> = xdr.slice(8..40).try_into().unwrap_optimized();
Some(AddressPayload::AccountIdPublicKeyEd25519(ed25519))
}
_ => None,
}
}
// Decode ScAddress::Contract
[0, 0, 0, 1] => {
let hash: BytesN<32> = xdr.slice(4..36).try_into().unwrap_optimized();
Some(AddressPayload::ContractIdHash(hash))
}
_ => None,
}
}
}