[WIP] p2w-client: Implement a migrate command and instruction generator (#232)
* p2w-client: Implement a migrate command and instruction generator * Fix minor bug in gen_migrate_tx * Fix migration test and add a test for success Co-authored-by: Ali Behjati <bahjatia@gmail.com>
This commit is contained in:
parent
3d62f2b4f5
commit
9dbfbd4174
|
@ -106,4 +106,14 @@ pub enum Action {
|
|||
#[clap(long = "is-active")]
|
||||
is_active: Option<bool>,
|
||||
},
|
||||
#[clap(about = "Migrate existing pyth2wormhole program settings to a newer format version. Client version must match the deployed contract.")]
|
||||
Migrate {
|
||||
/// owner keypair path
|
||||
#[clap(
|
||||
long,
|
||||
default_value = "~/.config/solana/id.json",
|
||||
help = "Keypair file for the current config owner"
|
||||
)]
|
||||
owner: String,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ use pyth2wormhole::{
|
|||
attest::P2W_MAX_BATCH_SIZE,
|
||||
config::P2WConfigAccount,
|
||||
initialize::InitializeAccounts,
|
||||
migrate::MigrateAccounts,
|
||||
set_config::SetConfigAccounts,
|
||||
AttestData,
|
||||
Pyth2WormholeConfig,
|
||||
|
@ -124,6 +125,39 @@ pub fn gen_set_config_tx(
|
|||
Ok(tx_signed)
|
||||
}
|
||||
|
||||
pub fn gen_migrate_tx(
|
||||
payer: Keypair,
|
||||
p2w_addr: Pubkey,
|
||||
owner: Keypair,
|
||||
latest_blockhash: Hash,
|
||||
) -> Result<Transaction, ErrBox> {
|
||||
use AccEntry::*;
|
||||
|
||||
let payer_pubkey = payer.pubkey();
|
||||
|
||||
let accs = MigrateAccounts {
|
||||
new_config: Derived(p2w_addr),
|
||||
old_config: DerivedRO(p2w_addr),
|
||||
current_owner: Signer(owner),
|
||||
payer: Signer(payer),
|
||||
};
|
||||
|
||||
let ix_data = (
|
||||
pyth2wormhole::instruction::Instruction::Migrate,
|
||||
(),
|
||||
);
|
||||
|
||||
let (ix, signers) = accs.to_ix(p2w_addr, ix_data.try_to_vec()?.as_slice())?;
|
||||
|
||||
let tx_signed = Transaction::new_signed_with_payer::<Vec<&Keypair>>(
|
||||
&[ix],
|
||||
Some(&payer_pubkey),
|
||||
signers.iter().collect::<Vec<_>>().as_ref(),
|
||||
latest_blockhash,
|
||||
);
|
||||
Ok(tx_signed)
|
||||
}
|
||||
|
||||
/// Get the current config account data for given p2w program address
|
||||
pub fn get_config_account(
|
||||
rpc_client: &RpcClient,
|
||||
|
|
|
@ -111,6 +111,21 @@ fn main() -> Result<(), ErrBox> {
|
|||
get_config_account(&rpc_client, &p2w_addr)?
|
||||
);
|
||||
}
|
||||
Action::Migrate {
|
||||
ref owner,
|
||||
} => {
|
||||
let tx = gen_migrate_tx(
|
||||
payer,
|
||||
p2w_addr,
|
||||
read_keypair_file(&*shellexpand::tilde(&owner))?,
|
||||
latest_blockhash,
|
||||
)?;
|
||||
rpc_client.send_and_confirm_transaction_with_spinner(&tx)?;
|
||||
println!(
|
||||
"Applied conifg:\n{:?}",
|
||||
get_config_account(&rpc_client, &p2w_addr)?
|
||||
);
|
||||
}
|
||||
Action::Attest {
|
||||
ref attestation_cfg,
|
||||
n_retries,
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
//! Checks for migrating the previous config schema into the current one
|
||||
|
||||
pub mod fixtures;
|
||||
|
||||
use solana_program::system_program;
|
||||
use solana_program_test::*;
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
instruction::{
|
||||
AccountMeta,
|
||||
Instruction,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
signature::Signer,
|
||||
signer::keypair::Keypair,
|
||||
transaction::Transaction,
|
||||
};
|
||||
|
||||
use bridge::accounts::{
|
||||
Bridge,
|
||||
BridgeConfig,
|
||||
BridgeData,
|
||||
};
|
||||
|
||||
use log::info;
|
||||
|
||||
use pyth2wormhole::config::{
|
||||
OldP2WConfigAccount,
|
||||
P2WConfigAccount,
|
||||
OldPyth2WormholeConfig,
|
||||
Pyth2WormholeConfig,
|
||||
};
|
||||
use pyth2wormhole_client as p2wc;
|
||||
use solitaire::{
|
||||
processors::seeded::Seeded,
|
||||
AccountState,
|
||||
BorshSerialize,
|
||||
};
|
||||
|
||||
use fixtures::{
|
||||
passthrough,
|
||||
pyth,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_migrate_works() -> Result<(), solitaire::ErrBox> {
|
||||
info!("Starting");
|
||||
// Programs
|
||||
let p2w_program_id = Pubkey::new_unique();
|
||||
let wh_fixture_program_id = Pubkey::new_unique();
|
||||
|
||||
// Authorities
|
||||
let p2w_owner = Keypair::new();
|
||||
let pyth_owner = Pubkey::new_unique();
|
||||
|
||||
// On-chain state
|
||||
let old_p2w_config = OldPyth2WormholeConfig {owner: p2w_owner.pubkey(),
|
||||
wh_prog: wh_fixture_program_id,
|
||||
max_batch_size: pyth2wormhole::attest::P2W_MAX_BATCH_SIZE,
|
||||
pyth_owner,
|
||||
};
|
||||
|
||||
info!("Before ProgramTest::new()");
|
||||
|
||||
// Populate test environment
|
||||
let mut p2w_test = ProgramTest::new(
|
||||
"pyth2wormhole",
|
||||
p2w_program_id,
|
||||
processor!(pyth2wormhole::instruction::solitaire),
|
||||
);
|
||||
|
||||
// Plant filled config accounts
|
||||
let old_p2w_config_bytes = old_p2w_config.try_to_vec()?;
|
||||
let old_p2w_config_account = Account {
|
||||
lamports: Rent::default().minimum_balance(old_p2w_config_bytes.len()),
|
||||
data: old_p2w_config_bytes,
|
||||
owner: p2w_program_id,
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let old_p2w_config_addr =
|
||||
OldP2WConfigAccount::key(None, &p2w_program_id);
|
||||
|
||||
info!("Before add_account() calls");
|
||||
|
||||
p2w_test.add_account(old_p2w_config_addr, old_p2w_config_account);
|
||||
|
||||
// Add system program because the contract creates an account for new configuration account
|
||||
passthrough::add_passthrough(&mut p2w_test, "system", system_program::id());
|
||||
|
||||
info!("Before start_with_context");
|
||||
let mut ctx = p2w_test.start_with_context().await;
|
||||
|
||||
let migrate_tx = p2wc::gen_migrate_tx(
|
||||
ctx.payer,
|
||||
p2w_program_id,
|
||||
p2w_owner,
|
||||
ctx.last_blockhash,
|
||||
)?;
|
||||
info!("Before process_transaction");
|
||||
|
||||
// Migration should fail because the new config account is already initialized
|
||||
ctx.banks_client.process_transaction(migrate_tx).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_migrate_already_migrated() -> Result<(), solitaire::ErrBox> {
|
||||
info!("Starting");
|
||||
// Programs
|
||||
let p2w_program_id = Pubkey::new_unique();
|
||||
let wh_fixture_program_id = Pubkey::new_unique();
|
||||
|
||||
// Authorities
|
||||
let p2w_owner = Keypair::new();
|
||||
let pyth_owner = Pubkey::new_unique();
|
||||
|
||||
// On-chain state
|
||||
let old_p2w_config = OldPyth2WormholeConfig {owner: p2w_owner.pubkey(),
|
||||
wh_prog: wh_fixture_program_id,
|
||||
max_batch_size: pyth2wormhole::attest::P2W_MAX_BATCH_SIZE,
|
||||
pyth_owner,
|
||||
};
|
||||
|
||||
let new_p2w_config = Pyth2WormholeConfig {owner: p2w_owner.pubkey(),
|
||||
wh_prog: wh_fixture_program_id,
|
||||
max_batch_size: pyth2wormhole::attest::P2W_MAX_BATCH_SIZE,
|
||||
pyth_owner,
|
||||
is_active: true,
|
||||
};
|
||||
|
||||
info!("Before ProgramTest::new()");
|
||||
|
||||
// Populate test environment
|
||||
let mut p2w_test = ProgramTest::new(
|
||||
"pyth2wormhole",
|
||||
p2w_program_id,
|
||||
processor!(pyth2wormhole::instruction::solitaire),
|
||||
);
|
||||
|
||||
// Plant filled config accounts
|
||||
let old_p2w_config_bytes = old_p2w_config.try_to_vec()?;
|
||||
let old_p2w_config_account = Account {
|
||||
lamports: Rent::default().minimum_balance(old_p2w_config_bytes.len()),
|
||||
data: old_p2w_config_bytes,
|
||||
owner: p2w_program_id,
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let old_p2w_config_addr =
|
||||
OldP2WConfigAccount::key(None, &p2w_program_id);
|
||||
|
||||
let new_p2w_config_bytes = new_p2w_config.try_to_vec()?;
|
||||
let new_p2w_config_account = Account {
|
||||
lamports: Rent::default().minimum_balance(new_p2w_config_bytes.len()),
|
||||
data: new_p2w_config_bytes,
|
||||
owner: p2w_program_id,
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let new_p2w_config_addr =
|
||||
P2WConfigAccount::<{AccountState::Initialized}>::key(None, &p2w_program_id);
|
||||
|
||||
info!("Before add_account() calls");
|
||||
|
||||
p2w_test.add_account(old_p2w_config_addr, old_p2w_config_account);
|
||||
p2w_test.add_account(new_p2w_config_addr, new_p2w_config_account);
|
||||
|
||||
info!("Before start_with_context");
|
||||
let mut ctx = p2w_test.start_with_context().await;
|
||||
|
||||
let migrate_tx = p2wc::gen_migrate_tx(
|
||||
ctx.payer,
|
||||
p2w_program_id,
|
||||
p2w_owner,
|
||||
ctx.last_blockhash,
|
||||
)?;
|
||||
info!("Before process_transaction");
|
||||
|
||||
// Migration should fail because the new config account is already initialized
|
||||
assert!(ctx.banks_client.process_transaction(migrate_tx).await.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue