Fix bridge fee and implement CLI
Change-Id: Ib17b335e05359fd4baf614d0b4eaae459814b04d
This commit is contained in:
parent
d677311d70
commit
ece0de4bef
|
@ -1,4 +1,5 @@
|
||||||
target
|
target
|
||||||
bin
|
bin
|
||||||
cli/target
|
bridge/target
|
||||||
agent/target
|
solitaire/target
|
||||||
|
modules/token_bridge/target
|
|
@ -34,7 +34,7 @@ dependencies = [
|
||||||
"bridge",
|
"bridge",
|
||||||
"bs58",
|
"bs58",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap 2.33.3",
|
"clap",
|
||||||
"futures 0.3.15",
|
"futures 0.3.15",
|
||||||
"hex",
|
"hex",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -463,44 +463,12 @@ dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"strsim 0.8.0",
|
"strsim",
|
||||||
"textwrap 0.11.0",
|
"textwrap",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap"
|
|
||||||
version = "3.0.0-beta.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
|
|
||||||
dependencies = [
|
|
||||||
"atty",
|
|
||||||
"bitflags",
|
|
||||||
"clap_derive",
|
|
||||||
"indexmap",
|
|
||||||
"lazy_static",
|
|
||||||
"os_str_bytes",
|
|
||||||
"strsim 0.10.0",
|
|
||||||
"termcolor",
|
|
||||||
"textwrap 0.12.1",
|
|
||||||
"unicode-width",
|
|
||||||
"vec_map",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_derive"
|
|
||||||
version = "3.0.0-beta.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro-error",
|
|
||||||
"proc-macro2 1.0.27",
|
|
||||||
"quote 1.0.9",
|
|
||||||
"syn 1.0.73",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "client"
|
name = "client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -508,9 +476,12 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"borsh",
|
"borsh",
|
||||||
"bridge",
|
"bridge",
|
||||||
"clap 3.0.0-beta.2",
|
"clap",
|
||||||
|
"hex",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
|
"solana-clap-utils",
|
||||||
|
"solana-cli-config",
|
||||||
"solana-client",
|
"solana-client",
|
||||||
"solana-program",
|
"solana-program",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
|
@ -2024,12 +1995,6 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "os_str_bytes"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ouroboros"
|
name = "ouroboros"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -2282,30 +2247,6 @@ dependencies = [
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error-attr",
|
|
||||||
"proc-macro2 1.0.27",
|
|
||||||
"quote 1.0.9",
|
|
||||||
"syn 1.0.73",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error-attr"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2 1.0.27",
|
|
||||||
"quote 1.0.9",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.19"
|
version = "0.5.19"
|
||||||
|
@ -3026,7 +2967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "484288242b2b175bf2b7554497318e39b23ee921989f976387dbe48e60b2f256"
|
checksum = "484288242b2b175bf2b7554497318e39b23ee921989f976387dbe48e60b2f256"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap 2.33.3",
|
"clap",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
"solana-remote-wallet",
|
"solana-remote-wallet",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
|
@ -3059,7 +3000,7 @@ dependencies = [
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bincode",
|
"bincode",
|
||||||
"bs58",
|
"bs58",
|
||||||
"clap 2.33.3",
|
"clap",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"jsonrpc-core",
|
"jsonrpc-core",
|
||||||
"log",
|
"log",
|
||||||
|
@ -3132,7 +3073,7 @@ checksum = "ed5e6adf551ca4e761c3395bb684ba5d907a051007a6dbf2d57cb99d2691e031"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap 2.33.3",
|
"clap",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
|
@ -3222,7 +3163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa68e25fb6452b85733cf5c301988b56fd2d5d5a8e93c75cf38cbec06efc2eae"
|
checksum = "fa68e25fb6452b85733cf5c301988b56fd2d5d5a8e93c75cf38cbec06efc2eae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"clap 2.33.3",
|
"clap",
|
||||||
"log",
|
"log",
|
||||||
"nix",
|
"nix",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
|
@ -3591,12 +3532,6 @@ version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strsim"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -3711,15 +3646,6 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "textwrap"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.25"
|
version = "1.0.25"
|
||||||
|
|
|
@ -8,11 +8,14 @@ edition = "2018"
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.40"
|
||||||
borsh = "0.8.1"
|
borsh = "0.8.1"
|
||||||
bridge = { path = "../program", features = ["no-idl", "no-entrypoint", "client"] }
|
bridge = { path = "../program", features = ["no-idl", "no-entrypoint", "client"] }
|
||||||
clap = "3.0.0-beta.2"
|
clap = "2.33.0"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
shellexpand = "2.1.0"
|
shellexpand = "2.1.0"
|
||||||
solana-client = "=1.7.0"
|
solana-client = "=1.7.0"
|
||||||
solana-program = "=1.7.0"
|
solana-program = "=1.7.0"
|
||||||
solana-sdk = "=1.7.0"
|
solana-sdk = "=1.7.0"
|
||||||
|
solana-cli-config = "=1.7.0"
|
||||||
solitaire = { path = "../../solitaire/program" }
|
solitaire = { path = "../../solitaire/program" }
|
||||||
solitaire-client = { path = "../../solitaire/client" }
|
solitaire-client = { path = "../../solitaire/client" }
|
||||||
|
solana-clap-utils = "=1.7.0"
|
||||||
|
hex = "0.4.3"
|
|
@ -1,97 +1,336 @@
|
||||||
use borsh::BorshSerialize;
|
#![feature(const_generics)]
|
||||||
use bridge::{
|
#![allow(warnings)]
|
||||||
api,
|
|
||||||
types,
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
mem::size_of,
|
||||||
|
process::exit,
|
||||||
|
};
|
||||||
|
|
||||||
|
use borsh::BorshDeserialize;
|
||||||
|
use bridge::{
|
||||||
|
accounts::{
|
||||||
|
Bridge,
|
||||||
|
FeeCollector,
|
||||||
|
},
|
||||||
|
types::BridgeData,
|
||||||
|
};
|
||||||
|
use clap::{
|
||||||
|
crate_description,
|
||||||
|
crate_name,
|
||||||
|
crate_version,
|
||||||
|
value_t,
|
||||||
|
App,
|
||||||
|
AppSettings,
|
||||||
|
Arg,
|
||||||
|
SubCommand,
|
||||||
|
};
|
||||||
|
use hex;
|
||||||
|
use solana_clap_utils::{
|
||||||
|
input_parsers::{
|
||||||
|
keypair_of,
|
||||||
|
pubkey_of,
|
||||||
|
value_of,
|
||||||
|
},
|
||||||
|
input_validators::{
|
||||||
|
is_keypair,
|
||||||
|
is_pubkey_or_keypair,
|
||||||
|
is_url,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use clap::Clap;
|
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
rpc_config::RpcSendTransactionConfig,
|
rpc_config::RpcSendTransactionConfig,
|
||||||
};
|
};
|
||||||
use solana_program::pubkey::Pubkey;
|
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
|
native_token::*,
|
||||||
|
program_error::ProgramError::AccountAlreadyInitialized,
|
||||||
|
pubkey::Pubkey,
|
||||||
signature::{
|
signature::{
|
||||||
read_keypair_file,
|
read_keypair_file,
|
||||||
Signer as SolSigner,
|
Keypair,
|
||||||
|
Signer,
|
||||||
},
|
},
|
||||||
|
system_instruction::transfer,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solitaire_client::{
|
|
||||||
AccEntry,
|
|
||||||
ToInstruction,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bridge::accounts::{
|
|
||||||
GuardianSet,
|
|
||||||
GuardianSetDerivationData,
|
|
||||||
};
|
|
||||||
use solitaire::{
|
use solitaire::{
|
||||||
processors::seeded::Seeded,
|
processors::seeded::Seeded,
|
||||||
AccountState,
|
AccountState,
|
||||||
};
|
};
|
||||||
use std::error;
|
|
||||||
|
|
||||||
#[derive(Clap)]
|
struct Config {
|
||||||
pub struct Opts {
|
rpc_client: RpcClient,
|
||||||
#[clap(long)]
|
owner: Keypair,
|
||||||
bridge_address: Pubkey,
|
fee_payer: Keypair,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ErrBox = Box<dyn error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
|
type CommmandResult = Result<Option<Transaction>, Error>;
|
||||||
|
|
||||||
pub const DEFAULT_MESSAGE_FEE: u64 = 42;
|
fn command_deploy_bridge(
|
||||||
pub const DEFAULT_GUARDIAN_SET_EXPIRATION_TIME: u32 = 42;
|
config: &Config,
|
||||||
|
bridge: &Pubkey,
|
||||||
|
_initial_guardian: Vec<[u8; 20]>,
|
||||||
|
guardian_expiration: u32,
|
||||||
|
message_fee: u64,
|
||||||
|
) -> CommmandResult {
|
||||||
|
println!("Initializing Wormhole bridge {}", bridge);
|
||||||
|
|
||||||
fn main() -> Result<(), ErrBox> {
|
let minimum_balance_for_rent_exemption = config
|
||||||
let opts = Opts::parse();
|
.rpc_client
|
||||||
|
.get_minimum_balance_for_rent_exemption(size_of::<BridgeData>())?;
|
||||||
|
|
||||||
let payer = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json"))
|
let ix = bridge::client_instructions::initialize(
|
||||||
.expect("Example requires a keypair file");
|
*bridge,
|
||||||
|
config.owner.pubkey(),
|
||||||
|
message_fee,
|
||||||
|
guardian_expiration,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
println!("config account: {}, ", ix.accounts[0].pubkey.to_string());
|
||||||
|
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
// Keypair is not Clone
|
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
||||||
let payer_for_tx = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json"))
|
check_fee_payer_balance(
|
||||||
.expect("Example requires a keypair file");
|
config,
|
||||||
let url = "http://localhost:8899".to_owned();
|
minimum_balance_for_rent_exemption + fee_calculator.calculate_fee(&transaction.message()),
|
||||||
|
)?;
|
||||||
|
transaction.sign(&[&config.fee_payer, &config.owner], recent_blockhash);
|
||||||
|
Ok(Some(transaction))
|
||||||
|
}
|
||||||
|
|
||||||
let client = RpcClient::new(url);
|
fn command_post_message(
|
||||||
|
config: &Config,
|
||||||
|
bridge: &Pubkey,
|
||||||
|
nonce: u32,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> CommmandResult {
|
||||||
|
println!("Posting a message to the wormhole");
|
||||||
|
|
||||||
let program_id = opts.bridge_address;
|
// Fetch the message fee
|
||||||
|
let bridge_config_account = config
|
||||||
|
.rpc_client
|
||||||
|
.get_account(&Bridge::<'_, { AccountState::Initialized }>::key(
|
||||||
|
None, bridge,
|
||||||
|
))?;
|
||||||
|
let bridge_config = BridgeData::try_from_slice(bridge_config_account.data.as_slice())?;
|
||||||
|
println!("Message fee: {} lamports", bridge_config.config.fee);
|
||||||
|
|
||||||
use AccEntry::*;
|
let transfer_ix = transfer(
|
||||||
let init = api::InitializeAccounts {
|
&config.owner.pubkey(),
|
||||||
bridge: Derived(program_id.clone()),
|
&FeeCollector::key(None, bridge),
|
||||||
guardian_set: Unprivileged(<GuardianSet<'_, { AccountState::Uninitialized }>>::key(
|
bridge_config.config.fee,
|
||||||
&GuardianSetDerivationData { index: 0 },
|
);
|
||||||
&program_id,
|
let ix = bridge::client_instructions::post_message(
|
||||||
)),
|
*bridge,
|
||||||
payer: Signer(payer),
|
config.owner.pubkey(),
|
||||||
|
config.fee_payer.pubkey(),
|
||||||
|
nonce,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut transaction =
|
||||||
|
Transaction::new_with_payer(&[transfer_ix, ix], Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
|
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
||||||
|
check_fee_payer_balance(config, fee_calculator.calculate_fee(&transaction.message()))?;
|
||||||
|
transaction.sign(&[&config.fee_payer, &config.owner], recent_blockhash);
|
||||||
|
Ok(Some(transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let matches = App::new(crate_name!())
|
||||||
|
.about(crate_description!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||||
|
.arg({
|
||||||
|
let arg = Arg::with_name("config_file")
|
||||||
|
.short("C")
|
||||||
|
.long("config")
|
||||||
|
.value_name("PATH")
|
||||||
|
.takes_value(true)
|
||||||
|
.global(true)
|
||||||
|
.help("Configuration file to use");
|
||||||
|
if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
|
||||||
|
arg.default_value(&config_file)
|
||||||
|
} else {
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("json_rpc_url")
|
||||||
|
.long("url")
|
||||||
|
.value_name("URL")
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(is_url)
|
||||||
|
.help("JSON RPC URL for the cluster. Default from the configuration file."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("owner")
|
||||||
|
.long("owner")
|
||||||
|
.value_name("KEYPAIR")
|
||||||
|
.validator(is_keypair)
|
||||||
|
.takes_value(true)
|
||||||
|
.help(
|
||||||
|
"Specify the contract payer account. \
|
||||||
|
This may be a keypair file, the ASK keyword. \
|
||||||
|
Defaults to the client keypair.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("fee_payer")
|
||||||
|
.long("fee-payer")
|
||||||
|
.value_name("KEYPAIR")
|
||||||
|
.validator(is_keypair)
|
||||||
|
.takes_value(true)
|
||||||
|
.help(
|
||||||
|
"Specify the fee-payer account. \
|
||||||
|
This may be a keypair file, the ASK keyword. \
|
||||||
|
Defaults to the client keypair.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("create-bridge")
|
||||||
|
.about("Create a new bridge")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("bridge")
|
||||||
|
.long("bridge")
|
||||||
|
.value_name("BRIDGE_KEY")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.takes_value(true)
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
.help("Specify the bridge program address"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("guardian")
|
||||||
|
.validator(is_hex)
|
||||||
|
.value_name("GUARDIAN_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(2)
|
||||||
|
.required(true)
|
||||||
|
.help("Address of the initial guardian"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("guardian_set_expiration")
|
||||||
|
.validator(is_u32)
|
||||||
|
.value_name("GUARDIAN_SET_EXPIRATION")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(3)
|
||||||
|
.required(true)
|
||||||
|
.help("Time in seconds after which a guardian set expires after an update"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("message_fee")
|
||||||
|
.validator(is_u64)
|
||||||
|
.value_name("MESSAGE_FEE")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(4)
|
||||||
|
.required(true)
|
||||||
|
.help("Initial message posting fee"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("post-message")
|
||||||
|
.about("Post a message via Wormhole")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("bridge")
|
||||||
|
.long("bridge")
|
||||||
|
.value_name("BRIDGE_KEY")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.takes_value(true)
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
.help("Specify the bridge program address"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("nonce")
|
||||||
|
.validator(is_u32)
|
||||||
|
.value_name("NONCE")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(2)
|
||||||
|
.required(true)
|
||||||
|
.help("Nonce of the message"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("data")
|
||||||
|
.validator(is_hex)
|
||||||
|
.value_name("DATA")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(3)
|
||||||
|
.required(true)
|
||||||
|
.help("Payload of the message"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
let cli_config = if let Some(config_file) = matches.value_of("config_file") {
|
||||||
|
solana_cli_config::Config::load(config_file).unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
solana_cli_config::Config::default()
|
||||||
|
};
|
||||||
|
let json_rpc_url = value_t!(matches, "json_rpc_url", String)
|
||||||
|
.unwrap_or_else(|_| cli_config.json_rpc_url.clone());
|
||||||
|
|
||||||
|
let client_keypair = || {
|
||||||
|
read_keypair_file(&cli_config.keypair_path).unwrap_or_else(|err| {
|
||||||
|
eprintln!("Unable to read {}: {}", cli_config.keypair_path, err);
|
||||||
|
exit(1)
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let init_args = bridge::instruction::Instruction::Initialize(types::BridgeConfig {
|
let owner = keypair_of(&matches, "owner").unwrap_or_else(client_keypair);
|
||||||
guardian_set_expiration_time: DEFAULT_GUARDIAN_SET_EXPIRATION_TIME,
|
let fee_payer = keypair_of(&matches, "fee_payer").unwrap_or_else(client_keypair);
|
||||||
fee: DEFAULT_MESSAGE_FEE,
|
|
||||||
});
|
|
||||||
|
|
||||||
let ix_data = init_args.try_to_vec()?;
|
Config {
|
||||||
|
rpc_client: RpcClient::new(json_rpc_url),
|
||||||
|
owner,
|
||||||
|
fee_payer,
|
||||||
|
commitment_config: CommitmentConfig::processed(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let (ix, signers) = init.to_ix(program_id, ix_data.as_slice())?;
|
let _ = match matches.subcommand() {
|
||||||
let (recent_blockhash, _) = client.get_recent_blockhash()?;
|
("create-bridge", Some(arg_matches)) => {
|
||||||
println!("Instruction ready.");
|
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
||||||
println!(
|
let initial_guardian: String = value_of(arg_matches, "guardian").unwrap();
|
||||||
"Signing for {} signer(s): {:?}",
|
let initial_data = hex::decode(initial_guardian).unwrap();
|
||||||
signers.len(),
|
let guardian_expiration: u32 =
|
||||||
signers.iter().map(|s| s.pubkey()).collect::<Vec<_>>()
|
value_of(arg_matches, "guardian_set_expiration").unwrap();
|
||||||
);
|
let msg_fee: u64 = value_of(arg_matches, "message_fee").unwrap();
|
||||||
|
|
||||||
let mut tx = Transaction::new_with_payer(&[ix], Some(&payer_for_tx.pubkey()));
|
let mut guardian = [0u8; 20];
|
||||||
|
guardian.copy_from_slice(&initial_data);
|
||||||
|
command_deploy_bridge(
|
||||||
|
&config,
|
||||||
|
&bridge,
|
||||||
|
vec![guardian],
|
||||||
|
guardian_expiration,
|
||||||
|
msg_fee,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
("post-message", Some(arg_matches)) => {
|
||||||
|
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
||||||
|
let data_str: String = value_of(arg_matches, "data").unwrap();
|
||||||
|
let data = hex::decode(data_str).unwrap();
|
||||||
|
let nonce: u32 = value_of(arg_matches, "nonce").unwrap();
|
||||||
|
|
||||||
tx.try_sign(&signers.iter().collect::<Vec<_>>(), recent_blockhash)?;
|
command_post_message(&config, &bridge, nonce, data)
|
||||||
println!("Transaction signed.");
|
}
|
||||||
|
|
||||||
let signature = client.send_and_confirm_transaction_with_spinner_and_config(
|
_ => unreachable!(),
|
||||||
&tx,
|
}
|
||||||
CommitmentConfig::processed(),
|
.and_then(|transaction| {
|
||||||
|
if let Some(transaction) = transaction {
|
||||||
|
let signature = config
|
||||||
|
.rpc_client
|
||||||
|
.send_and_confirm_transaction_with_spinner_and_config(
|
||||||
|
&transaction,
|
||||||
|
config.commitment_config,
|
||||||
RpcSendTransactionConfig {
|
RpcSendTransactionConfig {
|
||||||
skip_preflight: true,
|
skip_preflight: true,
|
||||||
preflight_commitment: None,
|
preflight_commitment: None,
|
||||||
|
@ -99,6 +338,77 @@ fn main() -> Result<(), ErrBox> {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
println!("Signature: {}", signature);
|
println!("Signature: {}", signature);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
eprintln!("{}", err);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_u8<T>(amount: T) -> Result<(), String>
|
||||||
|
where
|
||||||
|
T: AsRef<str> + Display,
|
||||||
|
{
|
||||||
|
if amount.as_ref().parse::<u8>().is_ok() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"Unable to parse input amount as integer, provided: {}",
|
||||||
|
amount
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_u32<T>(amount: T) -> Result<(), String>
|
||||||
|
where
|
||||||
|
T: AsRef<str> + Display,
|
||||||
|
{
|
||||||
|
if amount.as_ref().parse::<u32>().is_ok() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"Unable to parse input amount as integer, provided: {}",
|
||||||
|
amount
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_u64<T>(amount: T) -> Result<(), String>
|
||||||
|
where
|
||||||
|
T: AsRef<str> + Display,
|
||||||
|
{
|
||||||
|
if amount.as_ref().parse::<u64>().is_ok() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"Unable to parse input amount as integer, provided: {}",
|
||||||
|
amount
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_hex<T>(value: T) -> Result<(), String>
|
||||||
|
where
|
||||||
|
T: AsRef<str> + Display,
|
||||||
|
{
|
||||||
|
hex::decode(value.to_string())
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|e| format!("{}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_fee_payer_balance(config: &Config, required_balance: u64) -> Result<(), Error> {
|
||||||
|
let balance = config.rpc_client.get_balance(&config.fee_payer.pubkey())?;
|
||||||
|
if balance < required_balance {
|
||||||
|
Err(format!(
|
||||||
|
"Fee payer, {}, has insufficient balance: {} required, {} available",
|
||||||
|
config.fee_payer.pubkey(),
|
||||||
|
lamports_to_sol(required_balance),
|
||||||
|
lamports_to_sol(balance)
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{
|
accounts::{
|
||||||
Bridge,
|
Bridge,
|
||||||
|
FeeCollector,
|
||||||
GuardianSet,
|
GuardianSet,
|
||||||
GuardianSetDerivationData,
|
GuardianSetDerivationData,
|
||||||
},
|
},
|
||||||
|
@ -17,6 +18,7 @@ type Payer<'a> = Signer<Info<'a>>;
|
||||||
pub struct Initialize<'b> {
|
pub struct Initialize<'b> {
|
||||||
pub bridge: Bridge<'b, { AccountState::Uninitialized }>,
|
pub bridge: Bridge<'b, { AccountState::Uninitialized }>,
|
||||||
pub guardian_set: GuardianSet<'b, { AccountState::Uninitialized }>,
|
pub guardian_set: GuardianSet<'b, { AccountState::Uninitialized }>,
|
||||||
|
pub fee_collector: FeeCollector<'b>,
|
||||||
pub payer: Payer<'b>,
|
pub payer: Payer<'b>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,5 +51,15 @@ pub fn initialize(
|
||||||
accs.bridge.guardian_set_index = 0;
|
accs.bridge.guardian_set_index = 0;
|
||||||
accs.bridge.config = config;
|
accs.bridge.config = config;
|
||||||
|
|
||||||
|
// Initialize the fee collector account so it's rent exempt and will keep funds
|
||||||
|
accs.fee_collector.create(
|
||||||
|
ctx,
|
||||||
|
accs.payer.key,
|
||||||
|
Exempt,
|
||||||
|
0,
|
||||||
|
&solana_program::system_program::id(),
|
||||||
|
)?;
|
||||||
|
accs.bridge.last_lamports = accs.fee_collector.lamports();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,13 +89,12 @@ pub fn post_message(
|
||||||
.verify_derivation(ctx.program_id, &msg_derivation)?;
|
.verify_derivation(ctx.program_id, &msg_derivation)?;
|
||||||
|
|
||||||
// Fee handling
|
// Fee handling
|
||||||
let fee = transfer_fee();
|
|
||||||
if accs
|
if accs
|
||||||
.fee_collector
|
.fee_collector
|
||||||
.lamports()
|
.lamports()
|
||||||
.checked_sub(accs.bridge.last_lamports)
|
.checked_sub(accs.bridge.last_lamports)
|
||||||
.ok_or(MathOverflow)?
|
.ok_or(MathOverflow)?
|
||||||
< fee
|
< accs.bridge.config.fee
|
||||||
{
|
{
|
||||||
return Err(InsufficientFees.into());
|
return Err(InsufficientFees.into());
|
||||||
}
|
}
|
||||||
|
@ -124,7 +123,3 @@ pub fn post_message(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transfer_fee() -> u64 {
|
|
||||||
500
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use borsh::BorshSerialize;
|
use borsh::BorshSerialize;
|
||||||
use solitaire::processors::seeded::Seeded;
|
|
||||||
use solitaire::AccountState;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
borsh::try_from_slice_unchecked,
|
borsh::try_from_slice_unchecked,
|
||||||
hash,
|
hash,
|
||||||
|
@ -17,6 +15,10 @@ use solana_program::{
|
||||||
system_program,
|
system_program,
|
||||||
sysvar,
|
sysvar,
|
||||||
};
|
};
|
||||||
|
use solitaire::{
|
||||||
|
processors::seeded::Seeded,
|
||||||
|
AccountState,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{
|
accounts::{
|
||||||
|
@ -41,25 +43,22 @@ use crate::{
|
||||||
pub fn initialize(
|
pub fn initialize(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
payer: Pubkey,
|
payer: Pubkey,
|
||||||
bridge: Pubkey,
|
|
||||||
guardian_set_index: u32,
|
|
||||||
guardian_set: Pubkey,
|
|
||||||
fee: u64,
|
fee: u64,
|
||||||
guardian_set_expiration_time: u32,
|
guardian_set_expiration_time: u32,
|
||||||
) -> solitaire::Result<Instruction> {
|
) -> solitaire::Result<Instruction> {
|
||||||
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
||||||
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
||||||
&GuardianSetDerivationData {
|
&GuardianSetDerivationData { index: 0 },
|
||||||
index: guardian_set_index,
|
|
||||||
},
|
|
||||||
&program_id,
|
&program_id,
|
||||||
);
|
);
|
||||||
|
let fee_collector = FeeCollector::key(None, &program_id);
|
||||||
|
|
||||||
Ok(Instruction {
|
Ok(Instruction {
|
||||||
program_id,
|
program_id,
|
||||||
accounts: vec![
|
accounts: vec![
|
||||||
AccountMeta::new(bridge, false),
|
AccountMeta::new(bridge, false),
|
||||||
AccountMeta::new(guardian_set, false),
|
AccountMeta::new(guardian_set, false),
|
||||||
|
AccountMeta::new(fee_collector, false),
|
||||||
AccountMeta::new(payer, true),
|
AccountMeta::new(payer, true),
|
||||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||||
|
@ -75,22 +74,27 @@ pub fn initialize(
|
||||||
pub fn post_message(
|
pub fn post_message(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
payer: Pubkey,
|
payer: Pubkey,
|
||||||
bridge: Pubkey,
|
|
||||||
emitter: Pubkey,
|
emitter: Pubkey,
|
||||||
message: PostedMessage,
|
nonce: u32,
|
||||||
sequence: u64,
|
payload: Vec<u8>,
|
||||||
) -> solitaire::Result<Instruction> {
|
) -> solitaire::Result<Instruction> {
|
||||||
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
||||||
let fee_collector = FeeCollector::<'_>::key(None, &program_id);
|
let fee_collector = FeeCollector::<'_>::key(None, &program_id);
|
||||||
let sequence = Sequence::<'_>::key(&SequenceDerivationData {
|
let sequence = Sequence::<'_>::key(
|
||||||
|
&SequenceDerivationData {
|
||||||
emitter_key: &emitter,
|
emitter_key: &emitter,
|
||||||
}, &program_id);
|
},
|
||||||
let message = Message::<'_, { AccountState::Uninitialized }>::key(&MessageDerivationData {
|
&program_id,
|
||||||
|
);
|
||||||
|
let message = Message::<'_, { AccountState::Uninitialized }>::key(
|
||||||
|
&MessageDerivationData {
|
||||||
emitter_key: emitter.to_bytes(),
|
emitter_key: emitter.to_bytes(),
|
||||||
emitter_chain: message.emitter_chain,
|
emitter_chain: 1,
|
||||||
nonce: message.nonce,
|
nonce,
|
||||||
payload: message.payload.clone(),
|
payload: payload.clone(),
|
||||||
}, &program_id);
|
},
|
||||||
|
&program_id,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Instruction {
|
Ok(Instruction {
|
||||||
program_id,
|
program_id,
|
||||||
|
@ -108,10 +112,10 @@ pub fn post_message(
|
||||||
],
|
],
|
||||||
|
|
||||||
data: crate::instruction::Instruction::PostMessage(PostMessageData {
|
data: crate::instruction::Instruction::PostMessage(PostMessageData {
|
||||||
nonce: 0,
|
nonce,
|
||||||
payload: vec![],
|
payload: payload.clone(),
|
||||||
})
|
})
|
||||||
.try_to_vec()?
|
.try_to_vec()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +159,7 @@ pub fn verify_signatures(
|
||||||
signers,
|
signers,
|
||||||
initial_creation: true,
|
initial_creation: true,
|
||||||
})
|
})
|
||||||
.try_to_vec()?
|
.try_to_vec()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use solana_program::msg;
|
||||||
// package as soon as possible.
|
// package as soon as possible.
|
||||||
pub mod accounts;
|
pub mod accounts;
|
||||||
pub mod api;
|
pub mod api;
|
||||||
|
pub mod client_instructions;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod vaa;
|
pub mod vaa;
|
||||||
|
|
||||||
|
@ -25,14 +26,14 @@ pub use api::{
|
||||||
PostMessageData,
|
PostMessageData,
|
||||||
PostVAA,
|
PostVAA,
|
||||||
PostVAAData,
|
PostVAAData,
|
||||||
|
Signature,
|
||||||
|
UninitializedMessage,
|
||||||
UpgradeContract,
|
UpgradeContract,
|
||||||
UpgradeContractData,
|
UpgradeContractData,
|
||||||
UpgradeGuardianSet,
|
UpgradeGuardianSet,
|
||||||
UpgradeGuardianSetData,
|
UpgradeGuardianSetData,
|
||||||
VerifySignatures,
|
VerifySignatures,
|
||||||
VerifySignaturesData,
|
VerifySignaturesData,
|
||||||
UninitializedMessage,
|
|
||||||
Signature,
|
|
||||||
};
|
};
|
||||||
use types::BridgeConfig;
|
use types::BridgeConfig;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue