Skip to content
Merged
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
1 change: 1 addition & 0 deletions target_chains/solana/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"programs/pyth-push-oracle",
"programs/pyth-solana-receiver",
"programs/core-bridge",
]

[features]
Expand Down
1 change: 0 additions & 1 deletion target_chains/solana/Cargo.lock

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

11 changes: 9 additions & 2 deletions target_chains/solana/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[workspace]
members = ["programs/*", "cli/", "program_simulator/", "common_test_utils"]

exclude = [
"programs/pyth-price-store",
"pyth_solana_receiver_sdk",
Expand All @@ -16,11 +15,19 @@ incremental = false
codegen-units = 1

[workspace.dependencies]
wormhole-core-bridge-solana = { git = "https://github.com/wormhole-foundation/wormhole", rev = "7bd40b595e22c5512dfaa2ed8e6d7441df743a69" }
wormhole-core-bridge-solana = { path = "./programs/core-bridge" }
wormhole-vaas-serde = "0.1.0"
wormhole-raw-vaas = { version = "0.1.3", features = [
"ruint",
"on-chain",
], default-features = false }
serde_wormhole = "0.1.0"
anchor-lang = "0.28.0"
anchor-client = "0.28.0"
wormhole-io = "0.1.0"
cfg-if = "1.0"
hex = "0.4.3"
ruint = "1.9.0"
solana-program = "1.16.20"
solana-program-test = "1.16.20"
solana-sdk = "1.16.20"
Expand Down
2 changes: 1 addition & 1 deletion target_chains/solana/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ wormhole-solana = { git = "https://github.com/guibescos/wormhole", branch = "rei
pythnet-sdk = { path = "../../../pythnet/pythnet_sdk", version = "2.0.0" }
wormhole-vaas-serde = { workspace = true }
serde_wormhole = { workspace = true }
hex = "0.4.3"
hex = { workspace = true }
borsh = "0.9.3" # Old version of borsh needed for wormhole-solana
wormhole-core-bridge-solana = { workspace = true }
pyth-solana-receiver-sdk = "0.6.1" # This is the highest version of pyth-solana-receiver-sdk that is compatible with the anchor-lang version used in the contract (0.28.0)
35 changes: 35 additions & 0 deletions target_chains/solana/programs/core-bridge/Cargo.toml
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Vendoring entire core-bridge program is a large addition — scope and test coverage

This PR vendors the entire Wormhole Core Bridge program (~4000+ lines of Rust) as a local dependency, replacing a git dependency. Per REVIEW.md's scope discipline rule, this is a very large addition. The commit message is simply "go", which provides no context. There are no tests included in this PR for the new code, and the README.md is empty. While the core-bridge code may have been tested upstream, the vendored version should have at minimum integration tests to verify it works correctly with the existing pyth-solana-receiver.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "wormhole-core-bridge-solana"
description = "Wormhole Core Bridge Program for Solana"
version = "0.0.1-alpha.5"
edition = "2021"
authors = ["Wormhole Contributors"]
license = "Apache-2.0"
homepage = "https://wormhole.com"
repository = "https://github.com/wormhole-foundation/wormhole"

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

[features]
default = ["mainnet", "cpi", "no-idl"]
anchor-debug = []
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
### A.K.A. Local Validator (via JS Integration Test) and Tilt
localnet = []
### A.K.A. Solana Mainnet-Beta
mainnet = []
### A.K.A. Solana Devnet
testnet = []

[dependencies]
wormhole-io.workspace = true
wormhole-raw-vaas.workspace = true
anchor-lang = { workspace = true, features = ["init-if-needed", "derive"] }
solana-program.workspace = true
hex.workspace = true
ruint.workspace = true
cfg-if.workspace = true
2 changes: 2 additions & 0 deletions target_chains/solana/programs/core-bridge/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
31 changes: 31 additions & 0 deletions target_chains/solana/programs/core-bridge/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Constants used by the Core Bridge Program. For integrators, necessary constants are re-exported
//! in the [sdk](crate::sdk) module.

use anchor_lang::prelude::constant;

/// Wormhole Chain (Network) ID for Solana.
#[constant]
pub const SOLANA_CHAIN: u16 = 1;

/// Seed for fee collector (Core Bridge's system account).
#[constant]
pub const FEE_COLLECTOR_SEED_PREFIX: &[u8] = b"fee_collector";

#[constant]
/// Seed for upgrade authority.
pub const UPGRADE_SEED_PREFIX: &[u8] = b"upgrade";

/// Seed for program emitters.
#[constant]
pub const PROGRAM_EMITTER_SEED_PREFIX: &[u8] = b"emitter";

/// The max payload size allowed for outbound messages is 30KB. Any messages outbound larger than
/// this size will be disallowed.
#[constant]
pub const MAX_MESSAGE_PAYLOAD_SIZE: usize = 30 * 1_024;

pub(crate) const GOVERNANCE_CHAIN: u16 = 1;

pub(crate) const GOVERNANCE_EMITTER: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
];
195 changes: 195 additions & 0 deletions target_chains/solana/programs/core-bridge/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
//! Errors that may arise when interacting with the Core Bridge 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.
///
/// NOTE: All of these error codes when triggered are offset by `ERROR_CODE_OFFSET` (6000). So for
/// example, `U64Overflow` will return as 6006.
#[error_code]
pub enum CoreBridgeError {
#[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("InvalidSignatureSet")]
InvalidSignatureSet = 0x200,

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

#[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,

#[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("DuplicateGuardianAddress")]
DuplicateGuardianAddress = 0x1030,

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

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

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

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

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

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

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

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

#[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,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! A set of structs mirroring the structs deriving [Accounts](anchor_lang::prelude::Accounts),
//! where each field is a [Pubkey]. This is useful for specifying self for a client.
//!
//! NOTE: This is similar to how [accounts](mod@crate::accounts) is generated via Anchor's
//! [program][anchor_lang::prelude::program] macro.

use anchor_lang::{prelude::Pubkey, ToAccountMetas};
use solana_program::instruction::AccountMeta;

/// Context to post a new Core Bridge message.
pub struct PostMessage {
pub config: Pubkey,
pub message: Pubkey,
pub emitter: Option<Pubkey>,
pub emitter_sequence: Pubkey,
pub payer: Pubkey,
pub fee_collector: Option<Pubkey>,
pub system_program: Pubkey,
}

impl ToAccountMetas for PostMessage {
fn to_account_metas(&self, message_is_signer: Option<bool>) -> Vec<AccountMeta> {
// Using `message_is_signer` above is a hack. But because we do not want to return a result,
// we assume that the caller of this is passing in whether a message is a signer, which is
// the case when a new message account is created when someone invokes this instruction.
// Otherwise the message was already prepared so it does not have to be a signer.

// If the emitter is None, we do not require it to be a signer.
let (emitter, emitter_is_signer) = match self.emitter {
Some(emitter) => (emitter, true),
None => (crate::ID, false),
};

vec![
AccountMeta::new_readonly(self.config, false),
AccountMeta::new(self.message, message_is_signer.unwrap_or(true)),
AccountMeta::new_readonly(emitter, emitter_is_signer),
AccountMeta::new(self.emitter_sequence, false),
AccountMeta::new(self.payer, true),
AccountMeta::new(self.fee_collector.unwrap_or(crate::ID), false),
AccountMeta::new_readonly(crate::ID, false), // _clock
AccountMeta::new_readonly(self.system_program, false),
]
}
}

/// Context to post a new or reuse an existing Core Bridge message.
pub struct PostMessageUnreliable {
pub config: Pubkey,
pub message: Pubkey,
pub emitter: Pubkey,
pub emitter_sequence: Pubkey,
pub payer: Pubkey,
pub fee_collector: Option<Pubkey>,
pub system_program: Pubkey,
}

impl ToAccountMetas for PostMessageUnreliable {
fn to_account_metas(&self, _is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![
AccountMeta::new_readonly(self.config, false),
AccountMeta::new(self.message, true),
AccountMeta::new_readonly(self.emitter, true),
AccountMeta::new(self.emitter_sequence, false),
AccountMeta::new(self.payer, true),
AccountMeta::new(self.fee_collector.unwrap_or(crate::ID), false),
AccountMeta::new_readonly(crate::ID, false), // _clock
AccountMeta::new_readonly(self.system_program, false),
]
}
}
Loading
Loading