Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 36 additions & 29 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"account",
"account-info",
"address",
"address-lookup-table-interface",
"atomic-u64",
"big-mod-exp",
Expand Down Expand Up @@ -206,6 +207,7 @@ signal-hook = "0.3.17"
siphasher = "0.3.11"
solana-account = { path = "account", version = "2.2.1" }
solana-account-info = { path = "account-info", version = "2.2.1" }
solana-address = { path = "address", version = "0.1.0" }
solana-address-lookup-table-interface = { path = "address-lookup-table-interface", version = "2.2.2" }
solana-atomic-u64 = { path = "atomic-u64", version = "2.2.1" }
solana-big-mod-exp = { path = "big-mod-exp", version = "2.2.1" }
Expand Down
4 changes: 2 additions & 2 deletions account/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub mod state_traits;
#[cfg_attr(
feature = "frozen-abi",
derive(AbiExample),
frozen_abi(digest = "2SUJNHbXMPWrsSXmDTFc4VHx2XQ85fT5Leabefh5Nwe7")
frozen_abi(digest = "62EqVoynUFvuui7DVfqWCvZP7bxKGJGioeSBnWrdjRME")
)]
#[cfg_attr(
feature = "serde",
Expand Down Expand Up @@ -70,7 +70,7 @@ mod account_serialize {
#[cfg_attr(
feature = "frozen-abi",
derive(AbiExample),
frozen_abi(digest = "2SUJNHbXMPWrsSXmDTFc4VHx2XQ85fT5Leabefh5Nwe7")
frozen_abi(digest = "62EqVoynUFvuui7DVfqWCvZP7bxKGJGioeSBnWrdjRME")
)]
#[derive(serde_derive::Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down
83 changes: 83 additions & 0 deletions address/Cargo.toml
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! I like how every dependency is optional! However I think you're going to annoy a lot of developers if you don't include a default. I recommend at least decode, but you could also make a case for std.

We can build hyper-modular tooling to empower devs who want to optimize their programs, but that doesn't mean we need to increase the burden on the majority of developers.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a good point. My thinking was that most people will use solana-pubkey for a while, so that'll act like the "default" feature as people port over.

Since it's a breaking change to remove features from the default, I'd prefer to keep this lean for now. We can always add default features later as people struggle. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I'll leave it up to you, but if you have a small set of sensible defaults, and power users aren't happy with those, what's the reason you'd ever have to remove a default feature because someone couldn't get around it with default-features = false?

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
[package]
name = "solana-address"
description = "Solana account addresses"
documentation = "https://docs.rs/solana-address"
version = "0.1.0"
rust-version = "1.81.0"
authors = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
edition = { workspace = true }

[features]
atomic = ["dep:solana-atomic-u64"]
borsh = ["dep:borsh", "std"]
bytemuck = ["dep:bytemuck", "dep:bytemuck_derive"]
curve25519 = ["dep:curve25519-dalek", "error", "sha2"]
decode = ["dep:five8", "dep:five8_const", "error"]
dev-context-only-utils = ["dep:arbitrary", "rand"]
error = ["dep:num-traits", "dep:solana-program-error"]
frozen-abi = [
"dep:solana-frozen-abi",
"dep:solana-frozen-abi-macro",
"dep:solana-program-error",
"std",
]
rand = ["dep:rand", "atomic", "std"]
sanitize = ["dep:solana-sanitize"]
serde = ["dep:serde", "dep:serde_derive"]
sha2 = ["dep:solana-sha256-hasher", "error"]
std = ["decode"]
syscalls = ["dep:solana-define-syscall", "error"]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "error" feature is needed for syscalls since the helpers return an AddressError (used to be PubkeyError) instead of a ProgramError. I think the differentiation is there since these helpers can be used offchain. We could make the on-chain "version" of those helpers return a ProgramError so programs don't need the "error" feature in a future PR.


[dependencies]
arbitrary = { workspace = true, features = ["derive"], optional = true }
borsh = { workspace = true, optional = true }
bytemuck = { workspace = true, optional = true }
bytemuck_derive = { workspace = true, optional = true }
five8 = { workspace = true, optional = true }
five8_const = { workspace = true, optional = true }
num-traits = { workspace = true, optional = true }
rand = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_derive = { workspace = true, optional = true }
solana-atomic-u64 = { workspace = true, optional = true }
solana-frozen-abi = { workspace = true, features = ["frozen-abi"], optional = true }
solana-frozen-abi-macro = { workspace = true, features = ["frozen-abi"], optional = true }
solana-program-error = { workspace = true, optional = true }
solana-sanitize = { workspace = true, optional = true }

[target.'cfg(not(target_os = "solana"))'.dependencies]
curve25519-dalek = { workspace = true, optional = true }
solana-sha256-hasher = { workspace = true, features = ["sha2"], optional = true }

[target.'cfg(target_os = "solana")'.dependencies]
solana-define-syscall = { workspace = true, optional = true }
solana-sha256-hasher = { workspace = true, optional = true }

[dev-dependencies]
anyhow = { workspace = true }
solana-account-info = { path = "../account-info" }
solana-address = { path = ".", features = [
"atomic",
"borsh",
"curve25519",
"decode",
"dev-context-only-utils",
"error",
"sanitize",
"std",
"syscalls"
] }
solana-cpi = { path = "../cpi" }
solana-example-mocks = { path = "../example-mocks" }
solana-hash = { workspace = true }
solana-instruction = { path = "../instruction", features = ["borsh"] }
solana-program-error = { workspace = true, features = ["borsh"] }
solana-system-interface = { workspace = true, features = ["bincode"] }
strum = { workspace = true }
strum_macros = { workspace = true }

[lints]
workspace = true
148 changes: 148 additions & 0 deletions address/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#[cfg(feature = "serde")]
use serde_derive::Serialize;
use {
core::{convert::Infallible, fmt},
num_traits::{FromPrimitive, ToPrimitive},
solana_program_error::ProgramError,
};

// Use strum when testing to ensure our FromPrimitive
// impl is exhaustive
#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
#[cfg_attr(feature = "serde", derive(serde_derive::Serialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AddressError {
/// Length of the seed is too long for address generation
MaxSeedLengthExceeded,
InvalidSeeds,
IllegalOwner,
}

impl ToPrimitive for AddressError {
#[inline]
fn to_i64(&self) -> Option<i64> {
Some(match *self {
AddressError::MaxSeedLengthExceeded => AddressError::MaxSeedLengthExceeded as i64,
AddressError::InvalidSeeds => AddressError::InvalidSeeds as i64,
AddressError::IllegalOwner => AddressError::IllegalOwner as i64,
})
}
#[inline]
fn to_u64(&self) -> Option<u64> {
self.to_i64().map(|x| x as u64)
}
}

impl FromPrimitive for AddressError {
#[inline]
fn from_i64(n: i64) -> Option<Self> {
if n == AddressError::MaxSeedLengthExceeded as i64 {
Some(AddressError::MaxSeedLengthExceeded)
} else if n == AddressError::InvalidSeeds as i64 {
Some(AddressError::InvalidSeeds)
} else if n == AddressError::IllegalOwner as i64 {
Some(AddressError::IllegalOwner)
} else {
None
}
}
#[inline]
fn from_u64(n: u64) -> Option<Self> {
Self::from_i64(n as i64)
}
}

impl core::error::Error for AddressError {}

impl fmt::Display for AddressError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AddressError::MaxSeedLengthExceeded => {
f.write_str("Length of the seed is too long for address generation")
}
AddressError::InvalidSeeds => {
f.write_str("Provided seeds do not result in a valid address")
}
AddressError::IllegalOwner => f.write_str("Provided owner is not allowed"),
}
}
}

impl From<u64> for AddressError {
fn from(error: u64) -> Self {
match error {
0 => AddressError::MaxSeedLengthExceeded,
1 => AddressError::InvalidSeeds,
2 => AddressError::IllegalOwner,
_ => panic!("Unsupported AddressError"),
}
}
}

impl From<AddressError> for ProgramError {
fn from(error: AddressError) -> Self {
match error {
AddressError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded,
AddressError::InvalidSeeds => Self::InvalidSeeds,
AddressError::IllegalOwner => Self::IllegalOwner,
}
}
}

// Use strum when testing to ensure our FromPrimitive
// impl is exhaustive
#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseAddressError {
WrongSize,
Invalid,
}

impl ToPrimitive for ParseAddressError {
#[inline]
fn to_i64(&self) -> Option<i64> {
Some(match *self {
ParseAddressError::WrongSize => ParseAddressError::WrongSize as i64,
ParseAddressError::Invalid => ParseAddressError::Invalid as i64,
})
}
#[inline]
fn to_u64(&self) -> Option<u64> {
self.to_i64().map(|x| x as u64)
}
}

impl FromPrimitive for ParseAddressError {
#[inline]
fn from_i64(n: i64) -> Option<Self> {
if n == ParseAddressError::WrongSize as i64 {
Some(ParseAddressError::WrongSize)
} else if n == ParseAddressError::Invalid as i64 {
Some(ParseAddressError::Invalid)
} else {
None
}
}
#[inline]
fn from_u64(n: u64) -> Option<Self> {
Self::from_i64(n as i64)
}
}

impl core::error::Error for ParseAddressError {}

impl fmt::Display for ParseAddressError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParseAddressError::WrongSize => f.write_str("String is the wrong size"),
ParseAddressError::Invalid => f.write_str("Invalid Base58 string"),
}
}
}

impl From<Infallible> for ParseAddressError {
fn from(_: Infallible) -> Self {
unreachable!("Infallible uninhabited");
}
}
Loading
Loading