Skip to content

Commit

Permalink
tidying
Browse files Browse the repository at this point in the history
  • Loading branch information
evan-gray committed Jul 18, 2024
1 parent b8d731c commit 2071423
Show file tree
Hide file tree
Showing 12 changed files with 660 additions and 480 deletions.
37 changes: 26 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
## Queries Verification on Solana PoC
# Queries Verification on Solana PoC

This is a demo of verifying and parsing [Wormhole Queries](https://wormhole.com/queries/) on Solana.

This project was made with [Anchor](https://www.anchor-lang.com/).

Learn more about developing with Queries in [the docs](https://docs.wormhole.com/wormhole/queries/getting-started).

> N.B. This is a work-in-progress provided for example purposes only.
## Accounts

- [x] Verify mainnet queries using an active mainnet core bridge guardian set account
- [x] Verify mocked queries using a mock core bridge guardian set account on a mainnet address
- [x] Validate a query result passed via instruction data
- [x] Rust parsing for all query requests and responses
- [x] Allow for cleanup of signature set accounts
- [GuardianSignatures](programs/solana-world-id-program/src/state/guardian_signatures.rs) stores unverified guardian signatures for subsequent verification. These are created with `post_signatures` in service of verifying a root via Queries and closed when that root is verified with `verify_query` or can be explicitly closed with `close_signatures` by the initial payer.

- [ ] Verify testnet queries using an active testnet core bridge guardian set account
- [ ] Validate a query result passed via account
## Instructions

## Tests
- [post_signatures](programs/example-queries-solana-verify/src/instructions/post_signatures.rs) posts unverified guardian signatures for verification during `update_root_with_query`.
- [verify_query](programs/example-queries-solana-verify/src/instructions/verify_query.rs) with a Query response and `GuardianSignatures` account, verifies the signatures against an active guardian set and logs the Query response. This is where you would add additional verification relevant to your use case and process the result.
- [close_signatures](programs/example-queries-solana-verify/src/instructions/close_signatures.rs) allows the initial payer to close a `GuardianSignatures` account in case the query was invalid.

To run the tests, `anchor test`.
## Testing

```bash
anchor test
```

## Building

### Wormhole Testnet / Solana Devnet

```bash
anchor build -- --no-default-features --features testnet
```

### Mainnet

```bash
anchor build
```

---

Expand Down
10 changes: 6 additions & 4 deletions programs/example-queries-solana-verify/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ crate-type = ["cdylib", "lib"]
name = "example_queries_solana_verify"

[features]
idl-build = ["anchor-lang/idl-build"]
default = ["mainnet"]
cpi = ["no-entrypoint"]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
mainnet = ["wormhole-solana-consts/mainnet"]
testnet = ["wormhole-solana-consts/testnet"]
idl-build = ["anchor-lang/idl-build"]

[dependencies]
anchor-lang = { version = "0.30.1", features = ["init-if-needed"] }
wormhole-raw-vaas = {version = "0.3.0-alpha.1"}
wormhole-solana-consts = {version = "0.3.0-alpha.1", features = ["mainnet"] }
wormhole-solana-consts = {version = "0.3.0-alpha.1" }
wormhole-solana-utils = {version = "0.3.0-alpha.1"}
wormhole-query-sdk = { git = "https://github.com/wormholelabs-xyz/wormhole-query-sdk-rust", version = "0.0.1", rev = "0f34cb470f4e3137b53aa91adcbb0c7def280925" }
196 changes: 17 additions & 179 deletions programs/example-queries-solana-verify/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,199 +1,37 @@
//! Errors that may arise when interacting with the Core Bridge Program.
//! Errors that may arise when interacting with the Example Queries Solana Verify Program.
use anchor_lang::prelude::error_code;

/// Errors relevant to Core Bridge's malfunction.
///
/// * \>= 0x0 -- General program related.
/// * \>= 0x10 -- General Core Bridge.
/// * \>= 0x20 -- General Core Bridge Governance.
/// * \>= 0x100 -- Legacy Post Message.
/// * \>= 0x200 -- Legacy Post VAA.
/// * \>= 0x300 -- Legacy Set Message Fee.
/// * \>= 0x400 -- Legacy Transfer Fees.
/// * \>= 0x500 -- Legacy Upgrade Contract.
/// * \>= 0x600 -- Legacy Guardian Set Update.
/// * \>= 0x700 -- Legacy Verify Signatures.
/// * \>= 0x800 -- Legacy Post Message Unreliable.
/// * \>= 0x1000 -- Core Bridge Anchor Instruction.
/// * \>= 0x2000 -- Core Bridge SDK.
/// * \>= 0x3000 -- Query Response SDK.
/// * \>= 0x100 -- Query Verification.
///
/// NOTE: All of these error codes when triggered are offset by `ERROR_CODE_OFFSET` (6000). So for
/// example, `U64Overflow` will return as 6006.
/// example, `InvalidMessageHash` will return as 6256.
#[error_code]
pub enum ExampleQueriesSolanaVerifyError {
#[msg("InvalidInstructionArgument")]
InvalidInstructionArgument = 0x2,

#[msg("AccountNotZeroed")]
AccountNotZeroed = 0x3,

#[msg("InvalidDataConversion")]
InvalidDataConversion = 0x4,

#[msg("U64Overflow")]
U64Overflow = 0x6,

#[msg("InvalidComputeSize")]
InvalidComputeSize = 0x8,

#[msg("InvalidChain")]
InvalidChain = 0x10,

#[msg("InvalidGovernanceEmitter")]
InvalidGovernanceEmitter = 0x20,

#[msg("InvalidGovernanceAction")]
InvalidGovernanceAction = 0x22,

#[msg("LatestGuardianSetRequired")]
LatestGuardianSetRequired = 0x24,

#[msg("GovernanceForAnotherChain")]
GovernanceForAnotherChain = 0x26,

#[msg("InvalidGovernanceVaa")]
InvalidGovernanceVaa = 0x28,

#[msg("InsufficientFees")]
InsufficientFees = 0x100,

#[msg("EmitterMismatch")]
EmitterMismatch = 0x102,

#[msg("NotReadyForPublishing")]
NotReadyForPublishing = 0x104,

#[msg("InvalidPreparedMessage")]
InvalidPreparedMessage = 0x106,

#[msg("ExecutableEmitter")]
ExecutableEmitter = 0x108,

#[msg("LegacyEmitter")]
LegacyEmitter = 0x10a,
#[msg("WriteAuthorityMismatch")]
WriteAuthorityMismatch = 0x100,

#[msg("InvalidSignatureSet")]
InvalidSignatureSet = 0x200,
#[msg("GuardianSetExpired")]
GuardianSetExpired = 0x101,

#[msg("InvalidMessageHash")]
InvalidMessageHash = 0x202,
InvalidMessageHash = 0x102,

#[msg("NoQuorum")]
NoQuorum,

#[msg("MessageMismatch")]
MessageMismatch = 0x204,

#[msg("NotEnoughLamports")]
NotEnoughLamports = 0x400,

#[msg("InvalidFeeRecipient")]
InvalidFeeRecipient = 0x402,

#[msg("ImplementationMismatch")]
ImplementationMismatch = 0x500,

#[msg("InvalidGuardianSetIndex")]
InvalidGuardianSetIndex = 0x600,

#[msg("GuardianSetMismatch")]
GuardianSetMismatch = 0x700,

#[msg("InstructionAtWrongIndex")]
InstructionAtWrongIndex = 0x702,

#[msg("EmptySigVerifyInstruction")]
EmptySigVerifyInstruction = 0x703,

#[msg("InvalidSigVerifyInstruction")]
InvalidSigVerifyInstruction = 0x704,
NoQuorum = 0x103,

#[msg("GuardianSetExpired")]
GuardianSetExpired = 0x706,

#[msg("InvalidGuardianKeyRecovery")]
InvalidGuardianKeyRecovery = 0x708,

#[msg("SignerIndicesMismatch")]
SignerIndicesMismatch = 0x70a,

#[msg("PayloadSizeMismatch")]
PayloadSizeMismatch = 0x800,

#[msg("ZeroGuardians")]
ZeroGuardians = 0x1010,

#[msg("GuardianZeroAddress")]
GuardianZeroAddress = 0x1020,
#[msg("InvalidGuardianIndexNonIncreasing")]
InvalidGuardianIndexNonIncreasing = 0x104,

#[msg("DuplicateGuardianAddress")]
DuplicateGuardianAddress = 0x1030,

#[msg("MessageAlreadyPublished")]
MessageAlreadyPublished = 0x1040,

#[msg("VaaWritingDisallowed")]
VaaWritingDisallowed = 0x1050,

#[msg("VaaAlreadyVerified")]
VaaAlreadyVerified = 0x1060,

#[msg("InvalidGuardianIndex")]
InvalidGuardianIndex = 0x1070,
#[msg("InvalidGuardianIndexOutOfRange")]
InvalidGuardianIndexOutOfRange = 0x105,

#[msg("InvalidSignature")]
InvalidSignature = 0x1080,

#[msg("UnverifiedVaa")]
UnverifiedVaa = 0x10a0,

#[msg("VaaStillProcessing")]
VaaStillProcessing = 0x10a2,

#[msg("InWritingStatus")]
InWritingStatus = 0x10a4,
InvalidSignature = 0x106,

#[msg("NotInWritingStatus")]
NotInWritingStatus = 0x10a6,

#[msg("InvalidMessageStatus")]
InvalidMessageStatus = 0x10a8,

#[msg("HashNotComputed")]
HashNotComputed = 0x10aa,

#[msg("InvalidVaaVersion")]
InvalidVaaVersion = 0x10ac,

#[msg("InvalidCreatedAccountSize")]
InvalidCreatedAccountSize = 0x10ae,

#[msg("DataOverflow")]
DataOverflow = 0x10b0,

#[msg("ExceedsMaxPayloadSize (30KB)")]
ExceedsMaxPayloadSize = 0x10b2,

#[msg("CannotParseVaa")]
CannotParseVaa = 0x10b4,

#[msg("EmitterAuthorityMismatch")]
EmitterAuthorityMismatch = 0x10b6,

#[msg("InvalidProgramEmitter")]
InvalidProgramEmitter = 0x10b8,

#[msg("WriteAuthorityMismatch")]
WriteAuthorityMismatch = 0x10ba,

#[msg("PostedVaaPayloadTooLarge")]
PostedVaaPayloadTooLarge = 0x10bc,

#[msg("ExecutableDisallowed")]
ExecutableDisallowed = 0x10be,
#[msg("InvalidGuardianKeyRecovery")]
InvalidGuardianKeyRecovery = 0x107,

#[msg("FailedToParseResponse")]
FailedToParseResponse = 0x3000,
FailedToParseResponse = 0x110,
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,26 @@ pub struct PostSignatures<'info> {
#[account(mut)]
payer: Signer<'info>,

/// Stores signatures for later use by verify_query
#[account(
init_if_needed,
payer = payer,
space = GuardianSignatures::compute_size(usize::from(total_signatures))
space = 8 + GuardianSignatures::compute_size(usize::from(total_signatures))
)]
guardian_signatures: Account<'info, GuardianSignatures>,

system_program: Program<'info, System>,
}

/// Creates or appends to a GuardianSignatures account for subsequent use by verify_query.
/// This is necessary as the Wormhole query response (220 bytes)
/// and 13 guardian signatures (a quorum of the current 19 mainnet guardians, 66 bytes each)
/// alongside the required accounts is larger than the transaction size limit on Solana (1232 bytes).
///
/// This instruction allows for the initial payer to append additional signatures to the account by calling the instruction again.
/// This may be necessary if a quorum of signatures from the current guardian set grows larger than can fit into a single transaction.
///
/// The GuardianSignatures account can be closed by anyone with a successful update_root_with_query instruction
/// or by the initial payer via close_signatures, either of which will refund the initial payer.
pub fn post_signatures(
ctx: Context<PostSignatures>,
mut guardian_signatures: Vec<[u8; 66]>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@ use wormhole_solana_consts::CORE_BRIDGE_PROGRAM_ID;
#[derive(Accounts)]
#[instruction(_bytes: Vec<u8>, guardian_set_index: u32)]
pub struct VerifyQuery<'info> {
/// Guardian set used for signature verification (whose index should agree with the signature
/// set account's guardian set index).
/// Guardian set used for signature verification.
#[account(
seeds = [
WormholeGuardianSet::SEED_PREFIX,
guardian_set_index.to_be_bytes().as_ref()
],
bump,
seeds::program = CORE_BRIDGE_PROGRAM_ID
)]
],
bump,
seeds::program = CORE_BRIDGE_PROGRAM_ID
)]
guardian_set: Account<'info, WormholeGuardianSet>,

/// Stores unverified guardian signatures as they are too large to fit in the instruction data.
Expand All @@ -41,7 +40,7 @@ pub struct VerifyQuery<'info> {

impl<'info> VerifyQuery<'info> {
pub fn constraints(ctx: &Context<Self>, bytes: &Vec<u8>) -> Result<()> {
let guardian_set = ctx.accounts.guardian_set.clone().into_inner();
let guardian_set = &ctx.accounts.guardian_set;

// Check that the guardian set is still active.
let timestamp = Clock::get()?
Expand Down Expand Up @@ -91,14 +90,14 @@ impl<'info> VerifyQuery<'info> {
if let Some(last_index) = last_guardian_index {
require!(
index > last_index,
ExampleQueriesSolanaVerifyError::InvalidGuardianIndex
ExampleQueriesSolanaVerifyError::InvalidGuardianIndexNonIncreasing
);
}

// Does this guardian index exist in this guardian set?
let guardian_pubkey = guardian_keys
.get(index)
.ok_or_else(|| error!(ExampleQueriesSolanaVerifyError::InvalidGuardianIndex))?;
let guardian_pubkey = guardian_keys.get(index).ok_or_else(|| {
error!(ExampleQueriesSolanaVerifyError::InvalidGuardianIndexOutOfRange)
})?;

// Now verify that the signature agrees with the expected Guardian's pubkey.
verify_guardian_signature(&sig, guardian_pubkey, digest.as_ref())?;
Expand Down
Loading

0 comments on commit 2071423

Please sign in to comment.