1305 lines
42 KiB
Rust
1305 lines
42 KiB
Rust
#![allow(warnings)]
|
|
|
|
use borsh::BorshSerialize;
|
|
use byteorder::{
|
|
BigEndian,
|
|
WriteBytesExt,
|
|
};
|
|
use hex_literal::hex;
|
|
use rand::Rng;
|
|
use secp256k1::{
|
|
Message as Secp256k1Message,
|
|
PublicKey,
|
|
SecretKey,
|
|
};
|
|
use sha3::Digest;
|
|
use solana_client::rpc_client::RpcClient;
|
|
use solana_program::{
|
|
borsh::try_from_slice_unchecked,
|
|
hash,
|
|
instruction::{
|
|
AccountMeta,
|
|
Instruction,
|
|
},
|
|
program_pack::Pack,
|
|
pubkey::Pubkey,
|
|
system_instruction::{
|
|
self,
|
|
create_account,
|
|
},
|
|
system_program,
|
|
sysvar,
|
|
};
|
|
use solana_sdk::{
|
|
signature::{
|
|
read_keypair_file,
|
|
Keypair,
|
|
Signer,
|
|
},
|
|
transaction::Transaction,
|
|
};
|
|
use solitaire::{
|
|
processors::seeded::Seeded,
|
|
AccountState,
|
|
};
|
|
use std::{
|
|
convert::TryInto,
|
|
io::{
|
|
Cursor,
|
|
Write,
|
|
},
|
|
time::{
|
|
Duration,
|
|
SystemTime,
|
|
},
|
|
};
|
|
|
|
use bridge::{
|
|
accounts::{
|
|
Bridge,
|
|
FeeCollector,
|
|
GuardianSet,
|
|
GuardianSetDerivationData,
|
|
Message,
|
|
MessageDerivationData,
|
|
SignatureSet,
|
|
SignatureSetDerivationData,
|
|
},
|
|
instruction,
|
|
types::{
|
|
BridgeConfig,
|
|
BridgeData,
|
|
ConsistencyLevel,
|
|
GovernancePayloadGuardianSetChange,
|
|
GovernancePayloadSetMessageFee,
|
|
GovernancePayloadTransferFees,
|
|
GovernancePayloadUpgrade,
|
|
GuardianSetData,
|
|
PostedMessage,
|
|
PostedMessageData,
|
|
SequenceTracker,
|
|
SignatureSet as SignatureSetData,
|
|
},
|
|
Initialize,
|
|
PostVAA,
|
|
PostVAAData,
|
|
SerializePayload,
|
|
Signature,
|
|
};
|
|
use primitive_types::U256;
|
|
|
|
mod common;
|
|
|
|
const GOVERNANCE_KEY: [u8; 64] = [
|
|
240, 133, 120, 113, 30, 67, 38, 184, 197, 72, 234, 99, 241, 21, 58, 225, 41, 157, 171, 44, 196,
|
|
163, 134, 236, 92, 148, 110, 68, 127, 114, 177, 0, 173, 253, 199, 9, 242, 142, 201, 174, 108,
|
|
197, 18, 102, 115, 0, 31, 205, 127, 188, 191, 56, 171, 228, 20, 247, 149, 170, 141, 231, 147,
|
|
88, 97, 199,
|
|
];
|
|
|
|
struct Context {
|
|
public: Vec<[u8; 20]>,
|
|
secret: Vec<SecretKey>,
|
|
seq: Sequencer,
|
|
}
|
|
|
|
/// Small helper to track and provide sequences during tests. This is in particular needed for
|
|
/// guardian operations that require them for derivations.
|
|
struct Sequencer {
|
|
sequences: std::collections::HashMap<[u8; 32], u64>,
|
|
}
|
|
|
|
impl Sequencer {
|
|
fn next(&mut self, emitter: [u8; 32]) -> u64 {
|
|
let entry = self.sequences.entry(emitter).or_insert(0);
|
|
*entry += 1;
|
|
*entry - 1
|
|
}
|
|
|
|
fn peek(&mut self, emitter: [u8; 32]) -> u64 {
|
|
*self.sequences.entry(emitter).or_insert(0)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn run_integration_tests() {
|
|
let (public_keys, secret_keys) = common::generate_keys(6);
|
|
let mut context = Context {
|
|
public: public_keys,
|
|
secret: secret_keys,
|
|
seq: Sequencer {
|
|
sequences: std::collections::HashMap::new(),
|
|
},
|
|
};
|
|
|
|
// Initialize the bridge and verify the bridges state.
|
|
test_initialize(&mut context);
|
|
|
|
// Tests are currently unhygienic as It's difficult to wrap `solana-test-validator` within the
|
|
// integration tests so for now we work around it by simply chain-calling our tests.
|
|
test_bridge_messages(&mut context);
|
|
test_foreign_bridge_messages(&mut context);
|
|
test_persistent_bridge_messages(&mut context);
|
|
test_invalid_emitter(&mut context);
|
|
test_duplicate_messages_fail(&mut context);
|
|
test_upgrade_contract(&mut context);
|
|
test_guardian_set_change(&mut context);
|
|
test_guardian_set_change_fails(&mut context);
|
|
test_set_fees(&mut context);
|
|
test_set_fees_fails(&mut context);
|
|
test_free_fees(&mut context);
|
|
test_transfer_fees(&mut context);
|
|
test_transfer_fees_fails(&mut context);
|
|
test_transfer_too_much(&mut context);
|
|
test_transfer_total_fails(&mut context);
|
|
}
|
|
|
|
fn test_initialize(context: &mut Context) {
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Use a timestamp from a few seconds earlier for testing to simulate thread::sleep();
|
|
let now = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_secs()
|
|
- 10;
|
|
|
|
common::initialize(client, program, payer, &*context.public.clone(), 500, 5000);
|
|
common::sync(client, payer);
|
|
|
|
// Verify the initial bridge state is as expected.
|
|
let bridge_key = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program);
|
|
let guardian_set_key = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&GuardianSetDerivationData { index: 0 },
|
|
&program,
|
|
);
|
|
|
|
// Fetch account states.
|
|
let bridge: BridgeData = common::get_account_data(client, &bridge_key);
|
|
let guardian_set: GuardianSetData = common::get_account_data(client, &guardian_set_key);
|
|
|
|
// Bridge Config should be as expected.
|
|
assert_eq!(bridge.guardian_set_index, 0);
|
|
assert_eq!(bridge.config.guardian_set_expiration_time, 2_000_000_000);
|
|
assert_eq!(bridge.config.fee, 500);
|
|
assert_eq!(bridge.config.fee_persistent, 5000);
|
|
|
|
// Guardian set account must also be as expected.
|
|
assert_eq!(guardian_set.index, 0);
|
|
assert_eq!(guardian_set.keys, context.public);
|
|
assert!(guardian_set.creation_time as u64 > now);
|
|
}
|
|
|
|
fn test_bridge_messages(context: &mut Context) {
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Data/Nonce used for emitting a message we want to prove exists. Run this twice to make sure
|
|
// that duplicate data does not clash.
|
|
let message = [0u8; 32].to_vec();
|
|
let emitter = Keypair::new();
|
|
|
|
for _ in 0..2 {
|
|
let nonce = rand::thread_rng().gen();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Post the message, publishing the data for guardian consumption.
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 0).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Derive where we expect created accounts to be.
|
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&SignatureSetDerivationData { hash: body},
|
|
&program,
|
|
);
|
|
|
|
// Fetch chain accounts to verify state.
|
|
let posted_message: PostedMessage = common::get_account_data(client, &message_key);
|
|
let signatures: SignatureSetData = common::get_account_data(client, &signature_set);
|
|
|
|
// Verify on chain Message
|
|
assert_eq!(posted_message.0.vaa_version, 0);
|
|
assert_eq!(posted_message.0.persist, false);
|
|
assert_eq!(posted_message.0.vaa_signature_account, signature_set);
|
|
assert_eq!(posted_message.0.nonce, nonce);
|
|
assert_eq!(posted_message.0.sequence, sequence);
|
|
assert_eq!(posted_message.0.emitter_chain, 1);
|
|
assert_eq!(posted_message.0.payload, message);
|
|
assert_eq!(
|
|
posted_message.0.emitter_address,
|
|
emitter.pubkey().to_bytes()
|
|
);
|
|
|
|
// Verify on chain Signatures
|
|
assert_eq!(signatures.hash, body);
|
|
assert_eq!(signatures.guardian_set_index, 0);
|
|
|
|
for (signature, secret_key) in signatures.signatures.iter().zip(context.secret.iter()) {
|
|
// Sign message locally.
|
|
let (local_sig, recover_id) =
|
|
secp256k1::sign(&Secp256k1Message::parse(&body_hash), &secret_key);
|
|
|
|
// Combine recoverify with signature to match 65 byte layout.
|
|
let mut signature_bytes = [0u8; 65];
|
|
signature_bytes[64] = recover_id.serialize();
|
|
(&mut signature_bytes[0..64]).copy_from_slice(&local_sig.serialize());
|
|
|
|
// Signature stored should on chain be as expected.
|
|
assert_eq!(*signature, signature_bytes);
|
|
}
|
|
}
|
|
|
|
// Prepare another message with no data in its message to confirm it succeeds.
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = b"".to_vec();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Post the message, publishing the data for guardian consumption.
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 0).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Derive where we expect created accounts to be.
|
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&SignatureSetDerivationData { hash: body },
|
|
&program,
|
|
);
|
|
|
|
// Fetch chain accounts to verify state.
|
|
let posted_message: PostedMessage = common::get_account_data(client, &message_key);
|
|
let signatures: SignatureSetData = common::get_account_data(client, &signature_set);
|
|
|
|
// Verify on chain Message
|
|
assert_eq!(posted_message.0.vaa_version, 0);
|
|
assert_eq!(posted_message.0.persist, false);
|
|
assert_eq!(posted_message.0.vaa_signature_account, signature_set);
|
|
assert_eq!(posted_message.0.nonce, nonce);
|
|
assert_eq!(posted_message.0.sequence, sequence);
|
|
assert_eq!(posted_message.0.emitter_chain, 1);
|
|
assert_eq!(posted_message.0.payload, message);
|
|
assert_eq!(
|
|
posted_message.0.emitter_address,
|
|
emitter.pubkey().to_bytes()
|
|
);
|
|
|
|
// Verify on chain Signatures
|
|
assert_eq!(signatures.hash, body);
|
|
assert_eq!(signatures.guardian_set_index, 0);
|
|
|
|
for (signature, secret_key) in signatures.signatures.iter().zip(context.secret.iter()) {
|
|
// Sign message locally.
|
|
let (local_sig, recover_id) =
|
|
secp256k1::sign(&Secp256k1Message::parse(&body_hash), &secret_key);
|
|
|
|
// Combine recoverify with signature to match 65 byte layout.
|
|
let mut signature_bytes = [0u8; 65];
|
|
signature_bytes[64] = recover_id.serialize();
|
|
(&mut signature_bytes[0..64]).copy_from_slice(&local_sig.serialize());
|
|
|
|
// Signature stored should on chain be as expected.
|
|
assert_eq!(*signature, signature_bytes);
|
|
}
|
|
}
|
|
|
|
fn test_persistent_bridge_messages(context: &mut Context) {
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Generate a message we want to persist.
|
|
let message = [0u8; 32].to_vec();
|
|
let emitter = Keypair::new();
|
|
let nonce = rand::thread_rng().gen();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Confirm that it fails if we try and save a message while paying below the expected fee.
|
|
assert!(common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
4999,
|
|
true,
|
|
)
|
|
.is_err());
|
|
|
|
// Check current balance to verify the right fee is going to be taken.
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
let account_balance = client.get_account(&fee_collector).unwrap().lamports;
|
|
|
|
// Generate a message we want to persist.
|
|
let message = [0u8; 32].to_vec();
|
|
let nonce = rand::thread_rng().gen();
|
|
|
|
// This time pay enough to confirm the message succeeds.
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
5000,
|
|
true,
|
|
)
|
|
.unwrap();
|
|
|
|
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 0).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Derive where we expect created accounts to be.
|
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&SignatureSetDerivationData { hash: body },
|
|
&program,
|
|
);
|
|
|
|
// Fetch chain accounts to verify state.
|
|
let posted_message: PostedMessage = common::get_account_data(client, &message_key);
|
|
let signatures: SignatureSetData = common::get_account_data(client, &signature_set);
|
|
|
|
// Verify on chain Message
|
|
assert_eq!(posted_message.0.vaa_version, 0);
|
|
assert_eq!(posted_message.0.persist, true);
|
|
assert_eq!(posted_message.0.vaa_signature_account, signature_set);
|
|
assert_eq!(posted_message.0.nonce, nonce);
|
|
assert_eq!(posted_message.0.sequence, sequence);
|
|
assert_eq!(posted_message.0.emitter_chain, 1);
|
|
assert_eq!(posted_message.0.payload, message);
|
|
assert_eq!(
|
|
posted_message.0.emitter_address,
|
|
emitter.pubkey().to_bytes()
|
|
);
|
|
|
|
// Verify on chain Signatures
|
|
assert_eq!(signatures.hash, body);
|
|
assert_eq!(signatures.guardian_set_index, 0);
|
|
|
|
for (signature, secret_key) in signatures.signatures.iter().zip(context.secret.iter()) {
|
|
// Sign message locally.
|
|
let (local_sig, recover_id) =
|
|
secp256k1::sign(&Secp256k1Message::parse(&body_hash), &secret_key);
|
|
|
|
// Combine recoverify with signature to match 65 byte layout.
|
|
let mut signature_bytes = [0u8; 65];
|
|
signature_bytes[64] = recover_id.serialize();
|
|
(&mut signature_bytes[0..64]).copy_from_slice(&local_sig.serialize());
|
|
|
|
// Signature stored should on chain be as expected.
|
|
assert_eq!(*signature, signature_bytes);
|
|
}
|
|
|
|
// Verify fee account gained a persistent fee.
|
|
assert_eq!(
|
|
client.get_account(&fee_collector).unwrap().lamports,
|
|
account_balance + 5000,
|
|
);
|
|
}
|
|
|
|
fn test_invalid_emitter(context: &mut Context) {
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Generate a message we want to persist.
|
|
let message = [0u8; 32].to_vec();
|
|
let emitter = Keypair::new();
|
|
let nonce = rand::thread_rng().gen();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
|
|
// Manually send a message that isn't signed by the emitter, which should be rejected to
|
|
// prevent fraudulant transactions sent on behalf of an emitter.
|
|
let (message_key, mut instruction) = bridge::instructions::post_message(
|
|
*program,
|
|
payer.pubkey(),
|
|
emitter.pubkey(),
|
|
nonce,
|
|
message,
|
|
false,
|
|
ConsistencyLevel::Confirmed,
|
|
)
|
|
.unwrap();
|
|
|
|
// Modify account list to not require the emitter signs.
|
|
instruction.accounts[2].is_signer = false;
|
|
|
|
// Executing this should fail.
|
|
assert!(common::execute(
|
|
client,
|
|
payer,
|
|
&[payer],
|
|
&[
|
|
system_instruction::transfer(&payer.pubkey(), &fee_collector, 10_000),
|
|
instruction,
|
|
],
|
|
solana_sdk::commitment_config::CommitmentConfig::processed(),
|
|
)
|
|
.is_err());
|
|
}
|
|
|
|
fn test_duplicate_messages_fail(context: &mut Context) {
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// We'll use the following nonce/message/emitter/sequence twice.
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = [0u8; 32].to_vec();
|
|
let emitter = Keypair::new();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Post the message, publishing the data for guardian consumption.
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
// Second should fail due to duplicate derivations.
|
|
assert!(common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.is_err());
|
|
}
|
|
|
|
fn test_guardian_set_change(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Use a timestamp from a few seconds earlier for testing to simulate thread::sleep();
|
|
let now = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_secs()
|
|
- 10;
|
|
|
|
// Upgrade the guardian set with a new set of guardians.
|
|
let (new_public_keys, new_secret_keys) = common::generate_keys(1);
|
|
|
|
let nonce = rand::thread_rng().gen();
|
|
let emitter = Keypair::from_bytes(&GOVERNANCE_KEY).unwrap();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
let message = GovernancePayloadGuardianSetChange {
|
|
new_guardian_set_index: 1,
|
|
new_guardian_set: new_public_keys.clone(),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 0).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::upgrade_guardian_set(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
0,
|
|
1,
|
|
sequence,
|
|
)
|
|
.unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Derive keys for accounts we want to check.
|
|
let bridge_key = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program);
|
|
let guardian_set_key = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&GuardianSetDerivationData { index: 1 },
|
|
&program,
|
|
);
|
|
|
|
// Fetch account states.
|
|
let bridge: BridgeData = common::get_account_data(client, &bridge_key);
|
|
let guardian_set: GuardianSetData = common::get_account_data(client, &guardian_set_key);
|
|
|
|
// Confirm the bridge now has a new guardian set, and no other fields have shifted.
|
|
assert_eq!(bridge.guardian_set_index, 1);
|
|
assert_eq!(bridge.config.guardian_set_expiration_time, 2_000_000_000);
|
|
assert_eq!(bridge.config.fee, 500);
|
|
assert_eq!(bridge.config.fee_persistent, 5000);
|
|
|
|
// Verify Created Guardian Set
|
|
assert_eq!(guardian_set.index, 1);
|
|
assert_eq!(guardian_set.keys, new_public_keys);
|
|
assert!(guardian_set.creation_time as u64 > now);
|
|
|
|
// Submit the message a second time with a new nonce.
|
|
let nonce = rand::thread_rng().gen();
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
context.public = new_public_keys;
|
|
context.secret = new_secret_keys;
|
|
|
|
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Derive where we expect created accounts to be.
|
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&SignatureSetDerivationData { hash: body },
|
|
&program,
|
|
);
|
|
|
|
// Fetch chain accounts to verify state.
|
|
let posted_message: PostedMessage = common::get_account_data(client, &message_key);
|
|
let signatures: SignatureSetData = common::get_account_data(client, &signature_set);
|
|
|
|
// Verify on chain Message
|
|
assert_eq!(posted_message.0.vaa_version, 0);
|
|
assert_eq!(posted_message.0.persist, false);
|
|
assert_eq!(posted_message.0.vaa_signature_account, signature_set);
|
|
assert_eq!(posted_message.0.nonce, nonce);
|
|
assert_eq!(posted_message.0.sequence, sequence);
|
|
assert_eq!(posted_message.0.emitter_chain, 1);
|
|
assert_eq!(posted_message.0.payload, message);
|
|
assert_eq!(
|
|
posted_message.0.emitter_address,
|
|
emitter.pubkey().to_bytes()
|
|
);
|
|
|
|
// Verify on chain Signatures
|
|
assert_eq!(signatures.hash, body);
|
|
assert_eq!(signatures.guardian_set_index, 1);
|
|
|
|
for (signature, secret_key) in signatures.signatures.iter().zip(context.secret.iter()) {
|
|
// Sign message locally.
|
|
let (local_sig, recover_id) =
|
|
secp256k1::sign(&Secp256k1Message::parse(&body_hash), &secret_key);
|
|
|
|
// Combine recoverify with signature to match 65 byte layout.
|
|
let mut signature_bytes = [0u8; 65];
|
|
signature_bytes[64] = recover_id.serialize();
|
|
(&mut signature_bytes[0..64]).copy_from_slice(&local_sig.serialize());
|
|
|
|
// Signature stored should on chain be as expected.
|
|
assert_eq!(*signature, signature_bytes);
|
|
}
|
|
}
|
|
|
|
fn test_guardian_set_change_fails(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Use a random emitter key to confirm the bridge rejects transactions from non-governance key.
|
|
let emitter = Keypair::new();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Upgrade the guardian set with a new set of guardians.
|
|
let (new_public_keys, new_secret_keys) = common::generate_keys(6);
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadGuardianSetChange {
|
|
new_guardian_set_index: 2,
|
|
new_guardian_set: new_public_keys.clone(),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
|
|
assert!(common::upgrade_guardian_set(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
1,
|
|
2,
|
|
sequence,
|
|
)
|
|
.is_err());
|
|
}
|
|
|
|
fn test_set_fees(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
let emitter = Keypair::from_bytes(&GOVERNANCE_KEY).unwrap();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadSetMessageFee {
|
|
fee: U256::from(100),
|
|
persisted_fee: U256::from(100),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::set_fees(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
sequence,
|
|
)
|
|
.unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Fetch Bridge to check on-state value.
|
|
let bridge_key = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program);
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
let bridge: BridgeData = common::get_account_data(client, &bridge_key);
|
|
assert_eq!(bridge.config.fee, 100);
|
|
assert_eq!(bridge.config.fee_persistent, 100);
|
|
|
|
// Check that posting a new message fails with too small a fee.
|
|
let account_balance = client.get_account(&fee_collector).unwrap().lamports;
|
|
let emitter = Keypair::new();
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = [0u8; 32].to_vec();
|
|
assert!(common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
50,
|
|
false
|
|
)
|
|
.is_err());
|
|
common::sync(client, payer);
|
|
|
|
assert_eq!(
|
|
client.get_account(&fee_collector).unwrap().lamports,
|
|
account_balance,
|
|
);
|
|
|
|
// And succeeds with the new.
|
|
let emitter = Keypair::new();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = [0u8; 32].to_vec();
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
100,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Verify that the fee collector was paid.
|
|
assert_eq!(
|
|
client.get_account(&fee_collector).unwrap().lamports,
|
|
account_balance + 100,
|
|
);
|
|
|
|
// Derive where we expect created accounts to be.
|
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&SignatureSetDerivationData { hash: body },
|
|
&program,
|
|
);
|
|
|
|
// And that the new message is on chain.
|
|
let posted_message: PostedMessage = common::get_account_data(client, &message_key);
|
|
let signatures: SignatureSetData = common::get_account_data(client, &signature_set);
|
|
|
|
// Verify on chain Message
|
|
assert_eq!(posted_message.0.vaa_version, 0);
|
|
assert_eq!(posted_message.0.persist, false);
|
|
assert_eq!(posted_message.0.vaa_signature_account, signature_set);
|
|
assert_eq!(posted_message.0.nonce, nonce);
|
|
assert_eq!(posted_message.0.sequence, sequence);
|
|
assert_eq!(posted_message.0.emitter_chain, 1);
|
|
assert_eq!(posted_message.0.payload, message);
|
|
assert_eq!(
|
|
posted_message.0.emitter_address,
|
|
emitter.pubkey().to_bytes()
|
|
);
|
|
|
|
// Verify on chain Signatures
|
|
assert_eq!(signatures.hash, body);
|
|
assert_eq!(signatures.guardian_set_index, 1);
|
|
|
|
for (signature, secret_key) in signatures.signatures.iter().zip(context.secret.iter()) {
|
|
// Sign message locally.
|
|
let (local_sig, recover_id) =
|
|
secp256k1::sign(&Secp256k1Message::parse(&body_hash), &secret_key);
|
|
|
|
// Combine recoverify with signature to match 65 byte layout.
|
|
let mut signature_bytes = [0u8; 65];
|
|
signature_bytes[64] = recover_id.serialize();
|
|
(&mut signature_bytes[0..64]).copy_from_slice(&local_sig.serialize());
|
|
|
|
// Signature stored should on chain be as expected.
|
|
assert_eq!(*signature, signature_bytes);
|
|
}
|
|
}
|
|
|
|
fn test_set_fees_fails(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Use a random key to confirm only the governance key is respected.
|
|
let emitter = Keypair::new();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadSetMessageFee {
|
|
fee: U256::from(100),
|
|
persisted_fee: U256::from(100),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
assert!(common::set_fees(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
sequence,
|
|
)
|
|
.is_err());
|
|
common::sync(client, payer);
|
|
}
|
|
|
|
fn test_free_fees(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
let emitter = Keypair::from_bytes(&GOVERNANCE_KEY).unwrap();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Set Fees to 0.
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadSetMessageFee {
|
|
fee: U256::from(0),
|
|
persisted_fee: U256::from(0),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::set_fees(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
sequence,
|
|
)
|
|
.unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Fetch Bridge to check on-state value.
|
|
let bridge_key = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program);
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
let bridge: BridgeData = common::get_account_data(client, &bridge_key);
|
|
assert_eq!(bridge.config.fee, 0);
|
|
assert_eq!(bridge.config.fee_persistent, 0);
|
|
|
|
// Check that posting a new message is free.
|
|
let account_balance = client.get_account(&fee_collector).unwrap().lamports;
|
|
let emitter = Keypair::new();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = [0u8; 32].to_vec();
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
0,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::sync(client, payer);
|
|
|
|
// Verify that the fee collector was paid.
|
|
assert_eq!(
|
|
client.get_account(&fee_collector).unwrap().lamports,
|
|
account_balance,
|
|
);
|
|
|
|
// Derive where we expect created accounts to be.
|
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&SignatureSetDerivationData { hash: body },
|
|
&program,
|
|
);
|
|
|
|
// And that the new message is on chain.
|
|
let posted_message: PostedMessage = common::get_account_data(client, &message_key);
|
|
let signatures: SignatureSetData = common::get_account_data(client, &signature_set);
|
|
|
|
// Verify on chain Message
|
|
assert_eq!(posted_message.0.vaa_version, 0);
|
|
assert_eq!(posted_message.0.persist, false);
|
|
assert_eq!(posted_message.0.vaa_signature_account, signature_set);
|
|
assert_eq!(posted_message.0.nonce, nonce);
|
|
assert_eq!(posted_message.0.sequence, sequence);
|
|
assert_eq!(posted_message.0.emitter_chain, 1);
|
|
assert_eq!(posted_message.0.payload, message);
|
|
assert_eq!(
|
|
posted_message.0.emitter_address,
|
|
emitter.pubkey().to_bytes()
|
|
);
|
|
|
|
// Verify on chain Signatures
|
|
assert_eq!(signatures.hash, body);
|
|
assert_eq!(signatures.guardian_set_index, 1);
|
|
|
|
for (signature, secret_key) in signatures.signatures.iter().zip(context.secret.iter()) {
|
|
// Sign message locally.
|
|
let (local_sig, recover_id) =
|
|
secp256k1::sign(&Secp256k1Message::parse(&body_hash), &secret_key);
|
|
|
|
// Combine recoverify with signature to match 65 byte layout.
|
|
let mut signature_bytes = [0u8; 65];
|
|
signature_bytes[64] = recover_id.serialize();
|
|
(&mut signature_bytes[0..64]).copy_from_slice(&local_sig.serialize());
|
|
|
|
// Signature stored should on chain be as expected.
|
|
assert_eq!(*signature, signature_bytes);
|
|
}
|
|
}
|
|
|
|
fn test_transfer_fees(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
let emitter = Keypair::from_bytes(&GOVERNANCE_KEY).unwrap();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
let recipient = Keypair::new();
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadTransferFees {
|
|
amount: 100.into(),
|
|
to: payer.pubkey().to_bytes(),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
// Fetch accounts for chain state checking.
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
let account_balance = client.get_account(&fee_collector).unwrap().lamports;
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::transfer_fees(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
payer.pubkey(),
|
|
sequence,
|
|
)
|
|
.unwrap();
|
|
common::sync(client, payer);
|
|
}
|
|
|
|
fn test_transfer_fees_fails(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Use an invalid emitter.
|
|
let emitter = Keypair::new();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
let recipient = Keypair::new();
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadTransferFees {
|
|
amount: 100.into(),
|
|
to: payer.pubkey().to_bytes(),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
// Fetch accounts for chain state checking.
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
let account_balance = client.get_account(&fee_collector).unwrap().lamports;
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
|
|
assert!(common::transfer_fees(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
payer.pubkey(),
|
|
sequence,
|
|
)
|
|
.is_err());
|
|
}
|
|
|
|
fn test_transfer_too_much(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
let emitter = Keypair::from_bytes(&GOVERNANCE_KEY).unwrap();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
let recipient = Keypair::new();
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadTransferFees {
|
|
amount: 100_000_000_000u64.into(),
|
|
to: payer.pubkey().to_bytes(),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
// Fetch accounts for chain state checking.
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
let account_balance = client.get_account(&fee_collector).unwrap().lamports;
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
|
|
// Should fail to transfer.
|
|
assert!(common::transfer_fees(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
payer.pubkey(),
|
|
sequence,
|
|
)
|
|
.is_err());
|
|
}
|
|
|
|
fn test_foreign_bridge_messages(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = [0u8; 32].to_vec();
|
|
let emitter = Keypair::new();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Verify the VAA generated on a foreign chain.
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0, 2);
|
|
|
|
// Derive where we expect created accounts to be.
|
|
let message_key = Message::<'_, { AccountState::MaybeInitialized }>::key(
|
|
&MessageDerivationData {
|
|
emitter_key: emitter.pubkey().to_bytes(),
|
|
emitter_chain: vaa.emitter_chain,
|
|
nonce,
|
|
sequence: Some(vaa.sequence),
|
|
payload: message.clone(),
|
|
},
|
|
&program,
|
|
);
|
|
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 0).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::sync(client, payer);
|
|
|
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
|
&SignatureSetDerivationData { hash: body },
|
|
&program,
|
|
);
|
|
|
|
// Fetch chain accounts to verify state.
|
|
let posted_message: PostedMessage = common::get_account_data(client, &message_key);
|
|
let signatures: SignatureSetData = common::get_account_data(client, &signature_set);
|
|
|
|
assert_eq!(posted_message.0.vaa_version, 0);
|
|
assert_eq!(posted_message.0.persist, false);
|
|
assert_eq!(posted_message.0.vaa_signature_account, signature_set);
|
|
assert_eq!(posted_message.0.nonce, nonce);
|
|
assert_eq!(posted_message.0.sequence, sequence);
|
|
assert_eq!(posted_message.0.emitter_chain, 2);
|
|
assert_eq!(posted_message.0.payload, message);
|
|
assert_eq!(
|
|
posted_message.0.emitter_address,
|
|
emitter.pubkey().to_bytes()
|
|
);
|
|
|
|
// Verify on chain Signatures
|
|
assert_eq!(signatures.hash, body);
|
|
assert_eq!(signatures.guardian_set_index, 0);
|
|
|
|
for (signature, secret_key) in signatures.signatures.iter().zip(context.secret.iter()) {
|
|
// Sign message locally.
|
|
let (local_sig, recover_id) =
|
|
secp256k1::sign(&Secp256k1Message::parse(&body_hash), &secret_key);
|
|
|
|
// Combine recoverify with signature to match 65 byte layout.
|
|
let mut signature_bytes = [0u8; 65];
|
|
signature_bytes[64] = recover_id.serialize();
|
|
(&mut signature_bytes[0..64]).copy_from_slice(&local_sig.serialize());
|
|
|
|
// Signature stored should on chain be as expected.
|
|
assert_eq!(*signature, signature_bytes);
|
|
}
|
|
}
|
|
|
|
fn test_transfer_total_fails(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
let emitter = Keypair::from_bytes(&GOVERNANCE_KEY).unwrap();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
|
|
// Be sure any previous tests have fully committed.
|
|
common::sync(client, payer);
|
|
|
|
let fee_collector = FeeCollector::key(None, &program);
|
|
let account_balance = client.get_account(&fee_collector).unwrap().lamports;
|
|
|
|
// Prepare to remove total balance, adding 10_000 to include the fee we're about to pay.
|
|
let recipient = Keypair::new();
|
|
let nonce = rand::thread_rng().gen();
|
|
let message = GovernancePayloadTransferFees {
|
|
amount: (account_balance + 10_000).into(),
|
|
to: payer.pubkey().to_bytes(),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 1).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
|
|
// Transferring total fees should fail, to prevent the account being de-allocated.
|
|
assert!(common::transfer_fees(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
payer.pubkey(),
|
|
sequence,
|
|
)
|
|
.is_err());
|
|
common::sync(client, payer);
|
|
|
|
// The fee should have been paid, but other than that the balance should be exactly the same,
|
|
// I.E non-zero.
|
|
assert_eq!(
|
|
client.get_account(&fee_collector).unwrap().lamports,
|
|
account_balance + 10_000
|
|
);
|
|
}
|
|
|
|
fn test_upgrade_contract(context: &mut Context) {
|
|
// Initialize a wormhole bridge on Solana to test with.
|
|
let (ref payer, ref client, ref program) = common::setup();
|
|
|
|
// Upgrade the guardian set with a new set of guardians.
|
|
let (new_public_keys, new_secret_keys) = common::generate_keys(1);
|
|
|
|
let nonce = rand::thread_rng().gen();
|
|
let emitter = Keypair::from_bytes(&GOVERNANCE_KEY).unwrap();
|
|
let sequence = context.seq.next(emitter.pubkey().to_bytes());
|
|
let message = GovernancePayloadUpgrade {
|
|
new_contract: Pubkey::new_unique(),
|
|
}
|
|
.try_to_vec()
|
|
.unwrap();
|
|
|
|
let message_key = common::post_message(
|
|
client,
|
|
program,
|
|
payer,
|
|
&emitter,
|
|
nonce,
|
|
message.clone(),
|
|
10_000,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0, 1);
|
|
common::verify_signatures(client, program, payer, body, &context.secret, 0).unwrap();
|
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
|
common::upgrade_contract(
|
|
client,
|
|
program,
|
|
payer,
|
|
message_key,
|
|
emitter.pubkey(),
|
|
Pubkey::new_unique(),
|
|
sequence,
|
|
)
|
|
.unwrap();
|
|
common::sync(client, payer);
|
|
}
|