Abehjati/repo-improvement (#395)
* Use a new rustfmt file * Run cosmwasm action only when needed * Remove pre-commit from remote-executor action * Fix formatting * Run precommit for all files As previous PR was merged without pre-commit
This commit is contained in:
parent
c3fefc78fa
commit
becc216853
|
@ -1,14 +1,14 @@
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- ethereum/**
|
- ethereum/**
|
||||||
- third_party/pyth/xc-governance-sdk-js/**
|
- third_party/pyth/xc-governance-sdk-js/**
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- ethereum/**
|
- ethereum/**
|
||||||
- third_party/pyth/xc-governance-sdk-js/**
|
- third_party/pyth/xc-governance-sdk-js/**
|
||||||
|
|
||||||
name: Ethereum Contract
|
name: Ethereum Contract
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ jobs:
|
||||||
uses: foundry-rs/foundry-toolchain@v1
|
uses: foundry-rs/foundry-toolchain@v1
|
||||||
with:
|
with:
|
||||||
version: nightly
|
version: nightly
|
||||||
|
|
||||||
- name: Install Forge dependencies
|
- name: Install Forge dependencies
|
||||||
run: npm run install-forge-deps
|
run: npm run install-forge-deps
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,15 @@ name: Pyth CosmWasm Contract
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- cosmwasm/**
|
||||||
|
- third_party/pyth/p2w-sdk/rust/**
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
paths:
|
||||||
|
- cosmwasm/**
|
||||||
|
- third_party/pyth/p2w-sdk/rust/**
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
|
@ -17,7 +17,6 @@ jobs:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
- uses: pre-commit/action@v2.0.3
|
|
||||||
- name: Install Solana
|
- name: Install Solana
|
||||||
run: |
|
run: |
|
||||||
sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
|
sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
|
||||||
|
|
|
@ -17,10 +17,10 @@ repos:
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
# Hooks for the remote executor
|
# Hooks for the remote executor
|
||||||
- id: cargo-fmt-executor
|
- id: cargo-fmt-executor-remote-executor
|
||||||
name: Cargo format executor
|
name: Cargo format executor for remote executor
|
||||||
language: "rust"
|
language: "rust"
|
||||||
entry: cargo +nightly fmt --manifest-path ./pythnet/remote-executor/Cargo.toml --all
|
entry: cargo +nightly fmt --manifest-path ./pythnet/remote-executor/Cargo.toml --all -- --config-path rustfmt.toml
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
files: pythnet/remote-executor/
|
files: pythnet/remote-executor/
|
||||||
- id: cargo-clippy-executor
|
- id: cargo-clippy-executor
|
||||||
|
@ -30,9 +30,16 @@ repos:
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
files: pythnet/remote-executor/
|
files: pythnet/remote-executor/
|
||||||
# Hooks for the attester
|
# Hooks for the attester
|
||||||
- id: cargo-fmt-executor
|
- id: cargo-fmt-executor-attester
|
||||||
name: Cargo format executor
|
name: Cargo format executor for attester
|
||||||
language: "rust"
|
language: "rust"
|
||||||
entry: cargo +nightly fmt --manifest-path ./solana/pyth2wormhole/Cargo.toml --all
|
entry: cargo +nightly fmt --manifest-path ./solana/pyth2wormhole/Cargo.toml --all -- --config-path rustfmt.toml
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
files: solana/pyth2wormhole/
|
files: solana/pyth2wormhole/
|
||||||
|
# Hooks for cosmwasm contract
|
||||||
|
- id: cargo-fmt-executor-cosmwasm
|
||||||
|
name: Cargo format executor form cosmwasm contract
|
||||||
|
language: "rust"
|
||||||
|
entry: cargo +nightly fmt --manifest-path ./cosmwasm/Cargo.toml --all -- --config-path rustfmt.toml
|
||||||
|
pass_filenames: false
|
||||||
|
files: cosmwasm/
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
use std::collections::HashSet;
|
use {
|
||||||
|
crate::{
|
||||||
use cosmwasm_std::{
|
error::PythContractError,
|
||||||
entry_point,
|
msg::{
|
||||||
to_binary,
|
ExecuteMsg,
|
||||||
Binary,
|
InstantiateMsg,
|
||||||
Deps,
|
MigrateMsg,
|
||||||
DepsMut,
|
PriceFeedResponse,
|
||||||
Env,
|
QueryMsg,
|
||||||
MessageInfo,
|
},
|
||||||
QueryRequest,
|
state::{
|
||||||
Response,
|
config,
|
||||||
StdResult,
|
config_read,
|
||||||
Timestamp,
|
price_info,
|
||||||
WasmQuery,
|
price_info_read,
|
||||||
|
ConfigInfo,
|
||||||
|
PriceInfo,
|
||||||
|
PythDataSource,
|
||||||
|
VALID_TIME_PERIOD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cosmwasm_std::{
|
||||||
|
entry_point,
|
||||||
|
to_binary,
|
||||||
|
Binary,
|
||||||
|
Deps,
|
||||||
|
DepsMut,
|
||||||
|
Env,
|
||||||
|
MessageInfo,
|
||||||
|
QueryRequest,
|
||||||
|
Response,
|
||||||
|
StdResult,
|
||||||
|
Timestamp,
|
||||||
|
WasmQuery,
|
||||||
|
},
|
||||||
|
p2w_sdk::BatchPriceAttestation,
|
||||||
|
pyth_sdk_cw::{
|
||||||
|
PriceFeed,
|
||||||
|
PriceIdentifier,
|
||||||
|
PriceStatus,
|
||||||
|
ProductIdentifier,
|
||||||
|
},
|
||||||
|
std::collections::HashSet,
|
||||||
|
wormhole::{
|
||||||
|
msg::QueryMsg as WormholeQueryMsg,
|
||||||
|
state::ParsedVAA,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use pyth_sdk_cw::{
|
|
||||||
PriceFeed,
|
|
||||||
PriceIdentifier,
|
|
||||||
PriceStatus,
|
|
||||||
ProductIdentifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::msg::{
|
|
||||||
ExecuteMsg,
|
|
||||||
InstantiateMsg,
|
|
||||||
MigrateMsg,
|
|
||||||
PriceFeedResponse,
|
|
||||||
QueryMsg,
|
|
||||||
};
|
|
||||||
use crate::state::{
|
|
||||||
config,
|
|
||||||
config_read,
|
|
||||||
price_info,
|
|
||||||
price_info_read,
|
|
||||||
ConfigInfo,
|
|
||||||
PriceInfo,
|
|
||||||
PythDataSource,
|
|
||||||
VALID_TIME_PERIOD,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::error::PythContractError;
|
|
||||||
|
|
||||||
use p2w_sdk::BatchPriceAttestation;
|
|
||||||
|
|
||||||
use wormhole::msg::QueryMsg as WormholeQueryMsg;
|
|
||||||
use wormhole::state::ParsedVAA;
|
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
|
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
|
||||||
Ok(Response::new())
|
Ok(Response::new())
|
||||||
|
@ -303,26 +303,27 @@ pub fn query_price_feed(deps: Deps, env: Env, address: &[u8]) -> StdResult<Price
|
||||||
price_feed: price_info.price_feed,
|
price_feed: price_info.price_feed,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(_) => Err(PythContractError::PriceFeedNotFound)?
|
Err(_) => Err(PythContractError::PriceFeedNotFound)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use cosmwasm_std::testing::{
|
use {
|
||||||
mock_dependencies,
|
super::*,
|
||||||
mock_env,
|
cosmwasm_std::{
|
||||||
mock_info,
|
testing::{
|
||||||
MockApi,
|
mock_dependencies,
|
||||||
MockQuerier,
|
mock_env,
|
||||||
MockStorage,
|
mock_info,
|
||||||
|
MockApi,
|
||||||
|
MockQuerier,
|
||||||
|
MockStorage,
|
||||||
|
},
|
||||||
|
Addr,
|
||||||
|
OwnedDeps,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use cosmwasm_std::{
|
|
||||||
Addr,
|
|
||||||
OwnedDeps,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn setup_test() -> (OwnedDeps<MockStorage, MockApi, MockQuerier>, Env) {
|
fn setup_test() -> (OwnedDeps<MockStorage, MockApi, MockQuerier>, Env) {
|
||||||
(mock_dependencies(), mock_env())
|
(mock_dependencies(), mock_env())
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use cosmwasm_std::StdError;
|
use {
|
||||||
use thiserror::Error;
|
cosmwasm_std::StdError,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum PythContractError {
|
pub enum PythContractError {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use cosmwasm_std::Binary;
|
use {
|
||||||
use schemars::JsonSchema;
|
crate::state::PythDataSource,
|
||||||
use serde::{
|
cosmwasm_std::Binary,
|
||||||
Deserialize,
|
schemars::JsonSchema,
|
||||||
Serialize,
|
serde::{
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::state::PythDataSource;
|
|
||||||
|
|
||||||
type HumanAddr = String;
|
type HumanAddr = String;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
use std::collections::HashSet;
|
use {
|
||||||
use std::time::Duration;
|
cosmwasm_std::{
|
||||||
|
Addr,
|
||||||
use pyth_sdk_cw::PriceFeed;
|
Binary,
|
||||||
use schemars::JsonSchema;
|
Storage,
|
||||||
use serde::{
|
Timestamp,
|
||||||
Deserialize,
|
},
|
||||||
Serialize,
|
cosmwasm_storage::{
|
||||||
};
|
bucket,
|
||||||
|
bucket_read,
|
||||||
use cosmwasm_std::{
|
singleton,
|
||||||
Addr,
|
singleton_read,
|
||||||
Binary,
|
Bucket,
|
||||||
Storage,
|
ReadonlyBucket,
|
||||||
Timestamp,
|
ReadonlySingleton,
|
||||||
};
|
Singleton,
|
||||||
|
},
|
||||||
use cosmwasm_storage::{
|
pyth_sdk_cw::PriceFeed,
|
||||||
bucket,
|
schemars::JsonSchema,
|
||||||
bucket_read,
|
serde::{
|
||||||
singleton,
|
Deserialize,
|
||||||
singleton_read,
|
Serialize,
|
||||||
Bucket,
|
},
|
||||||
ReadonlyBucket,
|
std::{
|
||||||
ReadonlySingleton,
|
collections::HashSet,
|
||||||
Singleton,
|
time::Duration,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static CONFIG_KEY: &[u8] = b"config";
|
pub static CONFIG_KEY: &[u8] = b"config";
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
# Merge similar crates together to avoid multiple use statements.
|
|
||||||
imports_granularity = "Module"
|
|
||||||
|
|
||||||
# Consistency in formatting makes tool based searching/editing better.
|
|
||||||
empty_item_single_line = false
|
|
||||||
|
|
||||||
# Easier editing when arbitrary mixed use statements do not collapse.
|
|
||||||
imports_layout = "Vertical"
|
|
||||||
|
|
||||||
# Default rustfmt formatting of match arms with branches is awful.
|
|
||||||
match_arm_leading_pipes = "Preserve"
|
|
||||||
|
|
||||||
# Align Fields
|
|
||||||
enum_discrim_align_threshold = 80
|
|
||||||
struct_field_align_threshold = 80
|
|
||||||
|
|
||||||
# Allow up to two blank lines for grouping.
|
|
||||||
blank_lines_upper_bound = 2
|
|
||||||
|
|
||||||
# Wrap comments
|
|
||||||
comment_width = 120
|
|
||||||
wrap_comments = true
|
|
|
@ -1,11 +1,13 @@
|
||||||
//! CLI options
|
//! CLI options
|
||||||
use clap::{
|
use {
|
||||||
Parser,
|
clap::{
|
||||||
Subcommand,
|
Parser,
|
||||||
};
|
Subcommand,
|
||||||
use solana_sdk::{
|
},
|
||||||
commitment_config::CommitmentConfig,
|
solana_sdk::{
|
||||||
pubkey::Pubkey,
|
commitment_config::CommitmentConfig,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
@ -17,7 +19,7 @@ pub struct Cli {
|
||||||
#[clap(long, default_value = "confirmed")]
|
#[clap(long, default_value = "confirmed")]
|
||||||
pub commitment: CommitmentConfig,
|
pub commitment: CommitmentConfig,
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
|
@ -25,7 +27,7 @@ pub enum Action {
|
||||||
#[clap(about = "Post a VAA and execute it through the remote executor")]
|
#[clap(about = "Post a VAA and execute it through the remote executor")]
|
||||||
PostAndExecute {
|
PostAndExecute {
|
||||||
#[clap(short = 'v', long = "vaa")]
|
#[clap(short = 'v', long = "vaa")]
|
||||||
vaa: String,
|
vaa: String,
|
||||||
#[clap(
|
#[clap(
|
||||||
long,
|
long,
|
||||||
default_value = "~/.config/solana/id.json",
|
default_value = "~/.config/solana/id.json",
|
||||||
|
@ -47,22 +49,22 @@ pub enum Action {
|
||||||
#[clap(about = "Get set upgrade authority payload for squads-cli")]
|
#[clap(about = "Get set upgrade authority payload for squads-cli")]
|
||||||
GetSetUpgradeAuthorityPayload {
|
GetSetUpgradeAuthorityPayload {
|
||||||
#[clap(short, long, help = "Current authority")]
|
#[clap(short, long, help = "Current authority")]
|
||||||
current: Pubkey,
|
current: Pubkey,
|
||||||
#[clap(short, long, help = "New authority")]
|
#[clap(short, long, help = "New authority")]
|
||||||
new: Pubkey,
|
new: Pubkey,
|
||||||
#[clap(short, long, help = "Program id")]
|
#[clap(short, long, help = "Program id")]
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
},
|
},
|
||||||
#[clap(about = "Get upgrade program payload for squads-cli")]
|
#[clap(about = "Get upgrade program payload for squads-cli")]
|
||||||
GetUpgradeProgramPayload {
|
GetUpgradeProgramPayload {
|
||||||
#[clap(short, long, help = "Current authority")]
|
#[clap(short, long, help = "Current authority")]
|
||||||
authority: Pubkey,
|
authority: Pubkey,
|
||||||
#[clap(short, long, help = "Program id")]
|
#[clap(short, long, help = "Program id")]
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
#[clap(short, long, help = "New buffer")]
|
#[clap(short, long, help = "New buffer")]
|
||||||
new_buffer: Pubkey,
|
new_buffer: Pubkey,
|
||||||
#[clap(short, long, help = "Spill address")]
|
#[clap(short, long, help = "Spill address")]
|
||||||
spill: Pubkey,
|
spill: Pubkey,
|
||||||
},
|
},
|
||||||
#[clap(about = "Map solana key to pythnet key")]
|
#[clap(about = "Map solana key to pythnet key")]
|
||||||
MapKey {
|
MapKey {
|
||||||
|
|
|
@ -1,69 +1,70 @@
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
use std::str::FromStr;
|
use {
|
||||||
|
anchor_client::{
|
||||||
use anchor_client::{
|
anchor_lang::{
|
||||||
anchor_lang::{
|
AccountDeserialize,
|
||||||
AccountDeserialize,
|
AnchorDeserialize,
|
||||||
AnchorDeserialize,
|
AnchorSerialize,
|
||||||
AnchorSerialize,
|
InstructionData as AnchorInstructionData,
|
||||||
InstructionData as AnchorInstructionData,
|
Owner,
|
||||||
Owner,
|
ToAccountMetas,
|
||||||
ToAccountMetas,
|
},
|
||||||
|
solana_sdk::bpf_loader_upgradeable,
|
||||||
},
|
},
|
||||||
solana_sdk::bpf_loader_upgradeable,
|
anyhow::Result,
|
||||||
};
|
clap::Parser,
|
||||||
use clap::Parser;
|
cli::{
|
||||||
use cli::{
|
Action,
|
||||||
Action,
|
Cli,
|
||||||
Cli,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use remote_executor::{
|
|
||||||
accounts::ExecutePostedVaa,
|
|
||||||
state::governance_payload::InstructionData,
|
|
||||||
EXECUTOR_KEY_SEED,
|
|
||||||
ID,
|
|
||||||
};
|
|
||||||
use solana_client::rpc_client::RpcClient;
|
|
||||||
use solana_sdk::{
|
|
||||||
instruction::{
|
|
||||||
AccountMeta,
|
|
||||||
Instruction,
|
|
||||||
},
|
},
|
||||||
pubkey::Pubkey,
|
remote_executor::{
|
||||||
signature::{
|
accounts::ExecutePostedVaa,
|
||||||
read_keypair_file,
|
state::{
|
||||||
Keypair,
|
governance_payload::{
|
||||||
|
ExecutorPayload,
|
||||||
|
GovernanceHeader,
|
||||||
|
InstructionData,
|
||||||
|
},
|
||||||
|
posted_vaa::AnchorVaa,
|
||||||
|
},
|
||||||
|
EXECUTOR_KEY_SEED,
|
||||||
|
ID,
|
||||||
},
|
},
|
||||||
signer::Signer,
|
solana_client::rpc_client::RpcClient,
|
||||||
system_instruction,
|
solana_sdk::{
|
||||||
system_instruction::transfer,
|
instruction::{
|
||||||
transaction::Transaction,
|
AccountMeta,
|
||||||
};
|
Instruction,
|
||||||
use wormhole_solana::{
|
},
|
||||||
instructions::{
|
pubkey::Pubkey,
|
||||||
post_message,
|
signature::{
|
||||||
post_vaa,
|
read_keypair_file,
|
||||||
verify_signatures_txs,
|
Keypair,
|
||||||
PostVAAData,
|
},
|
||||||
|
signer::Signer,
|
||||||
|
system_instruction::{
|
||||||
|
self,
|
||||||
|
transfer,
|
||||||
|
},
|
||||||
|
transaction::Transaction,
|
||||||
},
|
},
|
||||||
Account,
|
std::str::FromStr,
|
||||||
Config,
|
wormhole::VAA,
|
||||||
FeeCollector,
|
wormhole_solana::{
|
||||||
GuardianSet,
|
instructions::{
|
||||||
VAA as PostedVAA,
|
post_message,
|
||||||
};
|
post_vaa,
|
||||||
|
verify_signatures_txs,
|
||||||
use remote_executor::state::{
|
PostVAAData,
|
||||||
governance_payload::{
|
},
|
||||||
ExecutorPayload,
|
Account,
|
||||||
GovernanceHeader,
|
Config,
|
||||||
|
FeeCollector,
|
||||||
|
GuardianSet,
|
||||||
|
VAA as PostedVAA,
|
||||||
},
|
},
|
||||||
posted_vaa::AnchorVaa,
|
|
||||||
};
|
};
|
||||||
use wormhole::VAA;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
@ -130,15 +131,15 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
// Post VAA
|
// Post VAA
|
||||||
let post_vaa_data = PostVAAData {
|
let post_vaa_data = PostVAAData {
|
||||||
version: vaa.version,
|
version: vaa.version,
|
||||||
guardian_set_index: vaa.guardian_set_index,
|
guardian_set_index: vaa.guardian_set_index,
|
||||||
timestamp: vaa.timestamp,
|
timestamp: vaa.timestamp,
|
||||||
nonce: vaa.nonce,
|
nonce: vaa.nonce,
|
||||||
emitter_chain: vaa.emitter_chain.into(),
|
emitter_chain: vaa.emitter_chain.into(),
|
||||||
emitter_address: vaa.emitter_address,
|
emitter_address: vaa.emitter_address,
|
||||||
sequence: vaa.sequence,
|
sequence: vaa.sequence,
|
||||||
consistency_level: vaa.consistency_level,
|
consistency_level: vaa.consistency_level,
|
||||||
payload: vaa.payload,
|
payload: vaa.payload,
|
||||||
};
|
};
|
||||||
|
|
||||||
process_transaction(
|
process_transaction(
|
||||||
|
@ -184,7 +185,7 @@ fn main() -> Result<()> {
|
||||||
Config::try_from_slice(&rpc_client.get_account_data(&wormhole_config)?)?;
|
Config::try_from_slice(&rpc_client.get_account_data(&wormhole_config)?)?;
|
||||||
|
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: GovernanceHeader::executor_governance_header(),
|
header: GovernanceHeader::executor_governance_header(),
|
||||||
instructions: vec![],
|
instructions: vec![],
|
||||||
}
|
}
|
||||||
.try_to_vec()?;
|
.try_to_vec()?;
|
||||||
|
@ -212,7 +213,7 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
Action::GetTestPayload {} => {
|
Action::GetTestPayload {} => {
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: GovernanceHeader::executor_governance_header(),
|
header: GovernanceHeader::executor_governance_header(),
|
||||||
instructions: vec![],
|
instructions: vec![],
|
||||||
}
|
}
|
||||||
.try_to_vec()?;
|
.try_to_vec()?;
|
||||||
|
@ -239,7 +240,7 @@ fn main() -> Result<()> {
|
||||||
instruction.accounts[2].is_signer = true; // Require signature of new authority for safety
|
instruction.accounts[2].is_signer = true; // Require signature of new authority for safety
|
||||||
println!("New authority : {:}", instruction.accounts[2].pubkey);
|
println!("New authority : {:}", instruction.accounts[2].pubkey);
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: GovernanceHeader::executor_governance_header(),
|
header: GovernanceHeader::executor_governance_header(),
|
||||||
instructions: vec![InstructionData::from(&instruction)],
|
instructions: vec![InstructionData::from(&instruction)],
|
||||||
}
|
}
|
||||||
.try_to_vec()?;
|
.try_to_vec()?;
|
||||||
|
@ -261,7 +262,7 @@ fn main() -> Result<()> {
|
||||||
instruction.accounts[3].pubkey
|
instruction.accounts[3].pubkey
|
||||||
);
|
);
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: GovernanceHeader::executor_governance_header(),
|
header: GovernanceHeader::executor_governance_header(),
|
||||||
instructions: vec![InstructionData::from(&instruction)],
|
instructions: vec![InstructionData::from(&instruction)],
|
||||||
}
|
}
|
||||||
.try_to_vec()?;
|
.try_to_vec()?;
|
||||||
|
@ -310,8 +311,8 @@ pub fn get_execute_instruction(
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
account_metas.push(AccountMeta {
|
account_metas.push(AccountMeta {
|
||||||
pubkey: executor_key,
|
pubkey: executor_key,
|
||||||
is_signer: false,
|
is_signer: false,
|
||||||
is_writable: true,
|
is_writable: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -319,8 +320,8 @@ pub fn get_execute_instruction(
|
||||||
for instruction in executor_payload.instructions {
|
for instruction in executor_payload.instructions {
|
||||||
// Push program_id
|
// Push program_id
|
||||||
account_metas.push(AccountMeta {
|
account_metas.push(AccountMeta {
|
||||||
pubkey: instruction.program_id,
|
pubkey: instruction.program_id,
|
||||||
is_signer: false,
|
is_signer: false,
|
||||||
is_writable: false,
|
is_writable: false,
|
||||||
});
|
});
|
||||||
// Push other accounts
|
// Push other accounts
|
||||||
|
@ -333,7 +334,7 @@ pub fn get_execute_instruction(
|
||||||
|
|
||||||
Ok(Instruction {
|
Ok(Instruction {
|
||||||
program_id: ID,
|
program_id: ID,
|
||||||
accounts: account_metas,
|
accounts: account_metas,
|
||||||
data: remote_executor::instruction::ExecutePostedVaa.data(),
|
data: remote_executor::instruction::ExecutePostedVaa.data(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![allow(clippy::result_large_err)]
|
#![allow(clippy::result_large_err)]
|
||||||
|
|
||||||
use anchor_lang::{
|
use {
|
||||||
prelude::*,
|
anchor_lang::{
|
||||||
solana_program::borsh::get_packed_len,
|
prelude::*,
|
||||||
system_program,
|
solana_program::borsh::get_packed_len,
|
||||||
};
|
system_program,
|
||||||
use error::ExecutorError;
|
},
|
||||||
use state::{
|
error::ExecutorError,
|
||||||
claim_record::ClaimRecord,
|
state::{
|
||||||
posted_vaa::AnchorVaa,
|
claim_record::ClaimRecord,
|
||||||
};
|
posted_vaa::AnchorVaa,
|
||||||
use wormhole::Chain::{
|
},
|
||||||
self,
|
wormhole::Chain::{
|
||||||
Solana,
|
self,
|
||||||
|
Solana,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
@ -27,15 +29,15 @@ declare_id!("exe6S3AxPVNmy46L4Nj6HrnnAVQUhwyYzMSNcnRn3qq");
|
||||||
|
|
||||||
#[program]
|
#[program]
|
||||||
pub mod remote_executor {
|
pub mod remote_executor {
|
||||||
use anchor_lang::solana_program::{
|
use {
|
||||||
instruction::Instruction,
|
super::*,
|
||||||
program::invoke_signed,
|
crate::state::governance_payload::ExecutorPayload,
|
||||||
|
anchor_lang::solana_program::{
|
||||||
|
instruction::Instruction,
|
||||||
|
program::invoke_signed,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::state::governance_payload::ExecutorPayload;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn execute_posted_vaa(ctx: Context<ExecutePostedVaa>) -> Result<()> {
|
pub fn execute_posted_vaa(ctx: Context<ExecutePostedVaa>) -> Result<()> {
|
||||||
let posted_vaa = &ctx.accounts.posted_vaa;
|
let posted_vaa = &ctx.accounts.posted_vaa;
|
||||||
let claim_record = &mut ctx.accounts.claim_record;
|
let claim_record = &mut ctx.accounts.claim_record;
|
||||||
|
@ -71,12 +73,12 @@ pub const CLAIM_RECORD_SEED: &str = "CLAIM_RECORD";
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct ExecutePostedVaa<'info> {
|
pub struct ExecutePostedVaa<'info> {
|
||||||
#[account(mut)]
|
#[account(mut)]
|
||||||
pub payer: Signer<'info>,
|
pub payer: Signer<'info>,
|
||||||
#[account(constraint = Chain::from(posted_vaa.emitter_chain) == Solana @ ExecutorError::EmitterChainNotSolana, constraint = posted_vaa.sequence > claim_record.sequence @ExecutorError::NonIncreasingSequence, constraint = (&posted_vaa.magic == b"vaa" || &posted_vaa.magic == b"msg" || &posted_vaa.magic == b"msu") @ExecutorError::PostedVaaHeaderWrongMagicNumber )]
|
#[account(constraint = Chain::from(posted_vaa.emitter_chain) == Solana @ ExecutorError::EmitterChainNotSolana, constraint = posted_vaa.sequence > claim_record.sequence @ExecutorError::NonIncreasingSequence, constraint = (&posted_vaa.magic == b"vaa" || &posted_vaa.magic == b"msg" || &posted_vaa.magic == b"msu") @ExecutorError::PostedVaaHeaderWrongMagicNumber )]
|
||||||
pub posted_vaa: Account<'info, AnchorVaa>,
|
pub posted_vaa: Account<'info, AnchorVaa>,
|
||||||
/// The reason claim_record has different seeds than executor_key is that executor key might need to pay in the CPI, so we want it to be a native wallet
|
/// The reason claim_record has different seeds than executor_key is that executor key might need to pay in the CPI, so we want it to be a native wallet
|
||||||
#[account(init_if_needed, space = 8 + get_packed_len::<ClaimRecord>(), payer=payer, seeds = [CLAIM_RECORD_SEED.as_bytes(), &posted_vaa.emitter_address], bump)]
|
#[account(init_if_needed, space = 8 + get_packed_len::<ClaimRecord>(), payer=payer, seeds = [CLAIM_RECORD_SEED.as_bytes(), &posted_vaa.emitter_address], bump)]
|
||||||
pub claim_record: Account<'info, ClaimRecord>,
|
pub claim_record: Account<'info, ClaimRecord>,
|
||||||
pub system_program: Program<'info, System>,
|
pub system_program: Program<'info, System>,
|
||||||
// Additional accounts passed to the instruction will be passed down to the CPIs. Very importantly executor_key needs to be passed as it will be the signer of the CPIs.
|
// Additional accounts passed to the instruction will be passed down to the CPIs. Very importantly executor_key needs to be passed as it will be the signer of the CPIs.
|
||||||
// Below is the "anchor specification" of that account
|
// Below is the "anchor specification" of that account
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
use std::{
|
use {
|
||||||
io::ErrorKind,
|
crate::error::ExecutorError,
|
||||||
mem::size_of,
|
anchor_lang::{
|
||||||
ops::Deref,
|
prelude::*,
|
||||||
|
solana_program::instruction::Instruction,
|
||||||
|
},
|
||||||
|
boolinator::Boolinator,
|
||||||
|
std::{
|
||||||
|
io::ErrorKind,
|
||||||
|
mem::size_of,
|
||||||
|
ops::Deref,
|
||||||
|
},
|
||||||
|
wormhole::Chain,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anchor_lang::{
|
|
||||||
prelude::*,
|
|
||||||
solana_program::instruction::Instruction,
|
|
||||||
};
|
|
||||||
use boolinator::Boolinator;
|
|
||||||
use wormhole::Chain;
|
|
||||||
|
|
||||||
use crate::error::ExecutorError;
|
|
||||||
|
|
||||||
pub const MAGIC_NUMBER: u32 = 0x4d475450; // Reverse order of the solidity contract because borsh uses little endian numbers (the solidity contract uses 0x5054474d)
|
pub const MAGIC_NUMBER: u32 = 0x4d475450; // Reverse order of the solidity contract because borsh uses little endian numbers (the solidity contract uses 0x5054474d)
|
||||||
|
|
||||||
#[derive(AnchorDeserialize, AnchorSerialize, Debug, PartialEq, Eq)]
|
#[derive(AnchorDeserialize, AnchorSerialize, Debug, PartialEq, Eq)]
|
||||||
|
@ -41,9 +41,9 @@ pub enum Action {
|
||||||
#[derive(AnchorDeserialize, AnchorSerialize, Eq, PartialEq, Debug)]
|
#[derive(AnchorDeserialize, AnchorSerialize, Eq, PartialEq, Debug)]
|
||||||
pub struct GovernanceHeader {
|
pub struct GovernanceHeader {
|
||||||
pub magic_number: u32,
|
pub magic_number: u32,
|
||||||
pub module: Module,
|
pub module: Module,
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
pub chain: BigEndianU16,
|
pub chain: BigEndianU16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GovernanceHeader {
|
impl GovernanceHeader {
|
||||||
|
@ -51,9 +51,9 @@ impl GovernanceHeader {
|
||||||
pub fn executor_governance_header() -> Self {
|
pub fn executor_governance_header() -> Self {
|
||||||
Self {
|
Self {
|
||||||
magic_number: MAGIC_NUMBER,
|
magic_number: MAGIC_NUMBER,
|
||||||
module: Module::Executor,
|
module: Module::Executor,
|
||||||
action: Action::ExecutePostedVaa,
|
action: Action::ExecutePostedVaa,
|
||||||
chain: BigEndianU16 {
|
chain: BigEndianU16 {
|
||||||
value: Chain::Pythnet.try_into().unwrap(),
|
value: Chain::Pythnet.try_into().unwrap(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -100,18 +100,18 @@ pub struct InstructionData {
|
||||||
/// Pubkey of the instruction processor that executes this instruction
|
/// Pubkey of the instruction processor that executes this instruction
|
||||||
pub program_id: Pubkey,
|
pub program_id: Pubkey,
|
||||||
/// Metadata for what accounts should be passed to the instruction processor
|
/// Metadata for what accounts should be passed to the instruction processor
|
||||||
pub accounts: Vec<AccountMetaData>,
|
pub accounts: Vec<AccountMetaData>,
|
||||||
/// Opaque data passed to the instruction processor
|
/// Opaque data passed to the instruction processor
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Account metadata used to define Instructions
|
/// Account metadata used to define Instructions
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize)]
|
||||||
pub struct AccountMetaData {
|
pub struct AccountMetaData {
|
||||||
/// An account's public key
|
/// An account's public key
|
||||||
pub pubkey: Pubkey,
|
pub pubkey: Pubkey,
|
||||||
/// True if an Instruction requires a Transaction signature matching `pubkey`.
|
/// True if an Instruction requires a Transaction signature matching `pubkey`.
|
||||||
pub is_signer: bool,
|
pub is_signer: bool,
|
||||||
/// True if the `pubkey` can be loaded as a read-write account.
|
/// True if the `pubkey` can be loaded as a read-write account.
|
||||||
pub is_writable: bool,
|
pub is_writable: bool,
|
||||||
}
|
}
|
||||||
|
@ -120,16 +120,16 @@ impl From<&InstructionData> for Instruction {
|
||||||
fn from(instruction: &InstructionData) -> Self {
|
fn from(instruction: &InstructionData) -> Self {
|
||||||
Instruction {
|
Instruction {
|
||||||
program_id: instruction.program_id,
|
program_id: instruction.program_id,
|
||||||
accounts: instruction
|
accounts: instruction
|
||||||
.accounts
|
.accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| AccountMeta {
|
.map(|a| AccountMeta {
|
||||||
pubkey: a.pubkey,
|
pubkey: a.pubkey,
|
||||||
is_signer: a.is_signer,
|
is_signer: a.is_signer,
|
||||||
is_writable: a.is_writable,
|
is_writable: a.is_writable,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
data: instruction.data.clone(),
|
data: instruction.data.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,16 +138,16 @@ impl From<&Instruction> for InstructionData {
|
||||||
fn from(instruction: &Instruction) -> Self {
|
fn from(instruction: &Instruction) -> Self {
|
||||||
InstructionData {
|
InstructionData {
|
||||||
program_id: instruction.program_id,
|
program_id: instruction.program_id,
|
||||||
accounts: instruction
|
accounts: instruction
|
||||||
.accounts
|
.accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| AccountMetaData {
|
.map(|a| AccountMetaData {
|
||||||
pubkey: a.pubkey,
|
pubkey: a.pubkey,
|
||||||
is_signer: a.is_signer,
|
is_signer: a.is_signer,
|
||||||
is_writable: a.is_writable,
|
is_writable: a.is_writable,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
data: instruction.data.clone(),
|
data: instruction.data.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,24 +170,25 @@ impl ExecutorPayload {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use crate::{
|
use {
|
||||||
error,
|
super::ExecutorPayload,
|
||||||
error::ExecutorError,
|
crate::{
|
||||||
state::governance_payload::InstructionData,
|
error,
|
||||||
};
|
error::ExecutorError,
|
||||||
|
state::governance_payload::InstructionData,
|
||||||
use super::ExecutorPayload;
|
},
|
||||||
use anchor_lang::{
|
anchor_lang::{
|
||||||
prelude::Pubkey,
|
prelude::Pubkey,
|
||||||
AnchorDeserialize,
|
AnchorDeserialize,
|
||||||
AnchorSerialize,
|
AnchorSerialize,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_deserialization_serialization() {
|
fn test_check_deserialization_serialization() {
|
||||||
// No instructions
|
// No instructions
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: super::GovernanceHeader::executor_governance_header(),
|
header: super::GovernanceHeader::executor_governance_header(),
|
||||||
instructions: vec![],
|
instructions: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use anchor_lang::prelude::*;
|
use {
|
||||||
use std::{
|
anchor_lang::prelude::*,
|
||||||
io::Write,
|
std::{
|
||||||
ops::Deref,
|
io::Write,
|
||||||
str::FromStr,
|
ops::Deref,
|
||||||
|
str::FromStr,
|
||||||
|
},
|
||||||
|
wormhole_solana::VAA,
|
||||||
};
|
};
|
||||||
use wormhole_solana::VAA;
|
|
||||||
|
|
||||||
// The current chain's wormhole bridge owns the VAA accounts
|
// The current chain's wormhole bridge owns the VAA accounts
|
||||||
impl Owner for AnchorVaa {
|
impl Owner for AnchorVaa {
|
||||||
|
@ -50,5 +52,5 @@ impl Deref for AnchorVaa {
|
||||||
#[derive(Clone, AnchorDeserialize, AnchorSerialize)]
|
#[derive(Clone, AnchorDeserialize, AnchorSerialize)]
|
||||||
pub struct AnchorVaa {
|
pub struct AnchorVaa {
|
||||||
pub magic: [u8; 3],
|
pub magic: [u8; 3],
|
||||||
pub vaa: VAA,
|
pub vaa: VAA,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +1,69 @@
|
||||||
use anchor_lang::{
|
use {
|
||||||
prelude::{
|
crate::{
|
||||||
AccountMeta,
|
error::ExecutorError,
|
||||||
ProgramError,
|
state::{
|
||||||
Pubkey,
|
claim_record::ClaimRecord,
|
||||||
Rent,
|
governance_payload::{
|
||||||
UpgradeableLoaderState,
|
ExecutorPayload,
|
||||||
},
|
GovernanceHeader,
|
||||||
solana_program::hash::Hash,
|
InstructionData,
|
||||||
AccountDeserialize,
|
},
|
||||||
AnchorDeserialize,
|
posted_vaa::AnchorVaa,
|
||||||
AnchorSerialize,
|
|
||||||
InstructionData as AnchorInstructionData,
|
|
||||||
Key,
|
|
||||||
Owner,
|
|
||||||
ToAccountMetas,
|
|
||||||
};
|
|
||||||
use solana_program_test::{
|
|
||||||
find_file,
|
|
||||||
read_file,
|
|
||||||
BanksClient,
|
|
||||||
BanksClientError,
|
|
||||||
ProgramTest,
|
|
||||||
ProgramTestBanksClientExt,
|
|
||||||
};
|
|
||||||
use solana_sdk::{
|
|
||||||
account::Account,
|
|
||||||
bpf_loader_upgradeable,
|
|
||||||
instruction::{
|
|
||||||
Instruction,
|
|
||||||
InstructionError,
|
|
||||||
},
|
|
||||||
signature::Keypair,
|
|
||||||
signer::Signer,
|
|
||||||
stake_history::Epoch,
|
|
||||||
system_instruction,
|
|
||||||
transaction::{
|
|
||||||
Transaction,
|
|
||||||
TransactionError,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use wormhole::Chain;
|
|
||||||
use wormhole_solana::VAA;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::ExecutorError,
|
|
||||||
state::{
|
|
||||||
claim_record::ClaimRecord,
|
|
||||||
governance_payload::{
|
|
||||||
ExecutorPayload,
|
|
||||||
GovernanceHeader,
|
|
||||||
InstructionData,
|
|
||||||
},
|
},
|
||||||
posted_vaa::AnchorVaa,
|
CLAIM_RECORD_SEED,
|
||||||
|
EXECUTOR_KEY_SEED,
|
||||||
},
|
},
|
||||||
CLAIM_RECORD_SEED,
|
anchor_lang::{
|
||||||
EXECUTOR_KEY_SEED,
|
prelude::{
|
||||||
|
AccountMeta,
|
||||||
|
ProgramError,
|
||||||
|
Pubkey,
|
||||||
|
Rent,
|
||||||
|
UpgradeableLoaderState,
|
||||||
|
},
|
||||||
|
solana_program::hash::Hash,
|
||||||
|
AccountDeserialize,
|
||||||
|
AnchorDeserialize,
|
||||||
|
AnchorSerialize,
|
||||||
|
InstructionData as AnchorInstructionData,
|
||||||
|
Key,
|
||||||
|
Owner,
|
||||||
|
ToAccountMetas,
|
||||||
|
},
|
||||||
|
solana_program_test::{
|
||||||
|
find_file,
|
||||||
|
read_file,
|
||||||
|
BanksClient,
|
||||||
|
BanksClientError,
|
||||||
|
ProgramTest,
|
||||||
|
ProgramTestBanksClientExt,
|
||||||
|
},
|
||||||
|
solana_sdk::{
|
||||||
|
account::Account,
|
||||||
|
bpf_loader_upgradeable,
|
||||||
|
instruction::{
|
||||||
|
Instruction,
|
||||||
|
InstructionError,
|
||||||
|
},
|
||||||
|
signature::Keypair,
|
||||||
|
signer::Signer,
|
||||||
|
stake_history::Epoch,
|
||||||
|
system_instruction,
|
||||||
|
transaction::{
|
||||||
|
Transaction,
|
||||||
|
TransactionError,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::collections::HashMap,
|
||||||
|
wormhole::Chain,
|
||||||
|
wormhole_solana::VAA,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Bench for the tests, the goal of this struct is to be able to setup solana accounts before starting the local validator
|
/// Bench for the tests, the goal of this struct is to be able to setup solana accounts before starting the local validator
|
||||||
pub struct ExecutorBench {
|
pub struct ExecutorBench {
|
||||||
program_test: ProgramTest,
|
program_test: ProgramTest,
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
seqno: HashMap<Pubkey, u64>,
|
seqno: HashMap<Pubkey, u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When passed to `add_vaa_account` modify the posted vaa in a way that makes the vaa invalid
|
/// When passed to `add_vaa_account` modify the posted vaa in a way that makes the vaa invalid
|
||||||
|
@ -94,7 +95,7 @@ impl ExecutorBench {
|
||||||
programdata_address: programdata_key,
|
programdata_address: programdata_key,
|
||||||
};
|
};
|
||||||
let programdata_deserialized = UpgradeableLoaderState::ProgramData {
|
let programdata_deserialized = UpgradeableLoaderState::ProgramData {
|
||||||
slot: 1,
|
slot: 1,
|
||||||
upgrade_authority_address: Some(upgrade_authority_keypair.pubkey()),
|
upgrade_authority_address: Some(upgrade_authority_keypair.pubkey()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,16 +106,16 @@ impl ExecutorBench {
|
||||||
programdata_vec.append(&mut bpf_data);
|
programdata_vec.append(&mut bpf_data);
|
||||||
|
|
||||||
let program_account = Account {
|
let program_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(program_vec.len()),
|
lamports: Rent::default().minimum_balance(program_vec.len()),
|
||||||
data: program_vec,
|
data: program_vec,
|
||||||
owner: bpf_loader_upgradeable::ID,
|
owner: bpf_loader_upgradeable::ID,
|
||||||
executable: true,
|
executable: true,
|
||||||
rent_epoch: Epoch::default(),
|
rent_epoch: Epoch::default(),
|
||||||
};
|
};
|
||||||
let programdata_account = Account {
|
let programdata_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(programdata_vec.len()),
|
lamports: Rent::default().minimum_balance(programdata_vec.len()),
|
||||||
data: programdata_vec,
|
data: programdata_vec,
|
||||||
owner: bpf_loader_upgradeable::ID,
|
owner: bpf_loader_upgradeable::ID,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: Epoch::default(),
|
rent_epoch: Epoch::default(),
|
||||||
};
|
};
|
||||||
|
@ -166,7 +167,7 @@ impl ExecutorBench {
|
||||||
};
|
};
|
||||||
|
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: GovernanceHeader::executor_governance_header(),
|
header: GovernanceHeader::executor_governance_header(),
|
||||||
instructions: instructions
|
instructions: instructions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| InstructionData::from(x))
|
.map(|x| InstructionData::from(x))
|
||||||
|
@ -177,7 +178,7 @@ impl ExecutorBench {
|
||||||
|
|
||||||
let vaa = AnchorVaa {
|
let vaa = AnchorVaa {
|
||||||
magic: *vaa_magic,
|
magic: *vaa_magic,
|
||||||
vaa: VAA {
|
vaa: VAA {
|
||||||
vaa_version: 0,
|
vaa_version: 0,
|
||||||
consistency_level: 0,
|
consistency_level: 0,
|
||||||
vaa_time: 0,
|
vaa_time: 0,
|
||||||
|
@ -232,10 +233,10 @@ impl ExecutorBench {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct ExecutorSimulator {
|
pub struct ExecutorSimulator {
|
||||||
banks_client: BanksClient,
|
banks_client: BanksClient,
|
||||||
payer: Keypair,
|
payer: Keypair,
|
||||||
last_blockhash: Hash,
|
last_blockhash: Hash,
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When passed to execute_posted_vaa, try to impersonate some of the accounts
|
/// When passed to execute_posted_vaa, try to impersonate some of the accounts
|
||||||
|
@ -333,8 +334,8 @@ impl ExecutorSimulator {
|
||||||
|
|
||||||
// We need to add `executor_key` to the list of accounts
|
// We need to add `executor_key` to the list of accounts
|
||||||
account_metas.push(AccountMeta {
|
account_metas.push(AccountMeta {
|
||||||
pubkey: executor_key,
|
pubkey: executor_key,
|
||||||
is_signer: false,
|
is_signer: false,
|
||||||
is_writable: true,
|
is_writable: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -342,8 +343,8 @@ impl ExecutorSimulator {
|
||||||
for instruction in executor_payload.instructions {
|
for instruction in executor_payload.instructions {
|
||||||
// Push program_id
|
// Push program_id
|
||||||
account_metas.push(AccountMeta {
|
account_metas.push(AccountMeta {
|
||||||
pubkey: instruction.program_id,
|
pubkey: instruction.program_id,
|
||||||
is_signer: false,
|
is_signer: false,
|
||||||
is_writable: false,
|
is_writable: false,
|
||||||
});
|
});
|
||||||
// Push other accounts
|
// Push other accounts
|
||||||
|
@ -356,8 +357,8 @@ impl ExecutorSimulator {
|
||||||
|
|
||||||
let instruction = Instruction {
|
let instruction = Instruction {
|
||||||
program_id: self.program_id,
|
program_id: self.program_id,
|
||||||
accounts: account_metas,
|
accounts: account_metas,
|
||||||
data: crate::instruction::ExecutePostedVaa.data(),
|
data: crate::instruction::ExecutePostedVaa.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.process_ix(instruction, signers).await
|
self.process_ix(instruction, signers).await
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
use crate::error::ExecutorError;
|
use {
|
||||||
|
super::executor_simulator::{
|
||||||
use super::executor_simulator::{
|
ExecutorAttack,
|
||||||
ExecutorAttack,
|
ExecutorBench,
|
||||||
ExecutorBench,
|
VaaAttack,
|
||||||
VaaAttack,
|
},
|
||||||
};
|
crate::error::ExecutorError,
|
||||||
use anchor_lang::prelude::{
|
anchor_lang::prelude::{
|
||||||
ErrorCode,
|
ErrorCode,
|
||||||
ProgramError,
|
ProgramError,
|
||||||
Pubkey,
|
Pubkey,
|
||||||
Rent,
|
Rent,
|
||||||
};
|
},
|
||||||
use solana_sdk::{
|
solana_sdk::{
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
native_token::LAMPORTS_PER_SOL,
|
native_token::LAMPORTS_PER_SOL,
|
||||||
system_instruction::transfer,
|
system_instruction::transfer,
|
||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
|
@ -1,28 +1,29 @@
|
||||||
use crate::{
|
use {
|
||||||
error::ExecutorError,
|
super::executor_simulator::ExecutorBench,
|
||||||
tests::executor_simulator::{
|
crate::{
|
||||||
ExecutorAttack,
|
error::ExecutorError,
|
||||||
VaaAttack,
|
tests::executor_simulator::{
|
||||||
|
ExecutorAttack,
|
||||||
|
VaaAttack,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
anchor_lang::{
|
||||||
|
prelude::{
|
||||||
use super::executor_simulator::ExecutorBench;
|
Pubkey,
|
||||||
use anchor_lang::{
|
Rent,
|
||||||
prelude::{
|
},
|
||||||
Pubkey,
|
solana_program::{
|
||||||
Rent,
|
system_instruction::create_account,
|
||||||
|
system_program,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
solana_program::{
|
solana_sdk::{
|
||||||
system_instruction::create_account,
|
native_token::LAMPORTS_PER_SOL,
|
||||||
system_program,
|
signature::Keypair,
|
||||||
|
signer::Signer,
|
||||||
|
system_instruction::transfer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
|
||||||
native_token::LAMPORTS_PER_SOL,
|
|
||||||
signature::Keypair,
|
|
||||||
signer::Signer,
|
|
||||||
system_instruction::transfer,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
/// This test file tests that the executor can :
|
/// This test file tests that the executor can :
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Merge similar crates together to avoid multiple use statements.
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
|
|
||||||
# Consistency in formatting makes tool based searching/editing better.
|
|
||||||
empty_item_single_line = false
|
|
||||||
|
|
||||||
# Easier editing when arbitrary mixed use statements do not collapse.
|
|
||||||
imports_layout = "Vertical"
|
|
||||||
|
|
||||||
# Default rustfmt formatting of match arms with branches is awful.
|
|
||||||
match_arm_leading_pipes = "Preserve"
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Merge all imports into a clean vertical list of module imports.
|
||||||
|
imports_granularity = "One"
|
||||||
|
group_imports = "One"
|
||||||
|
imports_layout = "Vertical"
|
||||||
|
|
||||||
|
# Better grep-ability.
|
||||||
|
empty_item_single_line = false
|
||||||
|
|
||||||
|
# Consistent pipe layout.
|
||||||
|
match_arm_leading_pipes = "Preserve"
|
||||||
|
|
||||||
|
# Align Fields
|
||||||
|
enum_discrim_align_threshold = 80
|
||||||
|
struct_field_align_threshold = 80
|
||||||
|
|
||||||
|
# Allow up to two blank lines for visual grouping.
|
||||||
|
blank_lines_upper_bound = 2
|
|
@ -1,39 +1,38 @@
|
||||||
use std::{
|
use {
|
||||||
collections::{
|
crate::BatchState,
|
||||||
HashMap,
|
log::info,
|
||||||
HashSet,
|
serde::{
|
||||||
|
de::Error,
|
||||||
|
Deserialize,
|
||||||
|
Deserializer,
|
||||||
|
Serialize,
|
||||||
|
Serializer,
|
||||||
|
},
|
||||||
|
solana_program::pubkey::Pubkey,
|
||||||
|
std::{
|
||||||
|
collections::{
|
||||||
|
HashMap,
|
||||||
|
HashSet,
|
||||||
|
},
|
||||||
|
iter,
|
||||||
|
str::FromStr,
|
||||||
},
|
},
|
||||||
iter,
|
|
||||||
str::FromStr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
use serde::{
|
|
||||||
de::Error,
|
|
||||||
Deserialize,
|
|
||||||
Deserializer,
|
|
||||||
Serialize,
|
|
||||||
Serializer,
|
|
||||||
};
|
|
||||||
use solana_program::pubkey::Pubkey;
|
|
||||||
|
|
||||||
use crate::BatchState;
|
|
||||||
|
|
||||||
/// Pyth2wormhole config specific to attestation requests
|
/// Pyth2wormhole config specific to attestation requests
|
||||||
#[derive(Clone, Debug, Hash, Deserialize, Serialize, PartialEq)]
|
#[derive(Clone, Debug, Hash, Deserialize, Serialize, PartialEq)]
|
||||||
pub struct AttestationConfig {
|
pub struct AttestationConfig {
|
||||||
#[serde(default = "default_min_msg_reuse_interval_ms")]
|
#[serde(default = "default_min_msg_reuse_interval_ms")]
|
||||||
pub min_msg_reuse_interval_ms: u64,
|
pub min_msg_reuse_interval_ms: u64,
|
||||||
#[serde(default = "default_max_msg_accounts")]
|
#[serde(default = "default_max_msg_accounts")]
|
||||||
pub max_msg_accounts: u64,
|
pub max_msg_accounts: u64,
|
||||||
/// Optionally, we take a mapping account to add remaining symbols from a Pyth deployments. These symbols are processed under attestation conditions for the `default` symbol group.
|
/// Optionally, we take a mapping account to add remaining symbols from a Pyth deployments. These symbols are processed under attestation conditions for the `default` symbol group.
|
||||||
#[serde(
|
#[serde(
|
||||||
deserialize_with = "opt_pubkey_string_de",
|
deserialize_with = "opt_pubkey_string_de",
|
||||||
serialize_with = "opt_pubkey_string_ser",
|
serialize_with = "opt_pubkey_string_ser",
|
||||||
default // Uses Option::default() which is None
|
default // Uses Option::default() which is None
|
||||||
)]
|
)]
|
||||||
pub mapping_addr: Option<Pubkey>,
|
pub mapping_addr: Option<Pubkey>,
|
||||||
/// The known symbol list will be reloaded based off this
|
/// The known symbol list will be reloaded based off this
|
||||||
/// interval, to account for mapping changes. Note: This interval
|
/// interval, to account for mapping changes. Note: This interval
|
||||||
/// will only work if the mapping address is defined. Whenever
|
/// will only work if the mapping address is defined. Whenever
|
||||||
|
@ -45,8 +44,8 @@ pub struct AttestationConfig {
|
||||||
pub mapping_reload_interval_mins: u64,
|
pub mapping_reload_interval_mins: u64,
|
||||||
#[serde(default = "default_min_rpc_interval_ms")]
|
#[serde(default = "default_min_rpc_interval_ms")]
|
||||||
/// Rate-limiting minimum delay between RPC requests in milliseconds"
|
/// Rate-limiting minimum delay between RPC requests in milliseconds"
|
||||||
pub min_rpc_interval_ms: u64,
|
pub min_rpc_interval_ms: u64,
|
||||||
pub symbol_groups: Vec<SymbolGroup>,
|
pub symbol_groups: Vec<SymbolGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttestationConfig {
|
impl AttestationConfig {
|
||||||
|
@ -75,9 +74,9 @@ impl AttestationConfig {
|
||||||
.map(|(prod, prices)| iter::zip(iter::repeat(prod), prices)) // Convert to iterator over flat (prod, price) tuples
|
.map(|(prod, prices)| iter::zip(iter::repeat(prod), prices)) // Convert to iterator over flat (prod, price) tuples
|
||||||
.flatten() // Flatten the tuple iterators
|
.flatten() // Flatten the tuple iterators
|
||||||
.map(|(prod, price)| P2WSymbol {
|
.map(|(prod, price)| P2WSymbol {
|
||||||
name: None,
|
name: None,
|
||||||
product_addr: prod,
|
product_addr: prod,
|
||||||
price_addr: price,
|
price_addr: price,
|
||||||
})
|
})
|
||||||
.collect::<Vec<P2WSymbol>>();
|
.collect::<Vec<P2WSymbol>>();
|
||||||
|
|
||||||
|
@ -129,7 +128,7 @@ pub struct SymbolGroup {
|
||||||
pub group_name: String,
|
pub group_name: String,
|
||||||
/// Attestation conditions applied to all symbols in this group
|
/// Attestation conditions applied to all symbols in this group
|
||||||
pub conditions: AttestationConditions,
|
pub conditions: AttestationConditions,
|
||||||
pub symbols: Vec<P2WSymbol>,
|
pub symbols: Vec<P2WSymbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn default_max_msg_accounts() -> u64 {
|
pub const fn default_max_msg_accounts() -> u64 {
|
||||||
|
@ -202,9 +201,9 @@ impl AttestationConditions {
|
||||||
impl Default for AttestationConditions {
|
impl Default for AttestationConditions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
min_interval_secs: default_min_interval_secs(),
|
min_interval_secs: default_min_interval_secs(),
|
||||||
max_batch_jobs: default_max_batch_jobs(),
|
max_batch_jobs: default_max_batch_jobs(),
|
||||||
price_changed_bps: None,
|
price_changed_bps: None,
|
||||||
publish_time_min_delta_secs: None,
|
publish_time_min_delta_secs: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +224,7 @@ pub struct P2WSymbol {
|
||||||
deserialize_with = "pubkey_string_de",
|
deserialize_with = "pubkey_string_de",
|
||||||
serialize_with = "pubkey_string_ser"
|
serialize_with = "pubkey_string_ser"
|
||||||
)]
|
)]
|
||||||
pub price_addr: Pubkey,
|
pub price_addr: Pubkey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for P2WSymbol {
|
impl ToString for P2WSymbol {
|
||||||
|
@ -275,9 +274,10 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use {
|
||||||
|
super::*,
|
||||||
use solitaire::ErrBox;
|
solitaire::ErrBox,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sanity() -> Result<(), ErrBox> {
|
fn test_sanity() -> Result<(), ErrBox> {
|
||||||
|
@ -287,7 +287,7 @@ mod tests {
|
||||||
min_interval_secs: 5,
|
min_interval_secs: 5,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
symbols: vec![
|
symbols: vec![
|
||||||
P2WSymbol {
|
P2WSymbol {
|
||||||
name: Some("ETHUSD".to_owned()),
|
name: Some("ETHUSD".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -305,7 +305,7 @@ mod tests {
|
||||||
min_interval_secs: 200,
|
min_interval_secs: 200,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
symbols: vec![
|
symbols: vec![
|
||||||
P2WSymbol {
|
P2WSymbol {
|
||||||
name: Some("CNYAUD".to_owned()),
|
name: Some("CNYAUD".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -318,12 +318,12 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let cfg = AttestationConfig {
|
let cfg = AttestationConfig {
|
||||||
min_msg_reuse_interval_ms: 1000,
|
min_msg_reuse_interval_ms: 1000,
|
||||||
max_msg_accounts: 100_000,
|
max_msg_accounts: 100_000,
|
||||||
min_rpc_interval_ms: 2123,
|
min_rpc_interval_ms: 2123,
|
||||||
mapping_addr: None,
|
mapping_addr: None,
|
||||||
mapping_reload_interval_mins: 42,
|
mapping_reload_interval_mins: 42,
|
||||||
symbol_groups: vec![fastbois, slowbois],
|
symbol_groups: vec![fastbois, slowbois],
|
||||||
};
|
};
|
||||||
|
|
||||||
let serialized = serde_yaml::to_string(&cfg)?;
|
let serialized = serde_yaml::to_string(&cfg)?;
|
||||||
|
@ -338,12 +338,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_symbols_works() -> Result<(), ErrBox> {
|
fn test_add_symbols_works() -> Result<(), ErrBox> {
|
||||||
let empty_config = AttestationConfig {
|
let empty_config = AttestationConfig {
|
||||||
min_msg_reuse_interval_ms: 1000,
|
min_msg_reuse_interval_ms: 1000,
|
||||||
max_msg_accounts: 100,
|
max_msg_accounts: 100,
|
||||||
min_rpc_interval_ms: 42422,
|
min_rpc_interval_ms: 42422,
|
||||||
mapping_addr: None,
|
mapping_addr: None,
|
||||||
mapping_reload_interval_mins: 42,
|
mapping_reload_interval_mins: 42,
|
||||||
symbol_groups: vec![],
|
symbol_groups: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mock_new_symbols = (0..255)
|
let mock_new_symbols = (0..255)
|
||||||
|
|
|
@ -1,30 +1,29 @@
|
||||||
use log::{
|
use {
|
||||||
debug,
|
crate::{
|
||||||
warn,
|
AttestationConditions,
|
||||||
};
|
P2WSymbol,
|
||||||
use solana_client::nonblocking::rpc_client::RpcClient;
|
},
|
||||||
|
log::{
|
||||||
use pyth_sdk_solana::state::PriceAccount;
|
debug,
|
||||||
|
warn,
|
||||||
use std::time::{
|
},
|
||||||
Duration,
|
pyth_sdk_solana::state::PriceAccount,
|
||||||
Instant,
|
solana_client::nonblocking::rpc_client::RpcClient,
|
||||||
};
|
std::time::{
|
||||||
|
Duration,
|
||||||
use crate::{
|
Instant,
|
||||||
AttestationConditions,
|
},
|
||||||
P2WSymbol,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Runtime representation of a batch. It refers to the original group
|
/// Runtime representation of a batch. It refers to the original group
|
||||||
/// from the config.
|
/// from the config.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BatchState {
|
pub struct BatchState {
|
||||||
pub group_name: String,
|
pub group_name: String,
|
||||||
pub symbols: Vec<P2WSymbol>,
|
pub symbols: Vec<P2WSymbol>,
|
||||||
pub last_known_symbol_states: Vec<Option<PriceAccount>>,
|
pub last_known_symbol_states: Vec<Option<PriceAccount>>,
|
||||||
pub conditions: AttestationConditions,
|
pub conditions: AttestationConditions,
|
||||||
pub last_job_finished_at: Instant,
|
pub last_job_finished_at: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BatchState {
|
impl<'a> BatchState {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
//! CLI options
|
//! CLI options
|
||||||
|
|
||||||
use solana_program::pubkey::Pubkey;
|
use {
|
||||||
use solana_sdk::commitment_config::CommitmentConfig;
|
clap::{
|
||||||
use std::path::PathBuf;
|
Parser,
|
||||||
|
Subcommand,
|
||||||
use clap::{
|
},
|
||||||
Parser,
|
solana_program::pubkey::Pubkey,
|
||||||
Subcommand,
|
solana_sdk::commitment_config::CommitmentConfig,
|
||||||
|
std::path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -20,15 +21,15 @@ pub struct Cli {
|
||||||
help = "Identity JSON file for the entity meant to cover transaction costs",
|
help = "Identity JSON file for the entity meant to cover transaction costs",
|
||||||
default_value = "~/.config/solana/id.json"
|
default_value = "~/.config/solana/id.json"
|
||||||
)]
|
)]
|
||||||
pub payer: String,
|
pub payer: String,
|
||||||
#[clap(short, long, default_value = "http://localhost:8899")]
|
#[clap(short, long, default_value = "http://localhost:8899")]
|
||||||
pub rpc_url: String,
|
pub rpc_url: String,
|
||||||
#[clap(long, default_value = "confirmed")]
|
#[clap(long, default_value = "confirmed")]
|
||||||
pub commitment: CommitmentConfig,
|
pub commitment: CommitmentConfig,
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub p2w_addr: Pubkey,
|
pub p2w_addr: Pubkey,
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
|
@ -37,16 +38,16 @@ pub enum Action {
|
||||||
Init {
|
Init {
|
||||||
/// The bridge program account
|
/// The bridge program account
|
||||||
#[clap(short = 'w', long = "wh-prog")]
|
#[clap(short = 'w', long = "wh-prog")]
|
||||||
wh_prog: Pubkey,
|
wh_prog: Pubkey,
|
||||||
#[clap(short = 'o', long = "owner")]
|
#[clap(short = 'o', long = "owner")]
|
||||||
owner_addr: Pubkey,
|
owner_addr: Pubkey,
|
||||||
#[clap(short = 'p', long = "pyth-owner")]
|
#[clap(short = 'p', long = "pyth-owner")]
|
||||||
pyth_owner_addr: Pubkey,
|
pyth_owner_addr: Pubkey,
|
||||||
/// Option<> makes sure not specifying this flag does not imply "false"
|
/// Option<> makes sure not specifying this flag does not imply "false"
|
||||||
#[clap(long = "is-active")]
|
#[clap(long = "is-active")]
|
||||||
is_active: Option<bool>,
|
is_active: Option<bool>,
|
||||||
#[clap(long = "ops-owner")]
|
#[clap(long = "ops-owner")]
|
||||||
ops_owner_addr: Option<Pubkey>,
|
ops_owner_addr: Option<Pubkey>,
|
||||||
},
|
},
|
||||||
#[clap(
|
#[clap(
|
||||||
about = "Use an existing pyth2wormhole program to attest product price information to another chain"
|
about = "Use an existing pyth2wormhole program to attest product price information to another chain"
|
||||||
|
@ -54,14 +55,14 @@ pub enum Action {
|
||||||
// Note: defaults target SOL mainnet-beta conditions at implementation time
|
// Note: defaults target SOL mainnet-beta conditions at implementation time
|
||||||
Attest {
|
Attest {
|
||||||
#[clap(short = 'f', long = "--config", help = "Attestation YAML config")]
|
#[clap(short = 'f', long = "--config", help = "Attestation YAML config")]
|
||||||
attestation_cfg: PathBuf,
|
attestation_cfg: PathBuf,
|
||||||
#[clap(
|
#[clap(
|
||||||
short = 'n',
|
short = 'n',
|
||||||
long = "--n-retries",
|
long = "--n-retries",
|
||||||
help = "How many times to retry send_transaction() on each batch before flagging a failure. Only active outside daemon mode",
|
help = "How many times to retry send_transaction() on each batch before flagging a failure. Only active outside daemon mode",
|
||||||
default_value = "5"
|
default_value = "5"
|
||||||
)]
|
)]
|
||||||
n_retries: usize,
|
n_retries: usize,
|
||||||
#[clap(
|
#[clap(
|
||||||
short = 'i',
|
short = 'i',
|
||||||
long = "--retry-interval",
|
long = "--retry-interval",
|
||||||
|
@ -69,13 +70,13 @@ pub enum Action {
|
||||||
retries. Only active outside daemon mode",
|
retries. Only active outside daemon mode",
|
||||||
default_value = "5"
|
default_value = "5"
|
||||||
)]
|
)]
|
||||||
retry_interval_secs: u64,
|
retry_interval_secs: u64,
|
||||||
#[clap(
|
#[clap(
|
||||||
short = 'd',
|
short = 'd',
|
||||||
long = "--daemon",
|
long = "--daemon",
|
||||||
help = "Do not stop attesting. In this mode, this program will behave more like a daemon and continuously attest the specified symbols."
|
help = "Do not stop attesting. In this mode, this program will behave more like a daemon and continuously attest the specified symbols."
|
||||||
)]
|
)]
|
||||||
daemon: bool,
|
daemon: bool,
|
||||||
#[clap(
|
#[clap(
|
||||||
short = 't',
|
short = 't',
|
||||||
long = "--timeout",
|
long = "--timeout",
|
||||||
|
@ -94,20 +95,20 @@ pub enum Action {
|
||||||
default_value = "~/.config/solana/id.json",
|
default_value = "~/.config/solana/id.json",
|
||||||
help = "Keypair file for the current config owner"
|
help = "Keypair file for the current config owner"
|
||||||
)]
|
)]
|
||||||
owner: String,
|
owner: String,
|
||||||
/// New owner to set
|
/// New owner to set
|
||||||
#[clap(long = "new-owner")]
|
#[clap(long = "new-owner")]
|
||||||
new_owner_addr: Option<Pubkey>,
|
new_owner_addr: Option<Pubkey>,
|
||||||
#[clap(long = "new-wh-prog")]
|
#[clap(long = "new-wh-prog")]
|
||||||
new_wh_prog: Option<Pubkey>,
|
new_wh_prog: Option<Pubkey>,
|
||||||
#[clap(long = "new-pyth-owner")]
|
#[clap(long = "new-pyth-owner")]
|
||||||
new_pyth_owner_addr: Option<Pubkey>,
|
new_pyth_owner_addr: Option<Pubkey>,
|
||||||
#[clap(long = "is-active")]
|
#[clap(long = "is-active")]
|
||||||
is_active: Option<bool>,
|
is_active: Option<bool>,
|
||||||
#[clap(long = "ops-owner")]
|
#[clap(long = "ops-owner")]
|
||||||
ops_owner_addr: Option<Pubkey>,
|
ops_owner_addr: Option<Pubkey>,
|
||||||
#[clap(long = "remove-ops-owner", conflicts_with = "ops-owner-addr")]
|
#[clap(long = "remove-ops-owner", conflicts_with = "ops-owner-addr")]
|
||||||
remove_ops_owner: bool,
|
remove_ops_owner: bool,
|
||||||
},
|
},
|
||||||
#[clap(
|
#[clap(
|
||||||
about = "Migrate existing pyth2wormhole program settings to a newer format version. Client version must match the deployed contract."
|
about = "Migrate existing pyth2wormhole program settings to a newer format version. Client version must match the deployed contract."
|
||||||
|
@ -131,7 +132,7 @@ pub enum Action {
|
||||||
default_value = "~/.config/solana/id.json",
|
default_value = "~/.config/solana/id.json",
|
||||||
help = "Keypair file for the current ops owner"
|
help = "Keypair file for the current ops owner"
|
||||||
)]
|
)]
|
||||||
ops_owner: String,
|
ops_owner: String,
|
||||||
#[clap(
|
#[clap(
|
||||||
index = 1,
|
index = 1,
|
||||||
possible_values = ["true", "false"],
|
possible_values = ["true", "false"],
|
||||||
|
|
|
@ -8,9 +8,9 @@ pub struct Config {
|
||||||
pub struct P2WSymbol {
|
pub struct P2WSymbol {
|
||||||
/// Optional human-readable name, never used on-chain; makes
|
/// Optional human-readable name, never used on-chain; makes
|
||||||
/// attester logs and the config easier to understand
|
/// attester logs and the config easier to understand
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
product: Pubkey,
|
product: Pubkey,
|
||||||
price: Pubkey,
|
price: Pubkey,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[testmod]
|
#[testmod]
|
||||||
|
|
|
@ -3,91 +3,88 @@ pub mod batch_state;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
use borsh::{
|
pub use {
|
||||||
BorshDeserialize,
|
attestation_cfg::{
|
||||||
BorshSerialize,
|
AttestationConditions,
|
||||||
};
|
AttestationConfig,
|
||||||
use log::{
|
P2WSymbol,
|
||||||
debug,
|
|
||||||
trace,
|
|
||||||
warn,
|
|
||||||
};
|
|
||||||
use pyth_sdk_solana::state::{
|
|
||||||
load_mapping_account,
|
|
||||||
load_price_account,
|
|
||||||
load_product_account,
|
|
||||||
};
|
|
||||||
use solana_client::nonblocking::rpc_client::RpcClient;
|
|
||||||
use solana_program::{
|
|
||||||
hash::Hash,
|
|
||||||
instruction::{
|
|
||||||
AccountMeta,
|
|
||||||
Instruction,
|
|
||||||
},
|
},
|
||||||
pubkey::Pubkey,
|
batch_state::BatchState,
|
||||||
system_program,
|
message::P2WMessageQueue,
|
||||||
sysvar::{
|
pyth2wormhole::Pyth2WormholeConfig,
|
||||||
clock,
|
util::{
|
||||||
rent,
|
RLMutex,
|
||||||
|
RLMutexGuard,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use {
|
||||||
signer::{
|
borsh::{
|
||||||
keypair::Keypair,
|
BorshDeserialize,
|
||||||
Signer,
|
BorshSerialize,
|
||||||
},
|
},
|
||||||
transaction::Transaction,
|
bridge::{
|
||||||
};
|
accounts::{
|
||||||
use solitaire::{
|
Bridge,
|
||||||
processors::seeded::Seeded,
|
FeeCollector,
|
||||||
AccountState,
|
Sequence,
|
||||||
ErrBox,
|
SequenceDerivationData,
|
||||||
};
|
},
|
||||||
|
types::ConsistencyLevel,
|
||||||
use bridge::{
|
|
||||||
accounts::{
|
|
||||||
Bridge,
|
|
||||||
FeeCollector,
|
|
||||||
Sequence,
|
|
||||||
SequenceDerivationData,
|
|
||||||
},
|
},
|
||||||
types::ConsistencyLevel,
|
log::{
|
||||||
};
|
debug,
|
||||||
|
trace,
|
||||||
use std::collections::{
|
warn,
|
||||||
HashMap,
|
|
||||||
HashSet,
|
|
||||||
};
|
|
||||||
|
|
||||||
use p2w_sdk::P2WEmitter;
|
|
||||||
|
|
||||||
use pyth2wormhole::{
|
|
||||||
config::{
|
|
||||||
OldP2WConfigAccount,
|
|
||||||
P2WConfigAccount,
|
|
||||||
},
|
},
|
||||||
message::{
|
p2w_sdk::P2WEmitter,
|
||||||
P2WMessage,
|
pyth2wormhole::{
|
||||||
P2WMessageDrvData,
|
config::{
|
||||||
|
OldP2WConfigAccount,
|
||||||
|
P2WConfigAccount,
|
||||||
|
},
|
||||||
|
message::{
|
||||||
|
P2WMessage,
|
||||||
|
P2WMessageDrvData,
|
||||||
|
},
|
||||||
|
AttestData,
|
||||||
|
},
|
||||||
|
pyth_sdk_solana::state::{
|
||||||
|
load_mapping_account,
|
||||||
|
load_price_account,
|
||||||
|
load_product_account,
|
||||||
|
},
|
||||||
|
solana_client::nonblocking::rpc_client::RpcClient,
|
||||||
|
solana_program::{
|
||||||
|
hash::Hash,
|
||||||
|
instruction::{
|
||||||
|
AccountMeta,
|
||||||
|
Instruction,
|
||||||
|
},
|
||||||
|
pubkey::Pubkey,
|
||||||
|
system_program,
|
||||||
|
sysvar::{
|
||||||
|
clock,
|
||||||
|
rent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
solana_sdk::{
|
||||||
|
signer::{
|
||||||
|
keypair::Keypair,
|
||||||
|
Signer,
|
||||||
|
},
|
||||||
|
transaction::Transaction,
|
||||||
|
},
|
||||||
|
solitaire::{
|
||||||
|
processors::seeded::Seeded,
|
||||||
|
AccountState,
|
||||||
|
ErrBox,
|
||||||
|
},
|
||||||
|
std::collections::{
|
||||||
|
HashMap,
|
||||||
|
HashSet,
|
||||||
},
|
},
|
||||||
AttestData,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use pyth2wormhole::Pyth2WormholeConfig;
|
|
||||||
|
|
||||||
pub use attestation_cfg::{
|
|
||||||
AttestationConditions,
|
|
||||||
AttestationConfig,
|
|
||||||
P2WSymbol,
|
|
||||||
};
|
|
||||||
pub use batch_state::BatchState;
|
|
||||||
pub use util::{
|
|
||||||
RLMutex,
|
|
||||||
RLMutexGuard,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use message::P2WMessageQueue;
|
|
||||||
|
|
||||||
/// Future-friendly version of solitaire::ErrBox
|
/// Future-friendly version of solitaire::ErrBox
|
||||||
pub type ErrBoxSend = Box<dyn std::error::Error + Send + Sync>;
|
pub type ErrBoxSend = Box<dyn std::error::Error + Send + Sync>;
|
||||||
|
|
||||||
|
@ -362,8 +359,8 @@ pub fn gen_attest_tx(
|
||||||
AccountMeta::new(
|
AccountMeta::new(
|
||||||
P2WMessage::key(
|
P2WMessage::key(
|
||||||
&P2WMessageDrvData {
|
&P2WMessageDrvData {
|
||||||
id: wh_msg_id,
|
id: wh_msg_id,
|
||||||
batch_size: symbols.len() as u16,
|
batch_size: symbols.len() as u16,
|
||||||
message_owner: payer.pubkey(),
|
message_owner: payer.pubkey(),
|
||||||
},
|
},
|
||||||
&p2w_addr,
|
&p2w_addr,
|
||||||
|
@ -384,7 +381,7 @@ pub fn gen_attest_tx(
|
||||||
let ix_data = (
|
let ix_data = (
|
||||||
pyth2wormhole::instruction::Instruction::Attest,
|
pyth2wormhole::instruction::Instruction::Attest,
|
||||||
AttestData {
|
AttestData {
|
||||||
consistency_level: ConsistencyLevel::Confirmed,
|
consistency_level: ConsistencyLevel::Confirmed,
|
||||||
message_account_id: wh_msg_id,
|
message_account_id: wh_msg_id,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,67 +1,65 @@
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
|
||||||
use std::{
|
use {
|
||||||
fs::File,
|
clap::Parser,
|
||||||
sync::Arc,
|
cli::{
|
||||||
time::{
|
Action,
|
||||||
Duration,
|
Cli,
|
||||||
Instant,
|
},
|
||||||
|
futures::future::{
|
||||||
|
Future,
|
||||||
|
TryFutureExt,
|
||||||
|
},
|
||||||
|
generic_array::GenericArray,
|
||||||
|
log::{
|
||||||
|
debug,
|
||||||
|
error,
|
||||||
|
info,
|
||||||
|
warn,
|
||||||
|
LevelFilter,
|
||||||
|
},
|
||||||
|
p2w_sdk::P2WEmitter,
|
||||||
|
pyth2wormhole::{
|
||||||
|
attest::P2W_MAX_BATCH_SIZE,
|
||||||
|
Pyth2WormholeConfig,
|
||||||
|
},
|
||||||
|
pyth2wormhole_client::*,
|
||||||
|
sha3::{
|
||||||
|
Digest,
|
||||||
|
Sha3_256,
|
||||||
|
},
|
||||||
|
solana_client::{
|
||||||
|
nonblocking::rpc_client::RpcClient,
|
||||||
|
rpc_config::RpcTransactionConfig,
|
||||||
|
},
|
||||||
|
solana_program::pubkey::Pubkey,
|
||||||
|
solana_sdk::{
|
||||||
|
commitment_config::CommitmentConfig,
|
||||||
|
signature::read_keypair_file,
|
||||||
|
signer::keypair::Keypair,
|
||||||
|
},
|
||||||
|
solana_transaction_status::UiTransactionEncoding,
|
||||||
|
solitaire::{
|
||||||
|
processors::seeded::Seeded,
|
||||||
|
ErrBox,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
fs::File,
|
||||||
|
sync::Arc,
|
||||||
|
time::{
|
||||||
|
Duration,
|
||||||
|
Instant,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tokio::{
|
||||||
|
sync::{
|
||||||
|
Mutex,
|
||||||
|
Semaphore,
|
||||||
|
},
|
||||||
|
task::JoinHandle,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
use futures::future::{
|
|
||||||
Future,
|
|
||||||
TryFutureExt,
|
|
||||||
};
|
|
||||||
use generic_array::GenericArray;
|
|
||||||
use log::{
|
|
||||||
debug,
|
|
||||||
error,
|
|
||||||
info,
|
|
||||||
warn,
|
|
||||||
LevelFilter,
|
|
||||||
};
|
|
||||||
use sha3::{
|
|
||||||
Digest,
|
|
||||||
Sha3_256,
|
|
||||||
};
|
|
||||||
use solana_client::{
|
|
||||||
nonblocking::rpc_client::RpcClient,
|
|
||||||
rpc_config::RpcTransactionConfig,
|
|
||||||
};
|
|
||||||
use solana_program::pubkey::Pubkey;
|
|
||||||
use solana_sdk::{
|
|
||||||
commitment_config::CommitmentConfig,
|
|
||||||
signature::read_keypair_file,
|
|
||||||
signer::keypair::Keypair,
|
|
||||||
};
|
|
||||||
use solana_transaction_status::UiTransactionEncoding;
|
|
||||||
use solitaire::{
|
|
||||||
processors::seeded::Seeded,
|
|
||||||
ErrBox,
|
|
||||||
};
|
|
||||||
use tokio::{
|
|
||||||
sync::{
|
|
||||||
Mutex,
|
|
||||||
Semaphore,
|
|
||||||
},
|
|
||||||
task::JoinHandle,
|
|
||||||
};
|
|
||||||
|
|
||||||
use cli::{
|
|
||||||
Action,
|
|
||||||
Cli,
|
|
||||||
};
|
|
||||||
|
|
||||||
use p2w_sdk::P2WEmitter;
|
|
||||||
|
|
||||||
use pyth2wormhole::{
|
|
||||||
attest::P2W_MAX_BATCH_SIZE,
|
|
||||||
Pyth2WormholeConfig,
|
|
||||||
};
|
|
||||||
use pyth2wormhole_client::*;
|
|
||||||
|
|
||||||
pub const SEQNO_PREFIX: &'static str = "Program log: Sequence: ";
|
pub const SEQNO_PREFIX: &'static str = "Program log: Sequence: ";
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
|
@ -143,12 +141,12 @@ async fn main() -> Result<(), ErrBox> {
|
||||||
p2w_addr,
|
p2w_addr,
|
||||||
read_keypair_file(&*shellexpand::tilde(&owner))?,
|
read_keypair_file(&*shellexpand::tilde(&owner))?,
|
||||||
Pyth2WormholeConfig {
|
Pyth2WormholeConfig {
|
||||||
owner: new_owner_addr.unwrap_or(old_config.owner),
|
owner: new_owner_addr.unwrap_or(old_config.owner),
|
||||||
wh_prog: new_wh_prog.unwrap_or(old_config.wh_prog),
|
wh_prog: new_wh_prog.unwrap_or(old_config.wh_prog),
|
||||||
pyth_owner: new_pyth_owner_addr.unwrap_or(old_config.pyth_owner),
|
pyth_owner: new_pyth_owner_addr.unwrap_or(old_config.pyth_owner),
|
||||||
is_active: is_active.unwrap_or(old_config.is_active),
|
is_active: is_active.unwrap_or(old_config.is_active),
|
||||||
max_batch_size: P2W_MAX_BATCH_SIZE,
|
max_batch_size: P2W_MAX_BATCH_SIZE,
|
||||||
ops_owner: new_ops_owner,
|
ops_owner: new_ops_owner,
|
||||||
},
|
},
|
||||||
latest_blockhash,
|
latest_blockhash,
|
||||||
)?;
|
)?;
|
||||||
|
@ -195,8 +193,8 @@ async fn main() -> Result<(), ErrBox> {
|
||||||
// between RPC accesses.
|
// between RPC accesses.
|
||||||
let rpc_cfg = Arc::new(RLMutex::new(
|
let rpc_cfg = Arc::new(RLMutex::new(
|
||||||
RpcCfg {
|
RpcCfg {
|
||||||
url: cli.rpc_url,
|
url: cli.rpc_url,
|
||||||
timeout: Duration::from_secs(confirmation_timeout_secs),
|
timeout: Duration::from_secs(confirmation_timeout_secs),
|
||||||
commitment: cli.commitment.clone(),
|
commitment: cli.commitment.clone(),
|
||||||
},
|
},
|
||||||
Duration::from_millis(attestation_cfg.min_rpc_interval_ms),
|
Duration::from_millis(attestation_cfg.min_rpc_interval_ms),
|
||||||
|
@ -374,8 +372,8 @@ async fn handle_attest_daemon_mode(
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RpcCfg {
|
pub struct RpcCfg {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub timeout: Duration,
|
pub timeout: Duration,
|
||||||
pub commitment: CommitmentConfig,
|
pub commitment: CommitmentConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,13 +509,13 @@ fn prepare_attestation_sched_jobs(
|
||||||
/// The argument count on attestation_sched_job got out of hand. This
|
/// The argument count on attestation_sched_job got out of hand. This
|
||||||
/// helps keep the correct order in check.
|
/// helps keep the correct order in check.
|
||||||
pub struct AttestationSchedJobArgs {
|
pub struct AttestationSchedJobArgs {
|
||||||
pub batch: BatchState,
|
pub batch: BatchState,
|
||||||
pub batch_no: usize,
|
pub batch_no: usize,
|
||||||
pub batch_count: usize,
|
pub batch_count: usize,
|
||||||
pub rpc_cfg: Arc<RLMutex<RpcCfg>>,
|
pub rpc_cfg: Arc<RLMutex<RpcCfg>>,
|
||||||
pub p2w_addr: Pubkey,
|
pub p2w_addr: Pubkey,
|
||||||
pub config: Pyth2WormholeConfig,
|
pub config: Pyth2WormholeConfig,
|
||||||
pub payer: Keypair,
|
pub payer: Keypair,
|
||||||
pub message_q_mtx: Arc<Mutex<P2WMessageQueue>>,
|
pub message_q_mtx: Arc<Mutex<P2WMessageQueue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,17 +619,17 @@ async fn attestation_sched_job(args: AttestationSchedJobArgs) -> Result<(), ErrB
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AttestationRetryJobArgs {
|
pub struct AttestationRetryJobArgs {
|
||||||
pub batch_no: usize,
|
pub batch_no: usize,
|
||||||
pub batch_count: usize,
|
pub batch_count: usize,
|
||||||
pub group_name: String,
|
pub group_name: String,
|
||||||
pub symbols: Vec<P2WSymbol>,
|
pub symbols: Vec<P2WSymbol>,
|
||||||
pub n_retries: usize,
|
pub n_retries: usize,
|
||||||
pub retry_interval: Duration,
|
pub retry_interval: Duration,
|
||||||
pub rpc_cfg: Arc<RLMutex<RpcCfg>>,
|
pub rpc_cfg: Arc<RLMutex<RpcCfg>>,
|
||||||
pub p2w_addr: Pubkey,
|
pub p2w_addr: Pubkey,
|
||||||
pub p2w_config: Pyth2WormholeConfig,
|
pub p2w_config: Pyth2WormholeConfig,
|
||||||
pub payer: Keypair,
|
pub payer: Keypair,
|
||||||
pub message_q_mtx: Arc<Mutex<P2WMessageQueue>>,
|
pub message_q_mtx: Arc<Mutex<P2WMessageQueue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A future that cranks a batch up to n_retries times, pausing for
|
/// A future that cranks a batch up to n_retries times, pausing for
|
||||||
|
@ -685,14 +683,14 @@ async fn attestation_retry_job(args: AttestationRetryJobArgs) -> Result<(), ErrB
|
||||||
/// Arguments for attestation_job(). This struct rules out same-type
|
/// Arguments for attestation_job(). This struct rules out same-type
|
||||||
/// ordering errors due to the large argument count
|
/// ordering errors due to the large argument count
|
||||||
pub struct AttestationJobArgs {
|
pub struct AttestationJobArgs {
|
||||||
pub rlmtx: Arc<RLMutex<RpcCfg>>,
|
pub rlmtx: Arc<RLMutex<RpcCfg>>,
|
||||||
pub batch_no: usize,
|
pub batch_no: usize,
|
||||||
pub batch_count: usize,
|
pub batch_count: usize,
|
||||||
pub group_name: String,
|
pub group_name: String,
|
||||||
pub p2w_addr: Pubkey,
|
pub p2w_addr: Pubkey,
|
||||||
pub config: Pyth2WormholeConfig,
|
pub config: Pyth2WormholeConfig,
|
||||||
pub payer: Keypair,
|
pub payer: Keypair,
|
||||||
pub symbols: Vec<P2WSymbol>,
|
pub symbols: Vec<P2WSymbol>,
|
||||||
pub max_jobs_sema: Arc<Semaphore>,
|
pub max_jobs_sema: Arc<Semaphore>,
|
||||||
pub message_q_mtx: Arc<Mutex<P2WMessageQueue>>,
|
pub message_q_mtx: Arc<Mutex<P2WMessageQueue>>,
|
||||||
}
|
}
|
||||||
|
@ -743,8 +741,8 @@ async fn attestation_job(args: AttestationJobArgs) -> Result<(), ErrBoxSend> {
|
||||||
.get_transaction_with_config(
|
.get_transaction_with_config(
|
||||||
&sig,
|
&sig,
|
||||||
RpcTransactionConfig {
|
RpcTransactionConfig {
|
||||||
encoding: Some(UiTransactionEncoding::Json),
|
encoding: Some(UiTransactionEncoding::Json),
|
||||||
commitment: Some(rpc.commitment()),
|
commitment: Some(rpc.commitment()),
|
||||||
max_supported_transaction_version: None,
|
max_supported_transaction_version: None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
//! Re-usable message scheme for pyth2wormhole
|
//! Re-usable message scheme for pyth2wormhole
|
||||||
|
|
||||||
use log::debug;
|
use {
|
||||||
use std::{
|
crate::ErrBoxSend,
|
||||||
collections::VecDeque,
|
log::debug,
|
||||||
time::{
|
std::{
|
||||||
Duration,
|
collections::VecDeque,
|
||||||
Instant,
|
time::{
|
||||||
|
Duration,
|
||||||
|
Instant,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ErrBoxSend;
|
|
||||||
|
|
||||||
/// One of the accounts tracked by the attestation client.
|
/// One of the accounts tracked by the attestation client.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct P2WMessageAccount {
|
pub struct P2WMessageAccount {
|
||||||
/// Unique ID that lets us derive unique accounts for use on-chain
|
/// Unique ID that lets us derive unique accounts for use on-chain
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
/// Last time we've posted a message to wormhole with this account
|
/// Last time we've posted a message to wormhole with this account
|
||||||
pub last_used: Instant,
|
pub last_used: Instant,
|
||||||
}
|
}
|
||||||
|
@ -24,7 +25,7 @@ pub struct P2WMessageAccount {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct P2WMessageQueue {
|
pub struct P2WMessageQueue {
|
||||||
/// The tracked accounts. Sorted from oldest to newest, as guaranteed by get_account()
|
/// The tracked accounts. Sorted from oldest to newest, as guaranteed by get_account()
|
||||||
accounts: VecDeque<P2WMessageAccount>,
|
accounts: VecDeque<P2WMessageAccount>,
|
||||||
/// How much time needs to pass between reuses
|
/// How much time needs to pass between reuses
|
||||||
grace_period: Duration,
|
grace_period: Duration,
|
||||||
/// A hard cap on how many accounts will be created.
|
/// A hard cap on how many accounts will be created.
|
||||||
|
@ -72,13 +73,13 @@ impl P2WMessageQueue {
|
||||||
|
|
||||||
// Use a new account instead
|
// Use a new account instead
|
||||||
P2WMessageAccount {
|
P2WMessageAccount {
|
||||||
id: self.accounts.len() as u64,
|
id: self.accounts.len() as u64,
|
||||||
last_used: Instant::now(),
|
last_used: Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Base case: Queue is empty, use a new account
|
// Base case: Queue is empty, use a new account
|
||||||
None => P2WMessageAccount {
|
None => P2WMessageAccount {
|
||||||
id: self.accounts.len() as u64,
|
id: self.accounts.len() as u64,
|
||||||
last_used: Instant::now(),
|
last_used: Instant::now(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
use log::trace;
|
use {
|
||||||
|
log::trace,
|
||||||
use std::{
|
std::{
|
||||||
ops::{
|
ops::{
|
||||||
Deref,
|
Deref,
|
||||||
DerefMut,
|
DerefMut,
|
||||||
|
},
|
||||||
|
time::{
|
||||||
|
Duration,
|
||||||
|
Instant,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
time::{
|
tokio::sync::{
|
||||||
Duration,
|
Mutex,
|
||||||
Instant,
|
MutexGuard,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tokio::sync::{
|
|
||||||
Mutex,
|
|
||||||
MutexGuard,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Rate-limited mutex. Ensures there's a period of minimum rl_interval between lock acquisitions
|
/// Rate-limited mutex. Ensures there's a period of minimum rl_interval between lock acquisitions
|
||||||
pub struct RLMutex<T> {
|
pub struct RLMutex<T> {
|
||||||
mtx: Mutex<RLMutexState<T>>,
|
mtx: Mutex<RLMutexState<T>>,
|
||||||
rl_interval: Duration,
|
rl_interval: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ pub struct RLMutex<T> {
|
||||||
pub struct RLMutexState<T> {
|
pub struct RLMutexState<T> {
|
||||||
/// Helps make sure regular passage of time is subtracted from sleep duration
|
/// Helps make sure regular passage of time is subtracted from sleep duration
|
||||||
last_released: Instant,
|
last_released: Instant,
|
||||||
val: T,
|
val: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Deref for RLMutexState<T> {
|
impl<T> Deref for RLMutexState<T> {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
//! Trivial program for mocking other programs easily
|
//! Trivial program for mocking other programs easily
|
||||||
use solana_program::{
|
use {
|
||||||
account_info::AccountInfo,
|
solana_program::{
|
||||||
msg,
|
account_info::AccountInfo,
|
||||||
program_error::ProgramError,
|
msg,
|
||||||
|
program_error::ProgramError,
|
||||||
|
},
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
|
|
||||||
use solana_program_test::*;
|
|
||||||
|
|
||||||
pub fn passthrough_entrypoint(
|
pub fn passthrough_entrypoint(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
//! This module contains test fixtures for instantiating plausible
|
//! This module contains test fixtures for instantiating plausible
|
||||||
//! Pyth accounts for testing purposes.
|
//! Pyth accounts for testing purposes.
|
||||||
use solana_program_test::*;
|
use {
|
||||||
|
pyth_client::{
|
||||||
use solana_sdk::{
|
AccKey,
|
||||||
account::Account,
|
AccountType,
|
||||||
pubkey::Pubkey,
|
Price,
|
||||||
rent::Rent,
|
Product,
|
||||||
};
|
MAGIC,
|
||||||
|
PROD_ATTR_SIZE,
|
||||||
use pyth_client::{
|
VERSION,
|
||||||
AccKey,
|
},
|
||||||
AccountType,
|
solana_program_test::*,
|
||||||
Price,
|
solana_sdk::{
|
||||||
Product,
|
account::Account,
|
||||||
MAGIC,
|
pubkey::Pubkey,
|
||||||
PROD_ATTR_SIZE,
|
rent::Rent,
|
||||||
VERSION,
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a pair of brand new product/price accounts that point at each other
|
/// Create a pair of brand new product/price accounts that point at each other
|
||||||
|
@ -27,14 +27,14 @@ pub fn add_test_symbol(pt: &mut ProgramTest, owner: &Pubkey) -> (Pubkey, Pubkey)
|
||||||
// Instantiate
|
// Instantiate
|
||||||
let prod = {
|
let prod = {
|
||||||
Product {
|
Product {
|
||||||
magic: MAGIC,
|
magic: MAGIC,
|
||||||
ver: VERSION,
|
ver: VERSION,
|
||||||
atype: AccountType::Product as u32,
|
atype: AccountType::Product as u32,
|
||||||
size: 0,
|
size: 0,
|
||||||
px_acc: AccKey {
|
px_acc: AccKey {
|
||||||
val: price_id.to_bytes(),
|
val: price_id.to_bytes(),
|
||||||
},
|
},
|
||||||
attr: [0u8; PROD_ATTR_SIZE],
|
attr: [0u8; PROD_ATTR_SIZE],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,16 +66,16 @@ pub fn add_test_symbol(pt: &mut ProgramTest, owner: &Pubkey) -> (Pubkey, Pubkey)
|
||||||
|
|
||||||
// Populate the accounts
|
// Populate the accounts
|
||||||
let mut prod_acc = Account {
|
let mut prod_acc = Account {
|
||||||
lamports: prod_lamports,
|
lamports: prod_lamports,
|
||||||
data: (*prod_bytes).to_vec(),
|
data: (*prod_bytes).to_vec(),
|
||||||
owner: owner.clone(),
|
owner: owner.clone(),
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
executable: false,
|
executable: false,
|
||||||
};
|
};
|
||||||
let mut price_acc = Account {
|
let mut price_acc = Account {
|
||||||
lamports: price_lamports,
|
lamports: price_lamports,
|
||||||
data: (*price_bytes).to_vec(),
|
data: (*price_bytes).to_vec(),
|
||||||
owner: owner.clone(),
|
owner: owner.clone(),
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
executable: false,
|
executable: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,41 +1,39 @@
|
||||||
pub mod fixtures;
|
pub mod fixtures;
|
||||||
|
|
||||||
use solana_program_test::*;
|
use {
|
||||||
use solana_sdk::{
|
bridge::accounts::{
|
||||||
account::Account,
|
Bridge,
|
||||||
instruction::{
|
BridgeConfig,
|
||||||
AccountMeta,
|
BridgeData,
|
||||||
Instruction,
|
|
||||||
},
|
},
|
||||||
pubkey::Pubkey,
|
fixtures::{
|
||||||
rent::Rent,
|
passthrough,
|
||||||
signature::Signer,
|
pyth,
|
||||||
signer::keypair::Keypair,
|
},
|
||||||
transaction::Transaction,
|
pyth2wormhole::config::{
|
||||||
};
|
P2WConfigAccount,
|
||||||
|
Pyth2WormholeConfig,
|
||||||
use bridge::accounts::{
|
},
|
||||||
Bridge,
|
pyth2wormhole_client as p2wc,
|
||||||
BridgeConfig,
|
solana_program_test::*,
|
||||||
BridgeData,
|
solana_sdk::{
|
||||||
};
|
account::Account,
|
||||||
|
instruction::{
|
||||||
use pyth2wormhole::config::{
|
AccountMeta,
|
||||||
P2WConfigAccount,
|
Instruction,
|
||||||
Pyth2WormholeConfig,
|
},
|
||||||
};
|
pubkey::Pubkey,
|
||||||
use pyth2wormhole_client as p2wc;
|
rent::Rent,
|
||||||
use solitaire::{
|
signature::Signer,
|
||||||
processors::seeded::Seeded,
|
signer::keypair::Keypair,
|
||||||
AccountState,
|
transaction::Transaction,
|
||||||
BorshSerialize,
|
},
|
||||||
};
|
solitaire::{
|
||||||
|
processors::seeded::Seeded,
|
||||||
use std::time::Duration;
|
AccountState,
|
||||||
|
BorshSerialize,
|
||||||
use fixtures::{
|
},
|
||||||
passthrough,
|
std::time::Duration,
|
||||||
pyth,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
@ -77,9 +75,9 @@ async fn test_happy_path() -> Result<(), p2wc::ErrBoxSend> {
|
||||||
// Plant a filled config account
|
// Plant a filled config account
|
||||||
let p2w_config_bytes = p2w_config.try_to_vec()?;
|
let p2w_config_bytes = p2w_config.try_to_vec()?;
|
||||||
let p2w_config_account = Account {
|
let p2w_config_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(p2w_config_bytes.len()),
|
lamports: Rent::default().minimum_balance(p2w_config_bytes.len()),
|
||||||
data: p2w_config_bytes,
|
data: p2w_config_bytes,
|
||||||
owner: p2w_program_id,
|
owner: p2w_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
};
|
};
|
||||||
|
@ -91,9 +89,9 @@ async fn test_happy_path() -> Result<(), p2wc::ErrBoxSend> {
|
||||||
// Plant a bridge config
|
// Plant a bridge config
|
||||||
let bridge_config_bytes = bridge_config.try_to_vec()?;
|
let bridge_config_bytes = bridge_config.try_to_vec()?;
|
||||||
let wh_bridge_config_account = Account {
|
let wh_bridge_config_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(bridge_config_bytes.len()),
|
lamports: Rent::default().minimum_balance(bridge_config_bytes.len()),
|
||||||
data: bridge_config_bytes,
|
data: bridge_config_bytes,
|
||||||
owner: wh_fixture_program_id,
|
owner: wh_fixture_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
};
|
};
|
||||||
|
@ -109,9 +107,9 @@ async fn test_happy_path() -> Result<(), p2wc::ErrBoxSend> {
|
||||||
let mut ctx = p2w_test.start_with_context().await;
|
let mut ctx = p2w_test.start_with_context().await;
|
||||||
|
|
||||||
let symbols = vec![p2wc::P2WSymbol {
|
let symbols = vec![p2wc::P2WSymbol {
|
||||||
name: Some("Mock symbol".to_owned()),
|
name: Some("Mock symbol".to_owned()),
|
||||||
product_addr: prod_id,
|
product_addr: prod_id,
|
||||||
price_addr: price_id,
|
price_addr: price_id,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let attest_tx = p2wc::gen_attest_tx(
|
let attest_tx = p2wc::gen_attest_tx(
|
||||||
|
|
|
@ -2,45 +2,43 @@
|
||||||
|
|
||||||
pub mod fixtures;
|
pub mod fixtures;
|
||||||
|
|
||||||
use solana_program::system_program;
|
use {
|
||||||
use solana_program_test::*;
|
bridge::accounts::{
|
||||||
use solana_sdk::{
|
Bridge,
|
||||||
account::Account,
|
BridgeConfig,
|
||||||
instruction::{
|
BridgeData,
|
||||||
AccountMeta,
|
},
|
||||||
Instruction,
|
fixtures::{
|
||||||
|
passthrough,
|
||||||
|
pyth,
|
||||||
|
},
|
||||||
|
log::info,
|
||||||
|
pyth2wormhole::config::{
|
||||||
|
OldP2WConfigAccount,
|
||||||
|
OldPyth2WormholeConfig,
|
||||||
|
P2WConfigAccount,
|
||||||
|
Pyth2WormholeConfig,
|
||||||
|
},
|
||||||
|
pyth2wormhole_client as p2wc,
|
||||||
|
solana_program::system_program,
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::{
|
||||||
|
account::Account,
|
||||||
|
instruction::{
|
||||||
|
AccountMeta,
|
||||||
|
Instruction,
|
||||||
|
},
|
||||||
|
pubkey::Pubkey,
|
||||||
|
rent::Rent,
|
||||||
|
signature::Signer,
|
||||||
|
signer::keypair::Keypair,
|
||||||
|
transaction::Transaction,
|
||||||
|
},
|
||||||
|
solitaire::{
|
||||||
|
processors::seeded::Seeded,
|
||||||
|
AccountState,
|
||||||
|
BorshSerialize,
|
||||||
},
|
},
|
||||||
pubkey::Pubkey,
|
|
||||||
rent::Rent,
|
|
||||||
signature::Signer,
|
|
||||||
signer::keypair::Keypair,
|
|
||||||
transaction::Transaction,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bridge::accounts::{
|
|
||||||
Bridge,
|
|
||||||
BridgeConfig,
|
|
||||||
BridgeData,
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
use pyth2wormhole::config::{
|
|
||||||
OldP2WConfigAccount,
|
|
||||||
OldPyth2WormholeConfig,
|
|
||||||
P2WConfigAccount,
|
|
||||||
Pyth2WormholeConfig,
|
|
||||||
};
|
|
||||||
use pyth2wormhole_client as p2wc;
|
|
||||||
use solitaire::{
|
|
||||||
processors::seeded::Seeded,
|
|
||||||
AccountState,
|
|
||||||
BorshSerialize,
|
|
||||||
};
|
|
||||||
|
|
||||||
use fixtures::{
|
|
||||||
passthrough,
|
|
||||||
pyth,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
@ -75,9 +73,9 @@ async fn test_migrate_works() -> Result<(), solitaire::ErrBox> {
|
||||||
// Plant filled config accounts
|
// Plant filled config accounts
|
||||||
let old_p2w_config_bytes = old_p2w_config.try_to_vec()?;
|
let old_p2w_config_bytes = old_p2w_config.try_to_vec()?;
|
||||||
let old_p2w_config_account = Account {
|
let old_p2w_config_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(old_p2w_config_bytes.len()),
|
lamports: Rent::default().minimum_balance(old_p2w_config_bytes.len()),
|
||||||
data: old_p2w_config_bytes,
|
data: old_p2w_config_bytes,
|
||||||
owner: p2w_program_id,
|
owner: p2w_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
};
|
};
|
||||||
|
@ -147,9 +145,9 @@ async fn test_migrate_already_migrated() -> Result<(), solitaire::ErrBox> {
|
||||||
// Plant filled config accounts
|
// Plant filled config accounts
|
||||||
let old_p2w_config_bytes = old_p2w_config.try_to_vec()?;
|
let old_p2w_config_bytes = old_p2w_config.try_to_vec()?;
|
||||||
let old_p2w_config_account = Account {
|
let old_p2w_config_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(old_p2w_config_bytes.len()),
|
lamports: Rent::default().minimum_balance(old_p2w_config_bytes.len()),
|
||||||
data: old_p2w_config_bytes,
|
data: old_p2w_config_bytes,
|
||||||
owner: p2w_program_id,
|
owner: p2w_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
};
|
};
|
||||||
|
@ -157,9 +155,9 @@ async fn test_migrate_already_migrated() -> Result<(), solitaire::ErrBox> {
|
||||||
|
|
||||||
let new_p2w_config_bytes = new_p2w_config.try_to_vec()?;
|
let new_p2w_config_bytes = new_p2w_config.try_to_vec()?;
|
||||||
let new_p2w_config_account = Account {
|
let new_p2w_config_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(new_p2w_config_bytes.len()),
|
lamports: Rent::default().minimum_balance(new_p2w_config_bytes.len()),
|
||||||
data: new_p2w_config_bytes,
|
data: new_p2w_config_bytes,
|
||||||
owner: p2w_program_id,
|
owner: p2w_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
pub mod fixtures;
|
pub mod fixtures;
|
||||||
|
|
||||||
use borsh::BorshDeserialize;
|
use {
|
||||||
use p2wc::get_config_account;
|
borsh::BorshDeserialize,
|
||||||
use solana_program_test::*;
|
p2wc::get_config_account,
|
||||||
use solana_sdk::{
|
pyth2wormhole::config::{
|
||||||
account::Account,
|
P2WConfigAccount,
|
||||||
pubkey::Pubkey,
|
Pyth2WormholeConfig,
|
||||||
rent::Rent,
|
},
|
||||||
signature::Signer,
|
pyth2wormhole_client as p2wc,
|
||||||
signer::keypair::Keypair,
|
solana_program_test::*,
|
||||||
};
|
solana_sdk::{
|
||||||
|
account::Account,
|
||||||
use pyth2wormhole::config::{
|
pubkey::Pubkey,
|
||||||
P2WConfigAccount,
|
rent::Rent,
|
||||||
Pyth2WormholeConfig,
|
signature::Signer,
|
||||||
};
|
signer::keypair::Keypair,
|
||||||
use pyth2wormhole_client as p2wc;
|
},
|
||||||
use solitaire::{
|
solitaire::{
|
||||||
processors::seeded::Seeded,
|
processors::seeded::Seeded,
|
||||||
AccountState,
|
AccountState,
|
||||||
BorshSerialize,
|
BorshSerialize,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn clone_keypair(keypair: &Keypair) -> Keypair {
|
fn clone_keypair(keypair: &Keypair) -> Keypair {
|
||||||
|
@ -58,9 +59,9 @@ async fn test_setting_is_active_works() -> Result<(), p2wc::ErrBoxSend> {
|
||||||
// Plant a filled config account
|
// Plant a filled config account
|
||||||
let p2w_config_bytes = p2w_config.try_to_vec()?;
|
let p2w_config_bytes = p2w_config.try_to_vec()?;
|
||||||
let p2w_config_account = Account {
|
let p2w_config_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(p2w_config_bytes.len()),
|
lamports: Rent::default().minimum_balance(p2w_config_bytes.len()),
|
||||||
data: p2w_config_bytes,
|
data: p2w_config_bytes,
|
||||||
owner: p2w_program_id,
|
owner: p2w_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
};
|
};
|
||||||
|
@ -145,12 +146,12 @@ async fn test_setting_is_active_does_not_work_without_ops_owner() -> Result<(),
|
||||||
|
|
||||||
// On-chain state
|
// On-chain state
|
||||||
let p2w_config = Pyth2WormholeConfig {
|
let p2w_config = Pyth2WormholeConfig {
|
||||||
owner: p2w_owner,
|
owner: p2w_owner,
|
||||||
wh_prog: wh_fixture_program_id,
|
wh_prog: wh_fixture_program_id,
|
||||||
max_batch_size: pyth2wormhole::attest::P2W_MAX_BATCH_SIZE,
|
max_batch_size: pyth2wormhole::attest::P2W_MAX_BATCH_SIZE,
|
||||||
pyth_owner: pyth_owner.pubkey(),
|
pyth_owner: pyth_owner.pubkey(),
|
||||||
is_active: true,
|
is_active: true,
|
||||||
ops_owner: None,
|
ops_owner: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Populate test environment
|
// Populate test environment
|
||||||
|
@ -163,9 +164,9 @@ async fn test_setting_is_active_does_not_work_without_ops_owner() -> Result<(),
|
||||||
// Plant a filled config account
|
// Plant a filled config account
|
||||||
let p2w_config_bytes = p2w_config.try_to_vec()?;
|
let p2w_config_bytes = p2w_config.try_to_vec()?;
|
||||||
let p2w_config_account = Account {
|
let p2w_config_account = Account {
|
||||||
lamports: Rent::default().minimum_balance(p2w_config_bytes.len()),
|
lamports: Rent::default().minimum_balance(p2w_config_bytes.len()),
|
||||||
data: p2w_config_bytes,
|
data: p2w_config_bytes,
|
||||||
owner: p2w_program_id,
|
owner: p2w_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: 0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
//! CLI options
|
//! CLI options
|
||||||
use clap::{
|
use {
|
||||||
Parser,
|
clap::{
|
||||||
Subcommand,
|
Parser,
|
||||||
|
Subcommand,
|
||||||
|
},
|
||||||
|
solana_sdk::pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(
|
#[clap(
|
||||||
about = "A cli for the remote executor",
|
about = "A cli for the remote executor",
|
||||||
|
@ -21,33 +22,33 @@ pub enum Action {
|
||||||
#[clap(about = "Get set upgrade authority payload for squads-cli")]
|
#[clap(about = "Get set upgrade authority payload for squads-cli")]
|
||||||
GetSetConfig {
|
GetSetConfig {
|
||||||
#[clap(long, help = "Program id")]
|
#[clap(long, help = "Program id")]
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
#[clap(long, help = "Current owner")]
|
#[clap(long, help = "Current owner")]
|
||||||
owner: Pubkey,
|
owner: Pubkey,
|
||||||
#[clap(long, help = "Payer")]
|
#[clap(long, help = "Payer")]
|
||||||
payer: Pubkey,
|
payer: Pubkey,
|
||||||
#[clap(long, help = "Config : New owner")]
|
#[clap(long, help = "Config : New owner")]
|
||||||
new_owner: Pubkey,
|
new_owner: Pubkey,
|
||||||
#[clap(long, help = "Config : Wormhole program id")]
|
#[clap(long, help = "Config : Wormhole program id")]
|
||||||
wormhole: Pubkey,
|
wormhole: Pubkey,
|
||||||
#[clap(long, help = "Config : Pyth program id")]
|
#[clap(long, help = "Config : Pyth program id")]
|
||||||
pyth_owner: Pubkey,
|
pyth_owner: Pubkey,
|
||||||
#[clap(long, help = "Config : Max batch size")]
|
#[clap(long, help = "Config : Max batch size")]
|
||||||
max_batch_size: u16,
|
max_batch_size: u16,
|
||||||
#[clap(long, help = "Config : Is active")]
|
#[clap(long, help = "Config : Is active")]
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
#[clap(long, help = "Config : Ops owner")]
|
#[clap(long, help = "Config : Ops owner")]
|
||||||
ops_owner: Option<Pubkey>,
|
ops_owner: Option<Pubkey>,
|
||||||
},
|
},
|
||||||
#[clap(about = "Get upgrade program payload for squads-cli")]
|
#[clap(about = "Get upgrade program payload for squads-cli")]
|
||||||
GetSetIsActive {
|
GetSetIsActive {
|
||||||
#[clap(long, help = "Program id")]
|
#[clap(long, help = "Program id")]
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
#[clap(long, help = "Current ops owner")]
|
#[clap(long, help = "Current ops owner")]
|
||||||
ops_owner: Pubkey,
|
ops_owner: Pubkey,
|
||||||
#[clap(long, help = "Payer")]
|
#[clap(long, help = "Payer")]
|
||||||
payer: Pubkey,
|
payer: Pubkey,
|
||||||
#[clap(long, help = "Config : Is active")]
|
#[clap(long, help = "Config : Is active")]
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
use anyhow::Result;
|
use {
|
||||||
use clap::Parser;
|
anyhow::Result,
|
||||||
use cli::{
|
borsh::BorshSerialize,
|
||||||
Action,
|
clap::Parser,
|
||||||
Cli,
|
cli::{
|
||||||
};
|
Action,
|
||||||
use pyth2wormhole_client::{
|
Cli,
|
||||||
get_set_config_ix,
|
},
|
||||||
Pyth2WormholeConfig,
|
pyth2wormhole_client::{
|
||||||
};
|
get_set_config_ix,
|
||||||
|
get_set_is_active_ix,
|
||||||
use borsh::BorshSerialize;
|
Pyth2WormholeConfig,
|
||||||
use pyth2wormhole_client::get_set_is_active_ix;
|
},
|
||||||
use remote_executor::state::governance_payload::{
|
remote_executor::state::governance_payload::{
|
||||||
ExecutorPayload,
|
ExecutorPayload,
|
||||||
GovernanceHeader,
|
GovernanceHeader,
|
||||||
InstructionData,
|
InstructionData,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
|
@ -43,7 +44,7 @@ fn main() -> Result<()> {
|
||||||
};
|
};
|
||||||
let ix = get_set_config_ix(&program_id, &owner, &payer, new_config).unwrap();
|
let ix = get_set_config_ix(&program_id, &owner, &payer, new_config).unwrap();
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: GovernanceHeader::executor_governance_header(),
|
header: GovernanceHeader::executor_governance_header(),
|
||||||
instructions: vec![InstructionData::from(&ix)],
|
instructions: vec![InstructionData::from(&ix)],
|
||||||
}
|
}
|
||||||
.try_to_vec()?;
|
.try_to_vec()?;
|
||||||
|
@ -58,7 +59,7 @@ fn main() -> Result<()> {
|
||||||
} => {
|
} => {
|
||||||
let ix = get_set_is_active_ix(&program_id, &ops_owner, &payer, is_active).unwrap();
|
let ix = get_set_is_active_ix(&program_id, &ops_owner, &payer, is_active).unwrap();
|
||||||
let payload = ExecutorPayload {
|
let payload = ExecutorPayload {
|
||||||
header: GovernanceHeader::executor_governance_header(),
|
header: GovernanceHeader::executor_governance_header(),
|
||||||
instructions: vec![InstructionData::from(&ix)],
|
instructions: vec![InstructionData::from(&ix)],
|
||||||
}
|
}
|
||||||
.try_to_vec()?;
|
.try_to_vec()?;
|
||||||
|
|
|
@ -1,60 +1,59 @@
|
||||||
use crate::{
|
use {
|
||||||
config::P2WConfigAccount,
|
crate::{
|
||||||
message::{
|
config::P2WConfigAccount,
|
||||||
P2WMessage,
|
message::{
|
||||||
P2WMessageDrvData,
|
P2WMessage,
|
||||||
|
P2WMessageDrvData,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
borsh::{
|
||||||
use borsh::{
|
BorshDeserialize,
|
||||||
BorshDeserialize,
|
BorshSerialize,
|
||||||
BorshSerialize,
|
|
||||||
};
|
|
||||||
use solana_program::{
|
|
||||||
clock::Clock,
|
|
||||||
instruction::{
|
|
||||||
AccountMeta,
|
|
||||||
Instruction,
|
|
||||||
},
|
},
|
||||||
program::{
|
bridge::{
|
||||||
invoke,
|
accounts::BridgeData,
|
||||||
invoke_signed,
|
types::ConsistencyLevel,
|
||||||
|
PostMessageData,
|
||||||
|
},
|
||||||
|
p2w_sdk::{
|
||||||
|
BatchPriceAttestation,
|
||||||
|
Identifier,
|
||||||
|
P2WEmitter,
|
||||||
|
PriceAttestation,
|
||||||
|
},
|
||||||
|
solana_program::{
|
||||||
|
clock::Clock,
|
||||||
|
instruction::{
|
||||||
|
AccountMeta,
|
||||||
|
Instruction,
|
||||||
|
},
|
||||||
|
program::{
|
||||||
|
invoke,
|
||||||
|
invoke_signed,
|
||||||
|
},
|
||||||
|
program_error::ProgramError,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
rent::Rent,
|
||||||
|
system_instruction,
|
||||||
|
sysvar::Sysvar as SolanaSysvar,
|
||||||
|
},
|
||||||
|
solitaire::{
|
||||||
|
invoke_seeded,
|
||||||
|
trace,
|
||||||
|
AccountState,
|
||||||
|
Derive,
|
||||||
|
ExecutionContext,
|
||||||
|
FromAccounts,
|
||||||
|
Info,
|
||||||
|
Keyed,
|
||||||
|
Mut,
|
||||||
|
Peel,
|
||||||
|
Result as SoliResult,
|
||||||
|
Seeded,
|
||||||
|
Signer,
|
||||||
|
SolitaireError,
|
||||||
|
Sysvar,
|
||||||
},
|
},
|
||||||
program_error::ProgramError,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
rent::Rent,
|
|
||||||
system_instruction,
|
|
||||||
sysvar::Sysvar as SolanaSysvar,
|
|
||||||
};
|
|
||||||
|
|
||||||
use p2w_sdk::{
|
|
||||||
BatchPriceAttestation,
|
|
||||||
Identifier,
|
|
||||||
P2WEmitter,
|
|
||||||
PriceAttestation,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bridge::{
|
|
||||||
accounts::BridgeData,
|
|
||||||
types::ConsistencyLevel,
|
|
||||||
PostMessageData,
|
|
||||||
};
|
|
||||||
|
|
||||||
use solitaire::{
|
|
||||||
invoke_seeded,
|
|
||||||
trace,
|
|
||||||
AccountState,
|
|
||||||
Derive,
|
|
||||||
ExecutionContext,
|
|
||||||
FromAccounts,
|
|
||||||
Info,
|
|
||||||
Keyed,
|
|
||||||
Mut,
|
|
||||||
Peel,
|
|
||||||
Result as SoliResult,
|
|
||||||
Seeded,
|
|
||||||
Signer,
|
|
||||||
SolitaireError,
|
|
||||||
Sysvar,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Important: must be manually maintained until native Solitaire
|
/// Important: must be manually maintained until native Solitaire
|
||||||
|
@ -69,26 +68,26 @@ pub const P2W_MAX_BATCH_SIZE: u16 = 5;
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
pub struct Attest<'b> {
|
pub struct Attest<'b> {
|
||||||
// Payer also used for wormhole
|
// Payer also used for wormhole
|
||||||
pub payer: Mut<Signer<Info<'b>>>,
|
pub payer: Mut<Signer<Info<'b>>>,
|
||||||
pub system_program: Info<'b>,
|
pub system_program: Info<'b>,
|
||||||
pub config: P2WConfigAccount<'b, { AccountState::Initialized }>,
|
pub config: P2WConfigAccount<'b, { AccountState::Initialized }>,
|
||||||
|
|
||||||
// Hardcoded product/price pairs, bypassing Solitaire's variable-length limitations
|
// Hardcoded product/price pairs, bypassing Solitaire's variable-length limitations
|
||||||
// Any change to the number of accounts must include an appropriate change to P2W_MAX_BATCH_SIZE
|
// Any change to the number of accounts must include an appropriate change to P2W_MAX_BATCH_SIZE
|
||||||
pub pyth_product: Info<'b>,
|
pub pyth_product: Info<'b>,
|
||||||
pub pyth_price: Info<'b>,
|
pub pyth_price: Info<'b>,
|
||||||
|
|
||||||
pub pyth_product2: Option<Info<'b>>,
|
pub pyth_product2: Option<Info<'b>>,
|
||||||
pub pyth_price2: Option<Info<'b>>,
|
pub pyth_price2: Option<Info<'b>>,
|
||||||
|
|
||||||
pub pyth_product3: Option<Info<'b>>,
|
pub pyth_product3: Option<Info<'b>>,
|
||||||
pub pyth_price3: Option<Info<'b>>,
|
pub pyth_price3: Option<Info<'b>>,
|
||||||
|
|
||||||
pub pyth_product4: Option<Info<'b>>,
|
pub pyth_product4: Option<Info<'b>>,
|
||||||
pub pyth_price4: Option<Info<'b>>,
|
pub pyth_price4: Option<Info<'b>>,
|
||||||
|
|
||||||
pub pyth_product5: Option<Info<'b>>,
|
pub pyth_product5: Option<Info<'b>>,
|
||||||
pub pyth_price5: Option<Info<'b>>,
|
pub pyth_price5: Option<Info<'b>>,
|
||||||
|
|
||||||
// Did you read the comment near `pyth_product`?
|
// Did you read the comment near `pyth_product`?
|
||||||
// pub pyth_product6: Option<Info<'b>>,
|
// pub pyth_product6: Option<Info<'b>>,
|
||||||
|
@ -139,7 +138,7 @@ pub struct Attest<'b> {
|
||||||
|
|
||||||
#[derive(BorshDeserialize, BorshSerialize)]
|
#[derive(BorshDeserialize, BorshSerialize)]
|
||||||
pub struct AttestData {
|
pub struct AttestData {
|
||||||
pub consistency_level: ConsistencyLevel,
|
pub consistency_level: ConsistencyLevel,
|
||||||
pub message_account_id: u64,
|
pub message_account_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,8 +265,8 @@ pub fn attest(ctx: &ExecutionContext, accs: &mut Attest, data: AttestData) -> So
|
||||||
|
|
||||||
let wh_msg_drv_data = P2WMessageDrvData {
|
let wh_msg_drv_data = P2WMessageDrvData {
|
||||||
message_owner: accs.payer.key.clone(),
|
message_owner: accs.payer.key.clone(),
|
||||||
batch_size: batch_attestation.price_attestations.len() as u16,
|
batch_size: batch_attestation.price_attestations.len() as u16,
|
||||||
id: data.message_account_id,
|
id: data.message_account_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !P2WMessage::key(&wh_msg_drv_data, ctx.program_id).eq(accs.wh_message.info().key) {
|
if !P2WMessage::key(&wh_msg_drv_data, ctx.program_id).eq(accs.wh_message.info().key) {
|
||||||
|
|
|
@ -20,17 +20,19 @@
|
||||||
//! 6. (optional) Remove/comment out config structs and aliases from
|
//! 6. (optional) Remove/comment out config structs and aliases from
|
||||||
//! before version Y.
|
//! before version Y.
|
||||||
|
|
||||||
use borsh::{
|
use {
|
||||||
BorshDeserialize,
|
borsh::{
|
||||||
BorshSerialize,
|
BorshDeserialize,
|
||||||
};
|
BorshSerialize,
|
||||||
use solana_program::pubkey::Pubkey;
|
},
|
||||||
use solitaire::{
|
solana_program::pubkey::Pubkey,
|
||||||
processors::seeded::AccountOwner,
|
solitaire::{
|
||||||
AccountState,
|
processors::seeded::AccountOwner,
|
||||||
Data,
|
AccountState,
|
||||||
Derive,
|
Data,
|
||||||
Owned,
|
Derive,
|
||||||
|
Owned,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Aliases for current config schema (to migrate into)
|
/// Aliases for current config schema (to migrate into)
|
||||||
|
@ -59,11 +61,11 @@ impl Owned for OldPyth2WormholeConfig {
|
||||||
#[cfg_attr(feature = "client", derive(Debug))]
|
#[cfg_attr(feature = "client", derive(Debug))]
|
||||||
pub struct Pyth2WormholeConfigV1 {
|
pub struct Pyth2WormholeConfigV1 {
|
||||||
/// Authority owning this contract
|
/// Authority owning this contract
|
||||||
pub owner: Pubkey,
|
pub owner: Pubkey,
|
||||||
/// Wormhole bridge program
|
/// Wormhole bridge program
|
||||||
pub wh_prog: Pubkey,
|
pub wh_prog: Pubkey,
|
||||||
/// Authority owning Pyth price data
|
/// Authority owning Pyth price data
|
||||||
pub pyth_owner: Pubkey,
|
pub pyth_owner: Pubkey,
|
||||||
pub max_batch_size: u16,
|
pub max_batch_size: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +77,11 @@ pub type P2WConfigAccountV1<'b, const IsInitialized: AccountState> =
|
||||||
#[cfg_attr(feature = "client", derive(Debug))]
|
#[cfg_attr(feature = "client", derive(Debug))]
|
||||||
pub struct Pyth2WormholeConfigV2 {
|
pub struct Pyth2WormholeConfigV2 {
|
||||||
/// Authority owning this contract
|
/// Authority owning this contract
|
||||||
pub owner: Pubkey,
|
pub owner: Pubkey,
|
||||||
/// Wormhole bridge program
|
/// Wormhole bridge program
|
||||||
pub wh_prog: Pubkey,
|
pub wh_prog: Pubkey,
|
||||||
/// Authority owning Pyth price data
|
/// Authority owning Pyth price data
|
||||||
pub pyth_owner: Pubkey,
|
pub pyth_owner: Pubkey,
|
||||||
/// How many product/price pairs can be sent and attested at once
|
/// How many product/price pairs can be sent and attested at once
|
||||||
///
|
///
|
||||||
/// Important: Whenever the corresponding logic in attest.rs
|
/// Important: Whenever the corresponding logic in attest.rs
|
||||||
|
@ -123,11 +125,11 @@ impl From<Pyth2WormholeConfigV1> for Pyth2WormholeConfigV2 {
|
||||||
#[cfg_attr(feature = "client", derive(Debug))]
|
#[cfg_attr(feature = "client", derive(Debug))]
|
||||||
pub struct Pyth2WormholeConfigV3 {
|
pub struct Pyth2WormholeConfigV3 {
|
||||||
/// Authority owning this contract
|
/// Authority owning this contract
|
||||||
pub owner: Pubkey,
|
pub owner: Pubkey,
|
||||||
/// Wormhole bridge program
|
/// Wormhole bridge program
|
||||||
pub wh_prog: Pubkey,
|
pub wh_prog: Pubkey,
|
||||||
/// Authority owning Pyth price data
|
/// Authority owning Pyth price data
|
||||||
pub pyth_owner: Pubkey,
|
pub pyth_owner: Pubkey,
|
||||||
/// How many product/price pairs can be sent and attested at once
|
/// How many product/price pairs can be sent and attested at once
|
||||||
///
|
///
|
||||||
/// Important: Whenever the corresponding logic in attest.rs
|
/// Important: Whenever the corresponding logic in attest.rs
|
||||||
|
|
|
@ -1,33 +1,34 @@
|
||||||
use solana_program::{
|
use {
|
||||||
program::invoke,
|
crate::config::{
|
||||||
pubkey::Pubkey,
|
P2WConfigAccount,
|
||||||
rent::Rent,
|
Pyth2WormholeConfig,
|
||||||
system_instruction,
|
},
|
||||||
sysvar::Sysvar,
|
solana_program::{
|
||||||
};
|
program::invoke,
|
||||||
use solitaire::{
|
pubkey::Pubkey,
|
||||||
trace,
|
rent::Rent,
|
||||||
AccountState,
|
system_instruction,
|
||||||
CreationLamports,
|
sysvar::Sysvar,
|
||||||
ExecutionContext,
|
},
|
||||||
FromAccounts,
|
solitaire::{
|
||||||
Info,
|
trace,
|
||||||
Keyed,
|
AccountState,
|
||||||
Mut,
|
CreationLamports,
|
||||||
Peel,
|
ExecutionContext,
|
||||||
Result as SoliResult,
|
FromAccounts,
|
||||||
Signer,
|
Info,
|
||||||
};
|
Keyed,
|
||||||
|
Mut,
|
||||||
use crate::config::{
|
Peel,
|
||||||
P2WConfigAccount,
|
Result as SoliResult,
|
||||||
Pyth2WormholeConfig,
|
Signer,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
pub struct Initialize<'b> {
|
pub struct Initialize<'b> {
|
||||||
pub new_config: Mut<P2WConfigAccount<'b, { AccountState::Uninitialized }>>,
|
pub new_config: Mut<P2WConfigAccount<'b, { AccountState::Uninitialized }>>,
|
||||||
pub payer: Mut<Signer<Info<'b>>>,
|
pub payer: Mut<Signer<Info<'b>>>,
|
||||||
pub system_program: Info<'b>,
|
pub system_program: Info<'b>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,32 +8,31 @@ pub mod set_config;
|
||||||
pub mod set_is_active;
|
pub mod set_is_active;
|
||||||
|
|
||||||
use solitaire::solitaire;
|
use solitaire::solitaire;
|
||||||
|
pub use {
|
||||||
pub use attest::{
|
attest::{
|
||||||
attest,
|
attest,
|
||||||
Attest,
|
Attest,
|
||||||
AttestData,
|
AttestData,
|
||||||
|
},
|
||||||
|
config::Pyth2WormholeConfig,
|
||||||
|
initialize::{
|
||||||
|
initialize,
|
||||||
|
Initialize,
|
||||||
|
},
|
||||||
|
migrate::{
|
||||||
|
migrate,
|
||||||
|
Migrate,
|
||||||
|
},
|
||||||
|
pyth_client,
|
||||||
|
set_config::{
|
||||||
|
set_config,
|
||||||
|
SetConfig,
|
||||||
|
},
|
||||||
|
set_is_active::{
|
||||||
|
set_is_active,
|
||||||
|
SetIsActive,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
pub use config::Pyth2WormholeConfig;
|
|
||||||
pub use initialize::{
|
|
||||||
initialize,
|
|
||||||
Initialize,
|
|
||||||
};
|
|
||||||
pub use migrate::{
|
|
||||||
migrate,
|
|
||||||
Migrate,
|
|
||||||
};
|
|
||||||
pub use set_config::{
|
|
||||||
set_config,
|
|
||||||
SetConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use set_is_active::{
|
|
||||||
set_is_active,
|
|
||||||
SetIsActive,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use pyth_client;
|
|
||||||
|
|
||||||
solitaire! {
|
solitaire! {
|
||||||
Attest => attest,
|
Attest => attest,
|
||||||
|
|
|
@ -7,19 +7,21 @@
|
||||||
//! derived with their address as message_owner in
|
//! derived with their address as message_owner in
|
||||||
//! `P2WMessageDrvData`.
|
//! `P2WMessageDrvData`.
|
||||||
|
|
||||||
use borsh::{
|
use {
|
||||||
BorshDeserialize,
|
borsh::{
|
||||||
BorshSerialize,
|
BorshDeserialize,
|
||||||
};
|
BorshSerialize,
|
||||||
use bridge::PostedMessageUnreliable;
|
},
|
||||||
use solana_program::pubkey::Pubkey;
|
bridge::PostedMessageUnreliable,
|
||||||
use solitaire::{
|
solana_program::pubkey::Pubkey,
|
||||||
processors::seeded::Seeded,
|
solitaire::{
|
||||||
AccountState,
|
processors::seeded::Seeded,
|
||||||
Data,
|
AccountState,
|
||||||
Info,
|
Data,
|
||||||
Mut,
|
Info,
|
||||||
Signer,
|
Mut,
|
||||||
|
Signer,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type P2WMessage<'a> = Mut<PostedMessageUnreliable<'a, { AccountState::MaybeInitialized }>>;
|
pub type P2WMessage<'a> = Mut<PostedMessageUnreliable<'a, { AccountState::MaybeInitialized }>>;
|
||||||
|
@ -34,9 +36,9 @@ pub struct P2WMessageDrvData {
|
||||||
/// Currently wormhole does not resize accounts if they have different
|
/// Currently wormhole does not resize accounts if they have different
|
||||||
/// payload sizes; this (along with versioning the seed literal below) is
|
/// payload sizes; this (along with versioning the seed literal below) is
|
||||||
/// a workaround to have different PDAs for different batch sizes.
|
/// a workaround to have different PDAs for different batch sizes.
|
||||||
pub batch_size: u16,
|
pub batch_size: u16,
|
||||||
/// Index for keeping many accounts per owner
|
/// Index for keeping many accounts per owner
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Seeded<&P2WMessageDrvData> for P2WMessage<'a> {
|
impl<'a> Seeded<&P2WMessageDrvData> for P2WMessage<'a> {
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
//! Instruction used to migrate on-chain configuration from an older format
|
//! Instruction used to migrate on-chain configuration from an older format
|
||||||
|
|
||||||
use solana_program::{
|
use {
|
||||||
program::invoke,
|
crate::config::{
|
||||||
program_error::ProgramError,
|
OldP2WConfigAccount,
|
||||||
pubkey::Pubkey,
|
OldPyth2WormholeConfig,
|
||||||
rent::Rent,
|
P2WConfigAccount,
|
||||||
system_instruction,
|
Pyth2WormholeConfig,
|
||||||
system_program,
|
},
|
||||||
sysvar::Sysvar,
|
solana_program::{
|
||||||
};
|
program::invoke,
|
||||||
|
program_error::ProgramError,
|
||||||
use solitaire::{
|
pubkey::Pubkey,
|
||||||
trace,
|
rent::Rent,
|
||||||
AccountSize,
|
system_instruction,
|
||||||
AccountState,
|
system_program,
|
||||||
CreationLamports,
|
sysvar::Sysvar,
|
||||||
ExecutionContext,
|
},
|
||||||
FromAccounts,
|
solitaire::{
|
||||||
Info,
|
trace,
|
||||||
Keyed,
|
AccountSize,
|
||||||
Mut,
|
AccountState,
|
||||||
Peel,
|
CreationLamports,
|
||||||
Result as SoliResult,
|
ExecutionContext,
|
||||||
Signer,
|
FromAccounts,
|
||||||
SolitaireError,
|
Info,
|
||||||
};
|
Keyed,
|
||||||
|
Mut,
|
||||||
use crate::config::{
|
Peel,
|
||||||
OldP2WConfigAccount,
|
Result as SoliResult,
|
||||||
OldPyth2WormholeConfig,
|
Signer,
|
||||||
P2WConfigAccount,
|
SolitaireError,
|
||||||
Pyth2WormholeConfig,
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Migration accounts meant to evolve with subsequent config accounts
|
/// Migration accounts meant to evolve with subsequent config accounts
|
||||||
|
@ -40,13 +40,13 @@ use crate::config::{
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
pub struct Migrate<'b> {
|
pub struct Migrate<'b> {
|
||||||
/// New config account to be populated. Must be unused.
|
/// New config account to be populated. Must be unused.
|
||||||
pub new_config: Mut<P2WConfigAccount<'b, { AccountState::Uninitialized }>>,
|
pub new_config: Mut<P2WConfigAccount<'b, { AccountState::Uninitialized }>>,
|
||||||
/// Old config using the previous format.
|
/// Old config using the previous format.
|
||||||
pub old_config: Mut<OldP2WConfigAccount<'b>>,
|
pub old_config: Mut<OldP2WConfigAccount<'b>>,
|
||||||
/// Current owner authority of the program
|
/// Current owner authority of the program
|
||||||
pub current_owner: Mut<Signer<Info<'b>>>,
|
pub current_owner: Mut<Signer<Info<'b>>>,
|
||||||
/// Payer account for updating the account data
|
/// Payer account for updating the account data
|
||||||
pub payer: Mut<Signer<Info<'b>>>,
|
pub payer: Mut<Signer<Info<'b>>>,
|
||||||
/// For creating the new config account
|
/// For creating the new config account
|
||||||
pub system_program: Info<'b>,
|
pub system_program: Info<'b>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,41 @@
|
||||||
use borsh::BorshSerialize;
|
use {
|
||||||
|
crate::config::{
|
||||||
use solana_program::{
|
P2WConfigAccount,
|
||||||
program::invoke,
|
Pyth2WormholeConfig,
|
||||||
program_error::ProgramError,
|
},
|
||||||
pubkey::Pubkey,
|
borsh::BorshSerialize,
|
||||||
rent::Rent,
|
solana_program::{
|
||||||
system_instruction,
|
program::invoke,
|
||||||
sysvar::Sysvar,
|
program_error::ProgramError,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
rent::Rent,
|
||||||
|
system_instruction,
|
||||||
|
sysvar::Sysvar,
|
||||||
|
},
|
||||||
|
solitaire::{
|
||||||
|
trace,
|
||||||
|
AccountState,
|
||||||
|
ExecutionContext,
|
||||||
|
FromAccounts,
|
||||||
|
Info,
|
||||||
|
Keyed,
|
||||||
|
Mut,
|
||||||
|
Peel,
|
||||||
|
Result as SoliResult,
|
||||||
|
Signer,
|
||||||
|
SolitaireError,
|
||||||
|
},
|
||||||
|
std::cmp::Ordering,
|
||||||
};
|
};
|
||||||
use solitaire::{
|
|
||||||
trace,
|
|
||||||
AccountState,
|
|
||||||
ExecutionContext,
|
|
||||||
FromAccounts,
|
|
||||||
Info,
|
|
||||||
Keyed,
|
|
||||||
Mut,
|
|
||||||
Peel,
|
|
||||||
Result as SoliResult,
|
|
||||||
Signer,
|
|
||||||
SolitaireError,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::config::{
|
|
||||||
P2WConfigAccount,
|
|
||||||
Pyth2WormholeConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
pub struct SetConfig<'b> {
|
pub struct SetConfig<'b> {
|
||||||
/// Current config used by the program
|
/// Current config used by the program
|
||||||
pub config: Mut<P2WConfigAccount<'b, { AccountState::Initialized }>>,
|
pub config: Mut<P2WConfigAccount<'b, { AccountState::Initialized }>>,
|
||||||
/// Current owner authority of the program
|
/// Current owner authority of the program
|
||||||
pub current_owner: Mut<Signer<Info<'b>>>,
|
pub current_owner: Mut<Signer<Info<'b>>>,
|
||||||
/// Payer account for updating the account data
|
/// Payer account for updating the account data
|
||||||
pub payer: Mut<Signer<Info<'b>>>,
|
pub payer: Mut<Signer<Info<'b>>>,
|
||||||
/// Used for rent adjustment transfer
|
/// Used for rent adjustment transfer
|
||||||
pub system_program: Info<'b>,
|
pub system_program: Info<'b>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,31 @@
|
||||||
use solitaire::{
|
use {
|
||||||
trace,
|
crate::config::{
|
||||||
AccountState,
|
P2WConfigAccount,
|
||||||
ExecutionContext,
|
Pyth2WormholeConfig,
|
||||||
FromAccounts,
|
},
|
||||||
Info,
|
solitaire::{
|
||||||
Keyed,
|
trace,
|
||||||
Mut,
|
AccountState,
|
||||||
Peel,
|
ExecutionContext,
|
||||||
Result as SoliResult,
|
FromAccounts,
|
||||||
Signer,
|
Info,
|
||||||
SolitaireError,
|
Keyed,
|
||||||
};
|
Mut,
|
||||||
|
Peel,
|
||||||
use crate::config::{
|
Result as SoliResult,
|
||||||
P2WConfigAccount,
|
Signer,
|
||||||
Pyth2WormholeConfig,
|
SolitaireError,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
pub struct SetIsActive<'b> {
|
pub struct SetIsActive<'b> {
|
||||||
/// Current config used by the program
|
/// Current config used by the program
|
||||||
pub config: Mut<P2WConfigAccount<'b, { AccountState::Initialized }>>,
|
pub config: Mut<P2WConfigAccount<'b, { AccountState::Initialized }>>,
|
||||||
/// Current owner authority of the program
|
/// Current owner authority of the program
|
||||||
pub ops_owner: Mut<Signer<Info<'b>>>,
|
pub ops_owner: Mut<Signer<Info<'b>>>,
|
||||||
/// Payer account for updating the account data
|
/// Payer account for updating the account data
|
||||||
pub payer: Mut<Signer<Info<'b>>>,
|
pub payer: Mut<Signer<Info<'b>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alters the current settings of pyth2wormhole
|
/// Alters the current settings of pyth2wormhole
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Merge similar crates together to avoid multiple use statements.
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
|
|
||||||
# Consistency in formatting makes tool based searching/editing better.
|
|
||||||
empty_item_single_line = false
|
|
||||||
|
|
||||||
# Easier editing when arbitrary mixed use statements do not collapse.
|
|
||||||
imports_layout = "Vertical"
|
|
||||||
|
|
||||||
# Default rustfmt formatting of match arms with branches is awful.
|
|
||||||
match_arm_leading_pipes = "Preserve"
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Merge similar crates together to avoid multiple use statements.
|
|
||||||
imports_granularity = "Module"
|
|
||||||
|
|
||||||
# Consistency in formatting makes tool based searching/editing better.
|
|
||||||
empty_item_single_line = false
|
|
||||||
|
|
||||||
# Easier editing when arbitrary mixed use statements do not collapse.
|
|
||||||
imports_layout = "Vertical"
|
|
||||||
|
|
||||||
# Default rustfmt formatting of match arms with branches is awful.
|
|
||||||
match_arm_leading_pipes = "Preserve"
|
|
||||||
|
|
||||||
# Align Fields
|
|
||||||
enum_discrim_align_threshold = 80
|
|
||||||
struct_field_align_threshold = 80
|
|
||||||
|
|
||||||
# Allow up to two blank lines for grouping.
|
|
||||||
blank_lines_upper_bound = 2
|
|
|
@ -6,29 +6,30 @@
|
||||||
//! similar human-readable names and provide a failsafe for some of
|
//! similar human-readable names and provide a failsafe for some of
|
||||||
//! the probable adversarial scenarios.
|
//! the probable adversarial scenarios.
|
||||||
|
|
||||||
use serde::{
|
|
||||||
Deserialize,
|
|
||||||
Serialize,
|
|
||||||
Serializer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::iter::Iterator;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
pub use pyth_sdk::{
|
pub use pyth_sdk::{
|
||||||
Identifier,
|
Identifier,
|
||||||
PriceStatus,
|
PriceStatus,
|
||||||
UnixTimestamp,
|
UnixTimestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "solana")]
|
#[cfg(feature = "solana")]
|
||||||
use solitaire::{
|
use solitaire::{
|
||||||
Derive,
|
Derive,
|
||||||
Info,
|
Info,
|
||||||
};
|
};
|
||||||
|
use {
|
||||||
|
serde::{
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
Serializer,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
convert::TryInto,
|
||||||
|
io::Read,
|
||||||
|
iter::Iterator,
|
||||||
|
mem,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||||
|
@ -472,8 +473,10 @@ impl PriceAttestation {
|
||||||
/// using `cargo test -- --nocapture`.
|
/// using `cargo test -- --nocapture`.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use {
|
||||||
use pyth_sdk_solana::state::PriceStatus;
|
super::*,
|
||||||
|
pyth_sdk_solana::state::PriceStatus,
|
||||||
|
};
|
||||||
|
|
||||||
fn mock_attestation(prod: Option<[u8; 32]>, price: Option<[u8; 32]>) -> PriceAttestation {
|
fn mock_attestation(prod: Option<[u8; 32]>, price: Option<[u8; 32]>) -> PriceAttestation {
|
||||||
let product_id_bytes = prod.unwrap_or([21u8; 32]);
|
let product_id_bytes = prod.unwrap_or([21u8; 32]);
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use solana_program::pubkey::Pubkey;
|
use {
|
||||||
use solitaire::Seeded;
|
crate::{
|
||||||
use std::str::FromStr;
|
BatchPriceAttestation,
|
||||||
use wasm_bindgen::prelude::*;
|
P2WEmitter,
|
||||||
|
PriceAttestation,
|
||||||
|
},
|
||||||
use crate::{
|
solana_program::pubkey::Pubkey,
|
||||||
BatchPriceAttestation,
|
solitaire::Seeded,
|
||||||
P2WEmitter,
|
std::str::FromStr,
|
||||||
PriceAttestation,
|
wasm_bindgen::prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
|
Loading…
Reference in New Issue