Skip to content

Commit adeac02

Browse files
mergify[bot]0xRigeljoncinque
authored
v2.1: SIMD-0075: Precompile for Secp256r1 (backport of #3152) (#3662)
* SIMD-0075: Precompile for Secp256r1 (#3152) * feat: secp256r1 precompile * add: num_signatures == 0 check from SIMD-0152 * rm: unnecessary comment * fix: legacy numeric constant * CI/fix: compilation for wasm32 target * Extract secp256r1 crate * rm: unnecessary import * update: sbf/Cargo.lock * rm: unnecesary re-exports * add: secp256r1 precompile to docs * add: docs/description to sdk/program/src/lib.rs * fix: alpha sort deps * fixes * docs fixes * add: solana-instruction std feature to deps * fix: lockfile from rebase * fix: target architecture * fix: workflow for client_target android * add: sudo to workflow perl install * fix: Cargo toml workspace member * modify: ranlib path in client-targets.yaml * fix: secp256r1/Cargo.toml formatting * add: openssl feature * fixes * add: precompile signature range error * more adjustments * change: feature id * fix: cargo format * Revert "add: precompile signature range error" This reverts commit fdf7673. * fix: cargo sanity * fix: client target openssl dep * fix: 31 byte r,s support in new_secp256r1_instruction * update: Cargo.lock * fix: unchecked math in new_secp256r1_instruction * fixes & increased test coverage * add: solana-sdk/openssl to all release binaries * update: comment to make openssl feature more clear * add: solana-sdk/openssl feature to dependencies * add: solana-sdk/openssl feature to dependencies * merge: master into secp256r1-precompile * fix: test-validator formatting * Revert "add: solana-sdk/openssl to all release binaries" This reverts commit 5c66b50. * add: reserved key for secp256r1 program * modify: client-targets.yaml * modify: client/Cargo.toml solana-sdk dep * modify: ledger-tool/Cargo.toml solana-sdk * modify: test-validator/Cargo.toml solana-sdk dep * modify: validator/Cargo.toml solana-sdk dep * change: openssl feature to openssl-vendored * remove: solana-sdk dep from sdk/program * refactor: secp256r1 directory name * fmt * cargo.lock files * revert: rustc-demangle bump * cargo lock sanity * fix: faulty feature-set merge * fix: reserved keys pending feature id --------- Co-authored-by: Iceomatic <[email protected]> (cherry picked from commit da4f55e) # Conflicts: # Cargo.lock # Cargo.toml # ledger-tool/Cargo.toml # programs/sbf/Cargo.lock # sdk/Cargo.toml # sdk/feature-set/src/lib.rs # sdk/reserved-account-keys/Cargo.toml # sdk/src/reserved_account_keys.rs * Fix merge conflicts --------- Co-authored-by: Orion <[email protected]> Co-authored-by: Jon C <[email protected]>
1 parent 34e62d6 commit adeac02

File tree

15 files changed

+909
-4
lines changed

15 files changed

+909
-4
lines changed

.github/workflows/client-targets.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ jobs:
3434
steps:
3535
- uses: actions/checkout@v4
3636

37+
# This can be removed once cargo-ndk >= 3.5.4 is used.
38+
- name: Setup environment for Android NDK
39+
run: |
40+
echo "RANLIB=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib" >> $GITHUB_ENV
41+
3742
- run: cargo install [email protected]
3843

3944
- name: Setup Rust

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ members = [
135135
"sdk/pubkey",
136136
"sdk/rent",
137137
"sdk/sanitize",
138+
"sdk/secp256r1-program",
138139
"sdk/serde-varint",
139140
"sdk/serialize-utils",
140141
"sdk/sha256-hasher",
@@ -475,6 +476,7 @@ solana-rayon-threadlimit = { path = "rayon-threadlimit", version = "=2.1.1" }
475476
solana-remote-wallet = { path = "remote-wallet", version = "=2.1.1", default-features = false }
476477
solana-rent = { path = "sdk/rent", version = "=2.1.1", default-features = false }
477478
solana-sanitize = { path = "sdk/sanitize", version = "=2.1.1" }
479+
solana-secp256r1-program = { path = "sdk/secp256r1-program", version = "=2.1.1", default-features = false }
478480
solana-serde-varint = { path = "sdk/serde-varint", version = "=2.1.1" }
479481
solana-serialize-utils = { path = "sdk/serialize-utils", version = "=2.1.1" }
480482
solana-sha256-hasher = { path = "sdk/sha256-hasher", version = "=2.1.1" }

client/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ solana-quic-client = { workspace = true }
2727
solana-rpc-client = { workspace = true, features = ["default"] }
2828
solana-rpc-client-api = { workspace = true }
2929
solana-rpc-client-nonce-utils = { workspace = true }
30-
solana-sdk = { workspace = true }
30+
solana-sdk = { workspace = true, features = ["openssl-vendored"] }
3131
solana-streamer = { workspace = true }
3232
solana-thin-client = { workspace = true }
3333
solana-tpu-client = { workspace = true, features = ["default"] }

docs/src/runtime/programs.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,71 @@ also receive data from the transaction itself.
168168
Cost of the transaction will count the number of signatures to verify multiplied
169169
by the signature cost verify multiplier.
170170

171+
## Secp256r1 Program
172+
173+
The program for verifying secp256r1 signatures. It takes a secp256r1 signature,
174+
a public key, and a message. Up to 8 signatures can be verified. If any of the
175+
signatures fail to verify, an error is returned.
176+
177+
- Program id: `Secp256r1SigVerify1111111111111111111111111`
178+
- Instructions: [secp256r1_instruction](https://docs.rs/solana-secp256r1)
179+
180+
The secp256r1 program processes an instruction. The first `u8` is a count of the number of signatures to check, followed by a single byte padding. After that, the following struct is serialized, one for each signature to check:
181+
182+
```rust
183+
struct Secp256r1SignatureOffsets {
184+
signature_offset: u16, // offset to compact secp256r1 signature of 64 bytes
185+
signature_instruction_index: u16, // instruction index to find signature
186+
public_key_offset: u16, // offset to compressed public key of 33 bytes
187+
public_key_instruction_index: u16, // instruction index to find public key
188+
message_data_offset: u16, // offset to start of message data
189+
message_data_size: u16, // size of message data
190+
message_instruction_index: u16, // index of instruction data to get message data
191+
}
192+
193+
```
194+
195+
The pseudo code of the signature verification:
196+
```
197+
process_instruction() {
198+
if data.len() < SIGNATURE_OFFSETS_START {
199+
return Error
200+
}
201+
202+
num_signatures = data[0] as usize
203+
if num_signatures == 0 || num_signatures > 8 {
204+
return Error
205+
}
206+
207+
expected_data_size = num_signatures * SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START
208+
if data.len() < expected_data_size {
209+
return Error
210+
}
211+
212+
for i in 0..num_signatures {
213+
offsets = parse_signature_offsets(data, i)
214+
215+
signature = get_data_slice(data, instruction_datas, offsets.signature_instruction_index, offsets.signature_offset, SIGNATURE_SERIALIZED_SIZE)
216+
217+
if s > half_curve_order {
218+
return Error
219+
}
220+
221+
pubkey = get_data_slice(data, instruction_datas, offsets.public_key_instruction_index, offsets.public_key_offset, COMPRESSED_PUBKEY_SERIALIZED_SIZE)
222+
223+
message = get_data_slice(data, instruction_datas, offsets.message_instruction_index, offsets.message_data_offset, offsets.message_data_size)
224+
225+
if !verify_signature(signature, pubkey, message) {
226+
return Error
227+
}
228+
}
229+
230+
return Success
231+
}
232+
```
233+
Note: Low S values are enforced for all signatures to avoid accidental signature
234+
malleability.
235+
171236
### Optimization notes
172237

173238
The operation will have to take place after (at least partial) deserialization,

ledger-tool/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ solana-measure = { workspace = true }
4545
solana-program-runtime = { workspace = true }
4646
solana-rpc = { workspace = true }
4747
solana-runtime = { workspace = true, features = ["dev-context-only-utils"] }
48-
solana-sdk = { workspace = true }
48+
solana-sdk = { workspace = true, features = ["openssl-vendored"] }
4949
solana-stake-program = { workspace = true }
5050
solana-storage-bigtable = { workspace = true }
5151
solana-streamer = { workspace = true }

programs/sbf/Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ frozen-abi = [
5050
"solana-signature/frozen-abi",
5151
"solana-transaction-error/frozen-abi"
5252
]
53+
# Enables the "vendored" feature of openssl inside of secp256r1-program
54+
openssl-vendored = ["solana-secp256r1-program/openssl-vendored"]
5355

5456
[dependencies]
5557
bincode = { workspace = true }
@@ -111,6 +113,7 @@ solana-pubkey = { workspace = true, default-features = false, features = ["std"]
111113
solana-sanitize = { workspace = true }
112114
solana-sdk-macro = { workspace = true }
113115
solana-secp256k1-recover = { workspace = true }
116+
solana-secp256r1-program = { workspace = true, default-features = false }
114117
solana-serde-varint = { workspace = true }
115118
solana-short-vec = { workspace = true }
116119
solana-signature = { workspace = true, features = [

sdk/feature-set/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,10 @@ pub mod disable_account_loader_special_case {
873873
solana_pubkey::declare_id!("EQUMpNFr7Nacb1sva56xn1aLfBxppEoSBH8RRVdkcD1x");
874874
}
875875

876+
pub mod enable_secp256r1_precompile {
877+
solana_pubkey::declare_id!("sr11RdZWgbHTHxSroPALe6zgaT5A1K9LcE4nfsZS4gi");
878+
}
879+
876880
lazy_static! {
877881
/// Map of feature identifiers to user-visible description
878882
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@@ -1086,6 +1090,7 @@ lazy_static! {
10861090
(disable_sbpf_v1_execution::id(), "Disables execution of SBPFv1 programs"),
10871091
(reenable_sbpf_v1_execution::id(), "Re-enables execution of SBPFv1 programs"),
10881092
(disable_account_loader_special_case::id(), "Disable account loader special case #3513"),
1093+
(enable_secp256r1_precompile::id(), "Enable secp256r1 precompile SIMD-0075"),
10891094
/*************** ADD NEW FEATURES HERE ***************/
10901095
]
10911096
.iter()

sdk/secp256r1-program/Cargo.toml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[package]
2+
name = "solana-secp256r1-program"
3+
description = "Precompile implementation for the secp256r1 elliptic curve."
4+
documentation = "https://docs.rs/solana-secp256r1"
5+
version = { workspace = true }
6+
authors = { workspace = true }
7+
repository = { workspace = true }
8+
homepage = { workspace = true }
9+
license = { workspace = true }
10+
edition = { workspace = true }
11+
12+
[dependencies]
13+
bytemuck = { workspace = true, features = ["derive"] }
14+
solana-feature-set = { workspace = true }
15+
solana-precompile-error = { workspace = true }
16+
solana-pubkey = { workspace = true }
17+
18+
[target.'cfg(all(not(target_arch = "wasm32"), not(target_os = "solana")))'.dependencies]
19+
solana-instruction = { workspace = true, features = ["std"] }
20+
openssl = { workspace = true }
21+
22+
[dev-dependencies]
23+
solana-logger = { workspace = true }
24+
solana-sdk = { path = "../" }
25+
26+
[features]
27+
default = []
28+
openssl-vendored = ["openssl/vendored"]
29+
30+
[package.metadata.docs.rs]
31+
targets = ["x86_64-unknown-linux-gnu"]
32+
all-features = true
33+
rustdoc-args = ["--cfg=docsrs"]
34+
35+
[lints]
36+
workspace = true

0 commit comments

Comments
 (0)