Skip to content
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

Implemented Example Program Using Ed25519 Signature Verification #352

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ members = [
"basics/create-account/native/program",
"basics/create-account/anchor/programs/create-system-account",
"basics/cross-program-invocation/anchor/programs/*",
"basics/ed25519-verification/native/program",
"basics/ed25519-verification/anchor/programs/*",
"basics/hello-solana/native/program",
"basics/hello-solana/anchor/programs/*",
"basics/pda-rent-payer/native/program",
Expand Down
128 changes: 128 additions & 0 deletions basics/ed25519-verification/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Ed25519 Signature Verification for Custodied Funds

This example demonstrates how to implement Ed25519 signature verification to manage custodied funds on Solana. The program verifies Ed25519 signatures before allowing transfers from custodial accounts.

## Overview

The example shows how to:
- Verify Ed25519 signatures using Solana's native Ed25519 program
- Transfer funds from custodial accounts after signature verification
- Implement secure authorization checks
- Handle signature verification errors

## Quick Start

The example is implemented in multiple frameworks:

### Native
```bash
cd native
pnpm install
pnpm build-and-test
```

### Anchor
```bash
cd anchor
pnpm install
anchor build
pnpm test
```

### Steel
```bash
cd steel
pnpm install
steel build
steel test
```

### Poseidon (TypeScript)
```bash
cd poseidon
pnpm install
pnpm test
```

## Program Structure

The program consists of the following key components:

1. **Signature Verification**: Uses Solana's Ed25519 program to verify signatures
2. **Fund Transfer**: Handles secure transfer of funds after verification
3. **Account Validation**: Ensures proper account permissions and ownership

### Account Structure
- Custodial Account: Holds the funds
- Recipient: Account receiving the funds
- Signer: Account authorized to initiate transfers
- Ed25519 Program: Solana's native signature verification program

### Instruction Data
```rust
pub struct TransferInstruction {
signature: [u8; 64], // Ed25519 signature
public_key: [u8; 32], // Signer's public key
amount: u64, // Transfer amount in lamports
message: Vec<u8>, // Message that was signed
}
```

## Usage

### Creating a Transfer

```typescript
// Create and sign the transfer message
const message = Buffer.from(`Transfer ${amount} lamports to ${recipient.toBase58()}`);
const signature = await sign(message, signerKeypair.secretKey.slice(0, 32));

// Create the instruction
const instruction = new TransactionInstruction({
keys: [
{ pubkey: custodialAccount, isSigner: false, isWritable: true },
{ pubkey: recipient, isSigner: false, isWritable: true },
{ pubkey: signerKeypair.publicKey, isSigner: true, isWritable: false },
{ pubkey: ed25519ProgramId, isSigner: false, isWritable: false },
],
programId,
data: Buffer.concat([signature, publicKey, amount, message]),
});
```

### Security Considerations

1. **Signature Verification**: Always verify signatures before transferring funds
2. **Account Validation**: Check account ownership and permissions
3. **Error Handling**: Properly handle all error cases
4. **Amount Validation**: Verify sufficient funds before transfer

## Testing

Each implementation includes comprehensive tests demonstrating:
- Successful signature verification and transfer
- Handling of invalid signatures
- Error cases for insufficient funds
- Account permission checks

## Framework-Specific Details

### Native Implementation
- Direct Solana program implementation
- Manual account and instruction handling
- Bankrun tests for verification

### Anchor Implementation
- Uses Anchor's account validation
- Structured instruction handling
- Type-safe client interface

### Steel Implementation
- Separated API and program logic
- Steel-specific optimizations
- Integrated testing tools

### Poseidon Implementation
- TypeScript client implementation
- Modern Solana practices
- Versioned transaction support
16 changes: 16 additions & 0 deletions basics/ed25519-verification/anchor/Anchor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[features]
seeds = false
skip-lint = false

[programs.localnet]
ed25519_custodial = "Ed25519CustodiaLXXXXXXXXXXXXXXXXXXXXXXXXXXX"

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

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

[scripts]
test = "pnpm ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
23 changes: 23 additions & 0 deletions basics/ed25519-verification/anchor/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"scripts": {
"test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts",
"build": "anchor build",
"deploy": "anchor deploy"
},
"dependencies": {
"@coral-xyz/anchor": "^0.30.0",
"@solana/web3.js": "^1.95.2",
"@noble/ed25519": "^1.7.1"
},
"devDependencies": {
"anchor-bankrun": "^0.4.0",
"solana-bankrun": "^0.3.0",
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
}
}
20 changes: 20 additions & 0 deletions basics/ed25519-verification/anchor/programs/ed25519-/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "ed25519-custodial"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "ed25519_custodial"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.30.0"
solana-program = "1.16"
60 changes: 60 additions & 0 deletions basics/ed25519-verification/anchor/programs/ed25519-/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use anchor_lang::prelude::*;
use anchor_lang::solana_program::ed25519_program;

declare_id!("Ed25519CustodiaLXXXXXXXXXXXXXXXXXXXXXXXXXXX");

#[program]
pub mod ed25519_custodial {
use super::*;

pub fn transfer(
ctx: Context<Transfer>,
signature: [u8; 64],
public_key: [u8; 32],
message: Vec<u8>,
amount: u64,
) -> Result<()> {
// Verify Ed25519 signature
let verification_instruction = ed25519_program::instruction::new_ed25519_instruction(
&public_key,
&message,
&signature,
);

// Invoke the Ed25519 program to verify the signature
solana_program::program::invoke(
&verification_instruction,
&[ctx.accounts.ed25519_program.to_account_info()],
)?;

msg!("Signature verification successful!");

// Transfer funds
**ctx.accounts.custodial_account.try_borrow_mut_lamports()? = ctx
.accounts
.custodial_account
.lamports()
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;

**ctx.accounts.recipient.try_borrow_mut_lamports()? = ctx
.accounts
.recipient
.lamports()
.checked_add(amount)
.ok_or(ProgramError::Overflow)?;

Ok(())
}
}

#[derive(Accounts)]
pub struct Transfer<'info> {
#[account(mut)]
pub custodial_account: AccountInfo<'info>,
#[account(mut)]
pub recipient: AccountInfo<'info>,
pub signer: Signer<'info>,
/// CHECK: This is the Ed25519 program ID
pub ed25519_program: AccountInfo<'info>,
}
56 changes: 56 additions & 0 deletions basics/ed25519-verification/anchor/tests/ed25519-custodial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { describe, it } from 'node:test';
import * as anchor from '@coral-xyz/anchor';
import { Program } from '@coral-xyz/anchor';
import { sign } from '@noble/ed25519';
import { Keypair, PublicKey } from '@solana/web3.js';
import { BankrunProvider } from 'anchor-bankrun';
import { startAnchor } from 'solana-bankrun';
import type { Ed25519Custodial } from '../target/types/ed25519_custodial';

describe('Ed25519 Custodial', async () => {
const context = await startAnchor(
'',
[
{
name: 'ed25519_custodial',
programId: new PublicKey('Ed25519CustodiaLXXXXXXXXXXXXXXXXXXXXXXXXXXX'),
},
],
[],
);
const provider = new BankrunProvider(context);
anchor.setProvider(provider);

const program = anchor.workspace.Ed25519Custodial as Program<Ed25519Custodial>;

it('Verifies signature and transfers funds', async () => {
const custodialAccount = Keypair.generate();
const recipient = Keypair.generate();
const signerKeypair = Keypair.generate();
const amount = 1000000; // lamports

// Message to sign
const message = Buffer.from(`Transfer ${amount} lamports to ${recipient.publicKey.toBase58()}`);

// Sign the message with Ed25519
const signature = await sign(message, signerKeypair.secretKey.slice(0, 32));

try {
await program.methods
.transfer(Array.from(signature), Array.from(signerKeypair.publicKey.toBytes()), Array.from(message), new anchor.BN(amount))
.accounts({
custodialAccount: custodialAccount.publicKey,
recipient: recipient.publicKey,
signer: signerKeypair.publicKey,
ed25519Program: new PublicKey('Ed25519SigVerify111111111111111111111111111'),
})
.signers([signerKeypair])
.rpc();

console.log('Transaction processed successfully');
} catch (error) {
console.error('Error:', error);
throw error;
}
});
});
8 changes: 8 additions & 0 deletions basics/ed25519-verification/native/cicd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

# This script is for quick building & deploying of the program.
# It also serves as a reference for the commands used for building & deploying Solana programs.
# Run this bad boy with "bash cicd.sh" or "./cicd.sh"

cargo build-sbf --manifest-path=./program/Cargo.toml --bpf-out-dir=./program/target/so
solana program deploy ./program/target/so/program.so
21 changes: 21 additions & 0 deletions basics/ed25519-verification/native/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"scripts": {
"test": "pnpm ts-mocha -p ./tests/tsconfig.test.json -t 1000000 ./tests/test.ts",
"build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test",
"build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so",
"deploy": "solana program deploy ./program/target/so/program.so"
},
"dependencies": {
"@solana/web3.js": "^1.95.2",
"@noble/ed25519": "^1.7.1",
"buffer": "^6.0.3",
"solana-bankrun": "^0.3.0"
},
"devDependencies": {
"@types/node": "^18.0.0",
"typescript": "^4.7.4",
"ts-mocha": "^10.0.0",
"@types/mocha": "^9.0.0",
"mocha": "^9.0.3"
}
}
Loading