Skip to content

add ed25519 sig-verification #355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
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
4,116 changes: 4,116 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ members = [
"tokens/token-2022/default-account-state/native/program",
"tokens/token-2022/transfer-fee/native/program",
"tokens/token-2022/multiple-extensions/native/program",
"basics/solana-signature-verification/anchor/programs/*"
]
resolver = "2"

Expand Down
29 changes: 15 additions & 14 deletions basics/favorites/native/program/src/instructions/create_pda.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
use crate::state::Favorites;
use borsh::BorshSerialize;
use solana_program::{
account_info::{next_account_info, AccountInfo},
pubkey::Pubkey,
program_error::ProgramError,
entrypoint::ProgramResult,
system_instruction,
msg,
program::invoke_signed,
program_error::ProgramError,
pubkey::Pubkey,
rent::Rent,
sysvar::Sysvar
system_instruction,
sysvar::Sysvar,
};
use crate::state::Favorites;

pub fn create_pda(
program_id: &Pubkey,
accounts: &[AccountInfo],
data: Favorites
) -> ProgramResult {
pub fn create_pda(program_id: &Pubkey, accounts: &[AccountInfo], data: Favorites) -> ProgramResult {
let account_iter = &mut accounts.iter();
let user = next_account_info(account_iter)?; // the user who's signing the transaction
let favorite_account = next_account_info(account_iter)?; // The target account that will be created in the process
let system_program = next_account_info(account_iter)?;

// deriving the favorite pda
let (favorite_pda, favorite_bump) = Pubkey::find_program_address(&[b"favorite", user.key.as_ref()], program_id);
// deriving the favorite pda
let (favorite_pda, favorite_bump) =
Pubkey::find_program_address(&[b"favorite", user.key.as_ref()], program_id);

// Checking if the favorite account is same as the derived favorite pda
if favorite_account.key != &favorite_pda {
Expand All @@ -46,13 +43,17 @@ pub fn create_pda(

invoke_signed(
&ix,
&[user.clone(), favorite_account.clone(), system_program.clone()],
&[
user.clone(),
favorite_account.clone(),
system_program.clone(),
],
&[&[b"favorite", user.key.as_ref(), &[favorite_bump]]],
)?;

// Serialize and store the data
data.serialize(&mut &mut favorite_account.data.borrow_mut()[..])?;
msg!("{:#?}",data);
msg!("{:#?}", data);
} else {
return Err(ProgramError::AccountAlreadyInitialized.into());
}
Expand Down
33 changes: 18 additions & 15 deletions basics/favorites/native/program/src/instructions/get_pda.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
use crate::state::Favorites;
use borsh::BorshDeserialize;
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
account_info::{ AccountInfo, next_account_info},
msg,
program_error::ProgramError,
pubkey::Pubkey,
program_error::ProgramError
};
use borsh::BorshDeserialize;
use crate::state::Favorites;


pub fn get_pda(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
pub fn get_pda(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let account_iter = &mut accounts.iter();
let user = next_account_info(account_iter)?;
let favorite_account = next_account_info(account_iter)?;

// deriving the favorite pda
let (favorite_pda, _) = Pubkey::find_program_address(&[b"favorite", user.key.as_ref()], program_id);
// deriving the favorite pda
let (favorite_pda, _) =
Pubkey::find_program_address(&[b"favorite", user.key.as_ref()], program_id);

// Checking if the favorite account is same as the derived favorite pda
if favorite_account.key != &favorite_pda {
return Err(ProgramError::IncorrectProgramId);
return Err(ProgramError::IncorrectProgramId);
};

let favorites = Favorites::try_from_slice(&favorite_account.data.borrow())?;

msg!("User {}'s favorite number is {}, favorite color is: {}, and their hobbies are {:#?}", user.key, favorites.number, favorites.color, favorites.hobbies);
msg!(
"User {}'s favorite number is {}, favorite color is: {}, and their hobbies are {:#?}",
user.key,
favorites.number,
favorites.color,
favorites.hobbies
);
Ok(())
}
}
2 changes: 1 addition & 1 deletion basics/favorites/native/program/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ pub mod create_pda;
pub mod get_pda;

use create_pda::*;
use get_pda::*;
use get_pda::*;
5 changes: 1 addition & 4 deletions basics/favorites/native/program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use solana_program::entrypoint;

pub mod state;
pub mod instructions;
pub mod processor;
pub mod state;

use processor::process_instruction;

entrypoint!(process_instruction);



8 changes: 2 additions & 6 deletions basics/favorites/native/program/src/processor.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use solana_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
pubkey::Pubkey,
};
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};

use crate::instructions::{create_pda::*, get_pda::*};
use crate::state::Favorites;
Expand All @@ -23,7 +19,7 @@ pub fn process_instruction(

match instruction {
FavoritesInstruction::CreatePda(data) => create_pda(program_id, accounts, data),
FavoritesInstruction::GetPda => get_pda(program_id,accounts),
FavoritesInstruction::GetPda => get_pda(program_id, accounts),
}?;

Ok(())
Expand Down
6 changes: 2 additions & 4 deletions basics/favorites/native/program/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ use borsh::{BorshDeserialize, BorshSerialize};
pub struct Favorites {
pub number: u64,
pub color: String,
pub hobbies: Vec<String>
pub hobbies: Vec<String>,
}

#[derive(BorshDeserialize, BorshSerialize)]
pub struct GetFavorites {
}

pub struct GetFavorites {}
18 changes: 18 additions & 0 deletions basics/solana-signature-verification/anchor/Anchor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[toolchain]

[features]
resolution = true
skip-lint = false

[programs.localnet]
solana-signature-verification = "BJuWkTa4GJDn75ENWqZ18ywjRoqxmybQXupAfmSH2Zyv"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "devnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
14 changes: 14 additions & 0 deletions basics/solana-signature-verification/anchor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[workspace]
members = [
"programs/*"
]
resolver = "2"

[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1
[profile.release.build-override]
opt-level = 3
incremental = false
codegen-units = 1
105 changes: 105 additions & 0 deletions basics/solana-signature-verification/anchor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Solana Program: Unlocking a Vault Based on SOL Price and Signature Verification

This Solana escrow program allows users to withdraw funds only when the SOL price reaches a certain target and after verifying their Ed25519 signature.

## Ed25519 Signature Verification

In Solana, programs cannot directly call the Ed25519 program using a CPI (Cross-Program Invocation) because signature verification is computationally expensive. Instead, the Ed25519 signature verification program exists as a precompiled instruction outside the Solana Virtual Machine (SVM).
we do verification by passing two instructions one [Ed25519 program](https://github.com/anza-xyz/agave/blob/master/sdk/ed25519-program/src/lib.rs) ix and second our custom logic ix (it mush have a sysvar ix to get current chain state)

The sysvar instructions account provides access to all instructions within the same transaction.
This allows our program to fetch and verify the arguments passed to the Ed25519 program, ensuring they were correctly signed before unlocking funds.

## Running test

To run this test on devnet, you can do the following:

```bash
$ pnpm install
$ anchor test --skip-deploy
```

# Vault Unlock Conditions

The SOL price must meet or exceed the target threshold & Ed25519 signature must be verified

# Vault Architecture

```mermaid
flowchart TD
%% Style Definitions - GitHub Monochrome Colors
classDef darkBackground fill:#24292e,stroke:#1b1f23,stroke-width:2,color:#ffffff,font-size:24px
classDef boxStyle fill:#2f363d,stroke:#1b1f23,stroke-width:2,color:#ffffff,font-size:22px
classDef subBoxStyle fill:#444d56,stroke:#1b1f23,stroke-width:2,color:#ffffff,font-size:20px
classDef lighterBoxStyle fill:#586069,stroke:#1b1f23,stroke-width:2,color:#ffffff,font-size:20px

%% User Entry Points
subgraph UserActions["User Actions"]
direction TB
class UserActions darkBackground
Deposit["Deposit SOL + Ed25519 Sig"]
Withdraw["Withdraw Request + Ed25519 Sig + Feed ID"]
end

%% Program Logic
subgraph ProgramFlow["Escrow Program"]
class ProgramFlow boxStyle

%% Signature Verification
subgraph SigVerification["Ed25519 Signature Verification"]
class SigVerification subBoxStyle
GetPrevIx["Get Previous Instruction"]
VerifyProgram["Verify Ed25519 Program ID"]

subgraph OffsetValidation["Signature Offset Validation"]
class OffsetValidation lighterBoxStyle
ValidatePK["Validate Public Key Offset"]
ValidateSig["Validate Signature Offset"]
ValidateMsg["Validate Message Data"]
VerifyIndices["Verify Instruction Indices Match"]
end
end

%% Main Operations
subgraph Operations["Program Operations"]
class Operations subBoxStyle

subgraph DepositFlow["Deposit Handler"]
class DepositFlow lighterBoxStyle
UpdateState["Update Escrow State:
- Set unlock_price
- Set escrow_amount"]
TransferToEscrow["Transfer SOL to Escrow Account"]
end

subgraph WithdrawFlow["Withdraw Handler"]
class WithdrawFlow lighterBoxStyle
GetPrice["Get Price from Pyth"]
PriceCheck["Check if price > unlock_price"]
TransferToUser["Transfer SOL to User"]
end
end
end

%% Flow Connections
Deposit --> GetPrevIx
Withdraw --> GetPrevIx
GetPrevIx --> VerifyProgram
VerifyProgram --> OffsetValidation
ValidatePK & ValidateSig & ValidateMsg --> VerifyIndices

VerifyIndices -->|"Signature Valid"| Operations
VerifyIndices -->|"Invalid"| Error["Return Signature Error"]

Operations --> DepositFlow
Operations --> WithdrawFlow

GetPrice --> PriceCheck
PriceCheck -->|"Price > Unlock Price"| TransferToUser
PriceCheck -->|"Price <= Unlock Price"| WithdrawError["Return Invalid Withdrawal Error"]

%% Apply Styles
class Deposit,Withdraw boxStyle
class GetPrevIx,VerifyProgram,Error,WithdrawError subBoxStyle
class UpdateState,TransferToEscrow,GetPrice,CheckAge,PriceCheck,TransferToUser lighterBoxStyle
```
32 changes: 32 additions & 0 deletions basics/solana-signature-verification/anchor/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"license": "ISC",
"scripts": {
"lint:fix": "prettier */*.ts \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.ts \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@coral-xyz/anchor": "^0.30.1",
"@noble/ed25519": "^1.6.0",
"@pythnetwork/hermes-client": "^1.3.1",
"@pythnetwork/pyth-solana-receiver": "^0.9.1",
"@solana-developers/helpers": "^2.7.0",
"@solana/wallet-adapter-react": "^0.15.35",
"@sqds/sdk": "^2.0.4",
"@switchboard-xyz/common": "^2.5.17",
"@switchboard-xyz/on-demand": "^1.2.63",
"@switchboard-xyz/solana.js": "^3.2.5",
"bs58": "^6.0.0",
"rpc-websockets": "7.11.0"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"ts-mocha": "^10.0.0",
"tsx": "^4.19.2",
"typescript": "^4.3.5"
}
}
Loading
Loading