Skip to content

Commit

Permalink
Land program addresses on the curve (solana-labs#11174)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmay authored Jul 27, 2020
1 parent aa8d608 commit f317c36
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 15 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions programs/bpf/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 sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ default = [
"assert_matches",
"byteorder",
"chrono",
"curve25519-dalek",
"generic-array",
"memmap",
"rand",
Expand All @@ -35,6 +36,7 @@ bs58 = "0.3.1"
bv = { version = "0.11.1", features = ["serde"] }
byteorder = { version = "1.3.4", optional = true }
chrono = { version = "0.4", optional = true }
curve25519-dalek = { version = "2.1.0", optional = true }
generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"], optional = true }
hex = "0.4.2"
hmac = "0.7.0"
Expand Down
13 changes: 13 additions & 0 deletions sdk/benches/pubkey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(test)]

extern crate test;
use solana_sdk::pubkey::Pubkey;
use test::Bencher;

#[bench]
fn bench_pubkey_create_program_address(b: &mut Bencher) {
let program_id = Pubkey::new_rand();
b.iter(|| {
Pubkey::create_program_address(&[b"todo"], &program_id).unwrap();
});
}
62 changes: 47 additions & 15 deletions sdk/src/pubkey.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use crate::{
decode_error::DecodeError,
hash::{hash, hashv, Hasher},
};
use crate::{decode_error::DecodeError, hash::hashv};
use num_derive::{FromPrimitive, ToPrimitive};
#[cfg(not(feature = "program"))]
use std::error;
use std::{convert::TryFrom, fmt, mem, str::FromStr};
use thiserror::Error;

Expand Down Expand Up @@ -87,20 +82,35 @@ impl Pubkey {
))
}

#[cfg(not(feature = "program"))]
pub fn create_program_address(
seeds: &[&[u8]],
program_id: &Pubkey,
) -> Result<Pubkey, PubkeyError> {
let mut hasher = Hasher::default();
use std::convert::TryInto;

let mut hasher = crate::hash::Hasher::default();
for seed in seeds.iter() {
if seed.len() > MAX_SEED_LEN {
return Err(PubkeyError::MaxSeedLengthExceeded);
}
hasher.hash(seed);
}
hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]);
let mut hashed_bits: [u8; 32] = hasher.result().as_ref().try_into().unwrap();

Ok(Pubkey::new(hash(hasher.result().as_ref()).as_ref()))
// clamp
hashed_bits[0] &= 248;
hashed_bits[31] &= 127;
hashed_bits[31] |= 64;

// point multiply
Ok(Pubkey::new(
(&curve25519_dalek::scalar::Scalar::from_bits(hashed_bits)
* &curve25519_dalek::constants::ED25519_BASEPOINT_TABLE)
.compress()
.as_bytes(),
))
}

#[cfg(not(feature = "program"))]
Expand Down Expand Up @@ -132,7 +142,7 @@ impl fmt::Display for Pubkey {
}

#[cfg(not(feature = "program"))]
pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn error::Error>> {
pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn std::error::Error>> {
use std::io::Write;

let printable = format!("{}", pubkey);
Expand All @@ -148,7 +158,7 @@ pub fn write_pubkey_file(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn er
}

#[cfg(not(feature = "program"))]
pub fn read_pubkey_file(infile: &str) -> Result<Pubkey, Box<dyn error::Error>> {
pub fn read_pubkey_file(infile: &str) -> Result<Pubkey, Box<dyn std::error::Error>> {
let f = std::fs::File::open(infile.to_string())?;
let printable: String = serde_json::from_reader(f)?;
Ok(Pubkey::from_str(&printable)?)
Expand Down Expand Up @@ -262,25 +272,25 @@ mod tests {
assert!(Pubkey::create_program_address(&[max_seed], &Pubkey::new_rand()).is_ok());
assert_eq!(
Pubkey::create_program_address(&[b""], &program_id),
Ok("CsdSsqp6Upkh2qajhZMBM8xT4GAyDNSmcV37g4pN8rsc"
Ok("Gv1heG5PQhTNevViduUvVBv8XmcEo6AHcoyA2wXrCsAa"
.parse()
.unwrap())
);
assert_eq!(
Pubkey::create_program_address(&["☉".as_ref()], &program_id),
Ok("A8mYnN8Pfx7Nn6f8RoQgsPNtAGAWmmKSBCDfyDvE6sXF"
Ok("GzVDzHSMACepN7FVPhPJG3DkG2WkEWgRHWanefxvaZq6"
.parse()
.unwrap())
);
assert_eq!(
Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id),
Ok("CawYq8Rmj4JRR992wVnGEFUjMEkmtmcFgEL4iS1qPczu"
Ok("BBstFkvRCCbzQKgdTqypWN1bZmfEmRc9d5sNGYZtEicM"
.parse()
.unwrap())
);
assert_eq!(
Pubkey::create_program_address(&[public_key.as_ref()], &program_id),
Ok("4ak7qJacCKMAGP8xJtDkg2VYZh5QKExa71ijMDjZGQyb"
Ok("6f2a73BjEZKguMpgdmzvSz6qNtmzomCkJgoK21F1yPQK"
.parse()
.unwrap())
);
Expand All @@ -291,7 +301,29 @@ mod tests {
}

#[test]
fn test_read_write_pubkey() -> Result<(), Box<dyn error::Error>> {
fn test_pubkey_on_curve() {
// try a bunch of random input, all the generated program addresses should land on the curve
// and should be unique
let mut addresses = vec![];
for _ in 0..1_000 {
let program_id = Pubkey::new_rand();
let bytes1 = rand::random::<[u8; 10]>();
let bytes2 = rand::random::<[u8; 32]>();
let program_address =
Pubkey::create_program_address(&[&bytes1, &bytes2], &program_id).unwrap();
let is_on_curve = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(
&program_address.to_bytes(),
)
.decompress()
.is_some();
assert!(is_on_curve);
assert!(!addresses.contains(&program_address));
addresses.push(program_address);
}
}

#[test]
fn test_read_write_pubkey() -> Result<(), Box<dyn std::error::Error>> {
let filename = "test_pubkey.json";
let pubkey = Pubkey::new_rand();
write_pubkey_file(filename, pubkey)?;
Expand Down

0 comments on commit f317c36

Please sign in to comment.