Align behaviour of persistent message fees

Change-Id: Ic9c6c40dbac2399e0eaf3a861dff33254a828a18
This commit is contained in:
Hendrik Hofstadt 2021-07-05 17:00:50 +02:00
parent 0f854dc08b
commit 7784e74725
13 changed files with 329 additions and 111 deletions

View File

@ -112,7 +112,9 @@ Module [32]byte = "Core"
Action uint16 = 3 Action uint16 = 3
Chain uint16 Chain uint16
// Message fee in the native token // Message fee in the native token
Fee uint64 Fee uint256
// Persistent message fee in the native token
FeePersistent uint256
``` ```
TransferFees: TransferFees:
@ -124,7 +126,7 @@ Module [32]byte = "Core"
Action uint16 = 4 Action uint16 = 4
Chain uint16 Chain uint16
// Amount being transferred (big-endian uint256) // Amount being transferred (big-endian uint256)
Amount [32]uint8 Amount uint256
// Address of the recipient. Left-zero-padded if shorter than 32 bytes // Address of the recipient. Left-zero-padded if shorter than 32 bytes
To [32]uint8 To [32]uint8
``` ```

View File

@ -79,6 +79,7 @@ fn command_deploy_bridge(
initial_guardians: Vec<[u8; 20]>, initial_guardians: Vec<[u8; 20]>,
guardian_expiration: u32, guardian_expiration: u32,
message_fee: u64, message_fee: u64,
message_fee_persistent: u64,
) -> CommmandResult { ) -> CommmandResult {
println!("Initializing Wormhole bridge {}", bridge); println!("Initializing Wormhole bridge {}", bridge);
@ -90,6 +91,7 @@ fn command_deploy_bridge(
*bridge, *bridge,
config.owner.pubkey(), config.owner.pubkey(),
message_fee, message_fee,
message_fee_persistent,
guardian_expiration, guardian_expiration,
initial_guardians.as_slice(), initial_guardians.as_slice(),
) )
@ -122,12 +124,19 @@ fn command_post_message(
None, bridge, None, bridge,
))?; ))?;
let bridge_config = BridgeData::try_from_slice(bridge_config_account.data.as_slice())?; let bridge_config = BridgeData::try_from_slice(bridge_config_account.data.as_slice())?;
println!("Message fee: {} lamports", bridge_config.config.fee); let fee = {
if persist {
bridge_config.config.fee_persistent
} else {
bridge_config.config.fee
}
};
println!("Message fee: {} lamports", fee);
let transfer_ix = transfer( let transfer_ix = transfer(
&config.owner.pubkey(), &config.owner.pubkey(),
&FeeCollector::key(None, bridge), &FeeCollector::key(None, bridge),
bridge_config.config.fee, fee,
); );
let (_, ix) = bridge::instructions::post_message( let (_, ix) = bridge::instructions::post_message(
*bridge, *bridge,
@ -237,6 +246,15 @@ fn main() {
.index(4) .index(4)
.required(true) .required(true)
.help("Initial message posting fee"), .help("Initial message posting fee"),
)
.arg(
Arg::with_name("message_fee_persistent")
.validator(is_u64)
.value_name("MESSAGE_FEE_PERSISTENT")
.takes_value(true)
.index(5)
.required(true)
.help("Initial persistent message posting fee"),
), ),
) )
.subcommand( .subcommand(
@ -315,6 +333,7 @@ fn main() {
let guardian_expiration: u32 = let guardian_expiration: u32 =
value_of(arg_matches, "guardian_set_expiration").unwrap(); value_of(arg_matches, "guardian_set_expiration").unwrap();
let msg_fee: u64 = value_of(arg_matches, "message_fee").unwrap(); let msg_fee: u64 = value_of(arg_matches, "message_fee").unwrap();
let msg_fee_persistent: u64 = value_of(arg_matches, "message_fee_persistent").unwrap();
let mut guardian = [0u8; 20]; let mut guardian = [0u8; 20];
guardian.copy_from_slice(&initial_data); guardian.copy_from_slice(&initial_data);
@ -324,6 +343,7 @@ fn main() {
vec![guardian], vec![guardian],
guardian_expiration, guardian_expiration,
msg_fee, msg_fee,
msg_fee_persistent,
) )
} }
("post-message", Some(arg_matches)) => { ("post-message", Some(arg_matches)) => {

View File

@ -170,8 +170,8 @@ pub struct SetFeesData {}
pub fn set_fees(ctx: &ExecutionContext, accs: &mut SetFees, _data: SetFeesData) -> Result<()> { pub fn set_fees(ctx: &ExecutionContext, accs: &mut SetFees, _data: SetFeesData) -> Result<()> {
accs.vaa.claim(ctx, accs.payer.key)?; accs.vaa.claim(ctx, accs.payer.key)?;
// Set expiration time for the old set accs.bridge.config.fee = accs.vaa.fee.as_u64();
accs.bridge.config.fee = accs.vaa.fee; accs.bridge.config.fee_persistent = accs.vaa.persisted_fee.as_u64();
Ok(()) Ok(())
} }

View File

@ -37,6 +37,9 @@ pub struct InitializeData {
/// Amount of lamports that needs to be paid to the protocol to post a message /// Amount of lamports that needs to be paid to the protocol to post a message
pub fee: u64, pub fee: u64,
/// Amount of lamports that needs to be paid to the protocol to post a message
pub fee_persistent: u64,
/// Initial Guardian Set /// Initial Guardian Set
pub initial_guardians: Vec<[u8; 20]>, pub initial_guardians: Vec<[u8; 20]>,
} }
@ -71,6 +74,7 @@ pub fn initialize(
accs.bridge.config = BridgeConfig { accs.bridge.config = BridgeConfig {
guardian_set_expiration_time: data.guardian_set_expiration_time, guardian_set_expiration_time: data.guardian_set_expiration_time,
fee: data.fee, fee: data.fee,
fee_persistent: data.fee_persistent,
}; };
// Initialize the fee collector account so it's rent exempt and will keep funds // Initialize the fee collector account so it's rent exempt and will keep funds

View File

@ -96,6 +96,13 @@ pub fn post_message(
accs.message accs.message
.verify_derivation(ctx.program_id, &msg_derivation)?; .verify_derivation(ctx.program_id, &msg_derivation)?;
let fee = {
if data.persist {
accs.bridge.config.fee_persistent
} else {
accs.bridge.config.fee
}
};
// Fee handling, checking previously known balance allows us to not care who is the payer of // Fee handling, checking previously known balance allows us to not care who is the payer of
// this submission. // this submission.
if accs if accs
@ -103,11 +110,11 @@ pub fn post_message(
.lamports() .lamports()
.checked_sub(accs.bridge.last_lamports) .checked_sub(accs.bridge.last_lamports)
.ok_or(MathOverflow)? .ok_or(MathOverflow)?
< accs.bridge.config.fee < fee
{ {
trace!( trace!(
"Expected fee not found: fee, last_lamports, collector: {} {} {}", "Expected fee not found: fee, last_lamports, collector: {} {} {}",
accs.bridge.config.fee, fee,
accs.bridge.last_lamports, accs.bridge.last_lamports,
accs.fee_collector.lamports(), accs.fee_collector.lamports(),
); );

View File

@ -29,9 +29,9 @@ use byteorder::{
}; };
use sha3::Digest; use sha3::Digest;
use solana_program::{ use solana_program::{
msg,
program_error::ProgramError, program_error::ProgramError,
pubkey::Pubkey, pubkey::Pubkey,
msg,
}; };
use solitaire::{ use solitaire::{
processors::seeded::Seeded, processors::seeded::Seeded,

View File

@ -44,6 +44,7 @@ pub fn initialize(
program_id: Pubkey, program_id: Pubkey,
payer: Pubkey, payer: Pubkey,
fee: u64, fee: u64,
fee_persistent: u64,
guardian_set_expiration_time: u32, guardian_set_expiration_time: u32,
initial_guardians: &[[u8; 20]], initial_guardians: &[[u8; 20]],
) -> solitaire::Result<Instruction> { ) -> solitaire::Result<Instruction> {
@ -67,6 +68,7 @@ pub fn initialize(
data: crate::instruction::Instruction::Initialize(InitializeData { data: crate::instruction::Instruction::Initialize(InitializeData {
initial_guardians: initial_guardians.to_vec(), initial_guardians: initial_guardians.to_vec(),
fee, fee,
fee_persistent,
guardian_set_expiration_time, guardian_set_expiration_time,
}) })
.try_to_vec()?, .try_to_vec()?,

View File

@ -74,6 +74,8 @@ pub struct BridgeConfig {
/// Amount of lamports that needs to be paid to the protocol to post a message /// Amount of lamports that needs to be paid to the protocol to post a message
pub fee: u64, pub fee: u64,
/// Amount of lamports that needs to be paid to the protocol to post a persistent message
pub fee_persistent: u64,
} }
#[derive(Default, BorshSerialize, BorshDeserialize)] #[derive(Default, BorshSerialize, BorshDeserialize)]
@ -220,34 +222,6 @@ impl Owned for ClaimData {
} }
} }
pub struct GovernancePayloadUpgrade {
// Address of the new Implementation
pub new_contract: Pubkey,
}
impl DeserializePayload for GovernancePayloadUpgrade
where
Self: DeserializeGovernancePayload,
{
fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> {
let mut c = Cursor::new(buf);
Self::check_governance_header(&mut c)?;
let mut addr = [0u8; 32];
c.read_exact(&mut addr)?;
Ok(GovernancePayloadUpgrade {
new_contract: Pubkey::new(&addr[..]),
})
}
}
impl DeserializeGovernancePayload for GovernancePayloadUpgrade {
const MODULE: &'static str = "CORE";
const ACTION: u8 = 2;
}
pub struct GovernancePayloadGuardianSetChange { pub struct GovernancePayloadGuardianSetChange {
// New GuardianSetIndex // New GuardianSetIndex
pub new_guardian_set_index: u32, pub new_guardian_set_index: u32,
@ -262,7 +236,7 @@ impl SerializePayload for GovernancePayloadGuardianSetChange {
v.write_u32::<BigEndian>(self.new_guardian_set_index)?; v.write_u32::<BigEndian>(self.new_guardian_set_index)?;
v.write_u8(self.new_guardian_set.len() as u8)?; v.write_u8(self.new_guardian_set.len() as u8)?;
for key in self.new_guardian_set.iter() { for key in self.new_guardian_set.iter() {
v.write(key); v.write(key)?;
} }
Ok(()) Ok(())
} }
@ -297,15 +271,52 @@ impl DeserializeGovernancePayload for GovernancePayloadGuardianSetChange {
const ACTION: u8 = 1; const ACTION: u8 = 1;
} }
pub struct GovernancePayloadUpgrade {
// Address of the new Implementation
pub new_contract: Pubkey,
}
impl DeserializePayload for GovernancePayloadUpgrade
where
Self: DeserializeGovernancePayload,
{
fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> {
let mut c = Cursor::new(buf);
Self::check_governance_header(&mut c)?;
let mut addr = [0u8; 32];
c.read_exact(&mut addr)?;
Ok(GovernancePayloadUpgrade {
new_contract: Pubkey::new(&addr[..]),
})
}
}
impl DeserializeGovernancePayload for GovernancePayloadUpgrade {
const MODULE: &'static str = "CORE";
const ACTION: u8 = 2;
}
pub struct GovernancePayloadSetMessageFee { pub struct GovernancePayloadSetMessageFee {
// New fee in lamports // New fee in lamports
pub fee: u64, pub fee: U256,
// New fee for persisted messages in lamports
pub persisted_fee: U256,
} }
impl SerializePayload for GovernancePayloadSetMessageFee { impl SerializePayload for GovernancePayloadSetMessageFee {
fn serialize<W: Write>(&self, v: &mut W) -> std::result::Result<(), SolitaireError> { fn serialize<W: Write>(&self, v: &mut W) -> std::result::Result<(), SolitaireError> {
use byteorder::WriteBytesExt; use byteorder::WriteBytesExt;
v.write_u64::<BigEndian>(self.fee)?; let mut fee_data = [0u8; 32];
self.fee.to_big_endian(&mut fee_data);
v.write(&fee_data[..])?;
let mut fee_persistent_data = [0u8; 32];
self.persisted_fee.to_big_endian(&mut fee_persistent_data);
v.write(&fee_persistent_data[..])?;
Ok(()) Ok(())
} }
} }
@ -317,8 +328,18 @@ where
fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> { fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> {
let mut c = Cursor::new(buf); let mut c = Cursor::new(buf);
let fee = c.read_u64::<BigEndian>()?; let mut fee_data: [u8; 32] = [0; 32];
Ok(GovernancePayloadSetMessageFee { fee }) c.read_exact(&mut fee_data)?;
let fee = U256::from_big_endian(&fee_data);
let mut fee_persisted_data: [u8; 32] = [0; 32];
c.read_exact(&mut fee_persisted_data)?;
let fee_persisted = U256::from_big_endian(&fee_persisted_data);
Ok(GovernancePayloadSetMessageFee {
fee,
persisted_fee: fee_persisted,
})
} }
} }

View File

@ -16,8 +16,8 @@ use secp256k1::{
}; };
use sha3::Digest; use sha3::Digest;
use solana_client::{ use solana_client::{
rpc_client::RpcClient,
client_error::ClientError, client_error::ClientError,
rpc_client::RpcClient,
rpc_config::RpcSendTransactionConfig, rpc_config::RpcSendTransactionConfig,
}; };
use solana_program::{ use solana_program::{
@ -42,8 +42,8 @@ use solana_sdk::{
signature::{ signature::{
read_keypair_file, read_keypair_file,
Keypair, Keypair,
Signer,
Signature, Signature,
Signer,
}, },
transaction::Transaction, transaction::Transaction,
}; };
@ -103,16 +103,15 @@ fn execute(
let mut transaction = Transaction::new_with_payer(instructions, Some(&payer.pubkey())); let mut transaction = Transaction::new_with_payer(instructions, Some(&payer.pubkey()));
let recent_blockhash = client.get_recent_blockhash().unwrap().0; let recent_blockhash = client.get_recent_blockhash().unwrap().0;
transaction.sign(&signers.to_vec(), recent_blockhash); transaction.sign(&signers.to_vec(), recent_blockhash);
client client.send_and_confirm_transaction_with_spinner_and_config(
.send_and_confirm_transaction_with_spinner_and_config( &transaction,
&transaction, CommitmentConfig::processed(),
CommitmentConfig::processed(), RpcSendTransactionConfig {
RpcSendTransactionConfig { skip_preflight: true,
skip_preflight: true, preflight_commitment: None,
preflight_commitment: None, encoding: None,
encoding: None, },
}, )
)
} }
mod helpers { mod helpers {
@ -133,7 +132,10 @@ mod helpers {
} }
/// Fetch account data, the loop is there to re-attempt until data is available. /// Fetch account data, the loop is there to re-attempt until data is available.
pub fn get_account_data<T: BorshDeserialize>(client: &RpcClient, account: &Pubkey) -> Option<T> { pub fn get_account_data<T: BorshDeserialize>(
client: &RpcClient,
account: &Pubkey,
) -> Option<T> {
for _ in 0..5 { for _ in 0..5 {
if let Ok(account) = client.get_account(account) { if let Ok(account) = client.get_account(account) {
return Some(T::try_from_slice(&account.data).unwrap()); return Some(T::try_from_slice(&account.data).unwrap());
@ -219,7 +221,12 @@ mod helpers {
(vaa, body, body_hash) (vaa, body, body_hash)
} }
pub fn transfer(client: &RpcClient, from: &Keypair, to: &Pubkey, lamports: u64) -> Result<Signature, ClientError> { pub fn transfer(
client: &RpcClient,
from: &Keypair,
to: &Pubkey,
lamports: u64,
) -> Result<Signature, ClientError> {
execute( execute(
client, client,
from, from,
@ -257,14 +264,21 @@ mod helpers {
nonce: u32, nonce: u32,
data: Vec<u8>, data: Vec<u8>,
fee: u64, fee: u64,
persist: bool,
) -> Result<Pubkey, ClientError> { ) -> Result<Pubkey, ClientError> {
// Transfer money into the fee collector as it needs a balance/must exist. // Transfer money into the fee collector as it needs a balance/must exist.
let fee_collector = FeeCollector::<'_>::key(None, program); let fee_collector = FeeCollector::<'_>::key(None, program);
// Capture the resulting message, later functions will need this. // Capture the resulting message, later functions will need this.
let (message_key, instruction) = let (message_key, instruction) = instructions::post_message(
instructions::post_message(*program, payer.pubkey(), emitter.pubkey(), nonce, data) *program,
.unwrap(); payer.pubkey(),
emitter.pubkey(),
nonce,
data,
persist,
)
.unwrap();
execute( execute(
client, client,
@ -317,7 +331,12 @@ mod helpers {
Ok(()) Ok(())
} }
pub fn post_vaa(client: &RpcClient, program: &Pubkey, payer: &Keypair, vaa: PostVAAData) -> Result<Signature, ClientError> { pub fn post_vaa(
client: &RpcClient,
program: &Pubkey,
payer: &Keypair,
vaa: PostVAAData,
) -> Result<Signature, ClientError> {
execute( execute(
client, client,
payer, payer,

View File

@ -1,7 +1,17 @@
#![allow(warnings)] #![allow(warnings)]
use borsh::BorshSerialize; use borsh::BorshSerialize;
use secp256k1::Message; use byteorder::{
BigEndian,
WriteBytesExt,
};
use hex_literal::hex;
use secp256k1::{
Message,
PublicKey,
SecretKey,
};
use sha3::Digest;
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_program::{ use solana_program::{
borsh::try_from_slice_unchecked, borsh::try_from_slice_unchecked,
@ -27,27 +37,17 @@ use solana_sdk::{
}, },
transaction::Transaction, transaction::Transaction,
}; };
use byteorder::{
BigEndian,
WriteBytesExt,
};
use std::{ use std::{
convert::TryInto, convert::TryInto,
io::{ io::{
Cursor, Cursor,
Write, Write,
}, },
time::{
Duration,
SystemTime,
},
}; };
use std::time::{
Duration,
SystemTime,
};
use hex_literal::hex;
use secp256k1::{
PublicKey,
SecretKey,
};
use sha3::Digest;
use bridge::{ use bridge::{
accounts::GuardianSetDerivationData, accounts::GuardianSetDerivationData,
@ -66,14 +66,15 @@ use bridge::{
SerializePayload, SerializePayload,
Signature, Signature,
}; };
use primitive_types::U256;
mod common; mod common;
const GOV_KEY: [u8; 64] = [ const GOV_KEY: [u8; 64] = [
240, 133, 120, 113, 30, 67, 38, 184, 197, 72, 234, 99, 241, 21, 58, 225, 41, 157, 171, 44, 240, 133, 120, 113, 30, 67, 38, 184, 197, 72, 234, 99, 241, 21, 58, 225, 41, 157, 171, 44, 196,
196, 163, 134, 236, 92, 148, 110, 68, 127, 114, 177, 0, 173, 253, 199, 9, 242, 142, 201, 163, 134, 236, 92, 148, 110, 68, 127, 114, 177, 0, 173, 253, 199, 9, 242, 142, 201, 174, 108,
174, 108, 197, 18, 102, 115, 0, 31, 205, 127, 188, 191, 56, 171, 228, 20, 247, 149, 170, 197, 18, 102, 115, 0, 31, 205, 127, 188, 191, 56, 171, 228, 20, 247, 149, 170, 141, 231, 147,
141, 231, 147, 88, 97, 199, 88, 97, 199,
]; ];
struct Context { struct Context {
@ -109,7 +110,17 @@ fn test_bridge_messages(context: &mut Context) {
let emitter = Keypair::new(); let emitter = Keypair::new();
// Post the message, publishing the data for guardian consumption. // Post the message, publishing the data for guardian consumption.
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap(); 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. // Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0); let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
@ -127,7 +138,17 @@ fn test_guardian_set_change(context: &mut Context) {
let emitter = Keypair::from_bytes(&GOV_KEY).unwrap(); let emitter = Keypair::from_bytes(&GOV_KEY).unwrap();
// Post the message, publishing the data for guardian consumption. // Post the message, publishing the data for guardian consumption.
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap(); 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. // Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0); let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
@ -141,9 +162,21 @@ fn test_guardian_set_change(context: &mut Context) {
let message = GovernancePayloadGuardianSetChange { let message = GovernancePayloadGuardianSetChange {
new_guardian_set_index: 1, new_guardian_set_index: 1,
new_guardian_set: new_public_keys.clone(), new_guardian_set: new_public_keys.clone(),
}.try_to_vec().unwrap(); }
.try_to_vec()
.unwrap();
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).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); let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
common::verify_signatures(client, program, payer, body, body_hash, &context.secret, 0).unwrap(); common::verify_signatures(client, program, payer, body, body_hash, &context.secret, 0).unwrap();
common::post_vaa(client, program, payer, vaa).unwrap(); common::post_vaa(client, program, payer, vaa).unwrap();
@ -157,11 +190,22 @@ fn test_guardian_set_change(context: &mut Context) {
0, 0,
1, 1,
1, 1,
).unwrap(); )
.unwrap();
// Submit the message a second time with a new nonce. // Submit the message a second time with a new nonce.
let nonce = 12399; let nonce = 12399;
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap(); let message_key = common::post_message(
client,
program,
payer,
&emitter,
nonce,
message.clone(),
10_000,
false,
)
.unwrap();
context.public = new_public_keys; context.public = new_public_keys;
context.secret = new_secret_keys; context.secret = new_secret_keys;
@ -183,9 +227,21 @@ fn test_guardian_set_change_fails(context: &mut Context) {
let message = GovernancePayloadGuardianSetChange { let message = GovernancePayloadGuardianSetChange {
new_guardian_set_index: 2, new_guardian_set_index: 2,
new_guardian_set: new_public_keys.clone(), new_guardian_set: new_public_keys.clone(),
}.try_to_vec().unwrap(); }
.try_to_vec()
.unwrap();
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).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); let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1);
assert!(common::upgrade_guardian_set( assert!(common::upgrade_guardian_set(
@ -197,7 +253,8 @@ fn test_guardian_set_change_fails(context: &mut Context) {
1, 1,
2, 2,
0, 0,
).is_err()); )
.is_err());
} }
fn test_set_fees(context: &mut Context) { fn test_set_fees(context: &mut Context) {
@ -206,9 +263,12 @@ fn test_set_fees(context: &mut Context) {
let emitter = Keypair::from_bytes(&GOV_KEY).unwrap(); let emitter = Keypair::from_bytes(&GOV_KEY).unwrap();
let nonce = 12401; let nonce = 12401;
let message = GovernancePayloadSetMessageFee { fee: 100 } let message = GovernancePayloadSetMessageFee {
.try_to_vec() fee: U256::from(100),
.unwrap(); persisted_fee: U256::from(100),
}
.try_to_vec()
.unwrap();
let message_key = common::post_message( let message_key = common::post_message(
client, client,
@ -218,6 +278,7 @@ fn test_set_fees(context: &mut Context) {
nonce, nonce,
message.clone(), message.clone(),
10_000, 10_000,
false,
) )
.unwrap(); .unwrap();
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1); let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1);
@ -229,13 +290,31 @@ fn test_set_fees(context: &mut Context) {
let emitter = Keypair::new(); let emitter = Keypair::new();
let nonce = 12402; let nonce = 12402;
let message = b"Fail to Pay".to_vec(); let message = b"Fail to Pay".to_vec();
assert!( assert!(common::post_message(
common::post_message(client, program, payer, &emitter, nonce, message.clone(), 50).is_err() client,
); program,
payer,
&emitter,
nonce,
message.clone(),
50,
false
)
.is_err());
// And succeeds with the new. // And succeeds with the new.
let emitter = Keypair::new(); let emitter = Keypair::new();
let nonce = 12402; let nonce = 12402;
let message = b"Fail to Pay".to_vec(); let message = b"Fail to Pay".to_vec();
common::post_message(client, program, payer, &emitter, nonce, message.clone(), 100).unwrap(); common::post_message(
client,
program,
payer,
&emitter,
nonce,
message.clone(),
100,
false,
)
.unwrap();
} }

View File

@ -44,7 +44,7 @@ spl-token mint "$token" 10000000000 "$account"
# Create the bridge contract at a known address # Create the bridge contract at a known address
# OK to fail on subsequent attempts (already created). # OK to fail on subsequent attempts (already created).
retry client create-bridge "$bridge_address" "$initial_guardian" 86400 100 retry client create-bridge "$bridge_address" "$initial_guardian" 86400 100 200
# Let k8s startup probe succeed # Let k8s startup probe succeed
nc -l -p 2000 nc -l -p 2000

View File

@ -12,7 +12,7 @@ use crate::msg::{
use crate::state::{ use crate::state::{
config, config_read, guardian_set_get, guardian_set_set, sequence_read, sequence_set, config, config_read, guardian_set_get, guardian_set_set, sequence_read, sequence_set,
vaa_archive_check, ConfigInfo, GovernancePacket, GuardianAddress, GuardianSetInfo, vaa_archive_check, ConfigInfo, GovernancePacket, GuardianAddress, GuardianSetInfo,
GuardianSetUpgrade, ParsedVAA, TransferFee, GuardianSetUpgrade, ParsedVAA, SetFee, TransferFee,
}; };
use k256::ecdsa::recoverable::Id as RecoverableId; use k256::ecdsa::recoverable::Id as RecoverableId;
@ -30,7 +30,7 @@ const CHAIN_ID: u16 = 3;
// Lock assets fee amount and denomination // Lock assets fee amount and denomination
const FEE_AMOUNT: u128 = 10000; const FEE_AMOUNT: u128 = 10000;
const FEE_DENOMINATION: &str = "uluna"; pub const FEE_DENOMINATION: &str = "uluna";
pub fn init<S: Storage, A: Api, Q: Querier>( pub fn init<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>, deps: &mut Extern<S, A, Q>,
@ -44,6 +44,7 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
guardian_set_index: 0, guardian_set_index: 0,
guardian_set_expirity: msg.guardian_set_expirity, guardian_set_expirity: msg.guardian_set_expirity,
fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
fee_persisted: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
}; };
config(&mut deps.storage).save(&state)?; config(&mut deps.storage).save(&state)?;
@ -103,9 +104,10 @@ fn handle_governance_payload<S: Storage, A: Api, Q: Querier>(
} }
match gov_packet.action { match gov_packet.action {
// 0 is reserved for upgrade / migration // 1 is reserved for upgrade / migration
1u8 => vaa_update_guardian_set(deps, env, &gov_packet.payload), 2u8 => vaa_update_guardian_set(deps, env, &gov_packet.payload),
2u8 => handle_transfer_fee(deps, env, &gov_packet.payload), 3u8 => handle_set_fee(deps, env, &gov_packet.payload),
4u8 => handle_transfer_fee(deps, env, &gov_packet.payload),
_ => ContractError::InvalidVAAAction.std_err(), _ => ContractError::InvalidVAAAction.std_err(),
} }
} }
@ -230,6 +232,32 @@ fn vaa_update_guardian_set<S: Storage, A: Api, Q: Querier>(
}) })
} }
pub fn handle_set_fee<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
data: &Vec<u8>,
) -> StdResult<HandleResponse> {
let set_fee_msg = SetFee::deserialize(&data)?;
// Save new fees
let mut state = config_read(&mut deps.storage).load()?;
state.fee = set_fee_msg.fee;
state.fee_persisted = set_fee_msg.fee_persistent;
config(&mut deps.storage).save(&state)?;
Ok(HandleResponse {
messages: vec![],
log: vec![
log("action", "fee_change"),
log("new_fee.amount", state.fee.amount),
log("new_fee.denom", state.fee.denom),
log("new_persisted_fee.amount", state.fee_persisted.amount),
log("new_persisted_fee.denom", state.fee_persisted.denom),
],
data: None,
})
}
pub fn handle_transfer_fee<S: Storage, A: Api, Q: Querier>( pub fn handle_transfer_fee<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>, deps: &mut Extern<S, A, Q>,
env: Env, env: Env,
@ -256,9 +284,16 @@ fn handle_post_message<S: Storage, A: Api, Q: Querier>(
persist: bool, persist: bool,
) -> StdResult<HandleResponse> { ) -> StdResult<HandleResponse> {
let state = config_read(&deps.storage).load()?; let state = config_read(&deps.storage).load()?;
let fee = {
if persist {
state.fee
} else {
state.fee_persisted
}
};
// Check fee // Check fee
if !has_coins(env.message.sent_funds.as_ref(), &state.fee) { if !has_coins(env.message.sent_funds.as_ref(), &fee) {
return ContractError::FeeTooLow.std_err(); return ContractError::FeeTooLow.std_err();
} }

View File

@ -1,4 +1,4 @@
use schemars::JsonSchema; use schemars::{JsonSchema, Set};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use cosmwasm_std::{Binary, CanonicalAddr, Coin, HumanAddr, StdResult, Storage, Uint128}; use cosmwasm_std::{Binary, CanonicalAddr, Coin, HumanAddr, StdResult, Storage, Uint128};
@ -31,8 +31,10 @@ pub struct ConfigInfo {
pub gov_chain: u16, pub gov_chain: u16,
pub gov_address: Vec<u8>, pub gov_address: Vec<u8>,
// Asset locking fee // Message sending fee
pub fee: Coin, pub fee: Coin,
// Persisted message sending fee
pub fee_persisted: Coin,
} }
// Validator Action Approval(VAA) data // Validator Action Approval(VAA) data
@ -144,6 +146,7 @@ pub struct GuardianAddress {
pub bytes: Binary, // 20-byte addresses pub bytes: Binary, // 20-byte addresses
} }
use crate::contract::FEE_DENOMINATION;
#[cfg(test)] #[cfg(test)]
use hex; use hex;
@ -263,7 +266,7 @@ impl GovernancePacket {
} }
} }
// action 1 // action 2
pub struct GuardianSetUpgrade { pub struct GuardianSetUpgrade {
pub new_guardian_set_index: u32, pub new_guardian_set_index: u32,
pub new_guardian_set: GuardianSetInfo, pub new_guardian_set: GuardianSetInfo,
@ -303,7 +306,34 @@ impl GuardianSetUpgrade {
} }
} }
// action 2 // action 3
pub struct SetFee {
pub fee: Coin,
pub fee_persistent: Coin,
}
impl SetFee {
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
let data = data.as_slice();
let (_, amount) = data.get_u256(0);
let (_, amount_persistent) = data.get_u256(32);
let fee = Coin {
denom: String::from(FEE_DENOMINATION),
amount: Uint128(amount),
};
let fee_persistent = Coin {
denom: String::from(FEE_DENOMINATION),
amount: Uint128(amount_persistent),
};
Ok(SetFee {
fee,
fee_persistent,
})
}
}
// action 4
pub struct TransferFee { pub struct TransferFee {
pub amount: Coin, pub amount: Coin,
pub recipient: CanonicalAddr, pub recipient: CanonicalAddr,
@ -314,12 +344,11 @@ impl TransferFee {
let data = data.as_slice(); let data = data.as_slice();
let recipient = data.get_address(0); let recipient = data.get_address(0);
let amount = Uint128(data.get_u128_be(32)); let (_, amount) = data.get_u256(32);
let denom = match String::from_utf8(data[48..].to_vec()) { let amount = Coin {
Ok(s) => s, denom: String::from(FEE_DENOMINATION),
Err(_) => return ContractError::InvalidVAA.std_err(), amount: Uint128(amount),
}; };
let amount = Coin { denom, amount };
Ok(TransferFee { amount, recipient }) Ok(TransferFee { amount, recipient })
} }
} }