Skip to content

Commit

Permalink
[upgrade] donor voice migration (#88)
Browse files Browse the repository at this point in the history
Co-authored-by: 0o-de-lally <[email protected]>
Co-authored-by: sirouk <[email protected]>
  • Loading branch information
3 people authored Nov 8, 2023
1 parent 3fbb620 commit 3a9aac0
Show file tree
Hide file tree
Showing 25 changed files with 746 additions and 355 deletions.
115 changes: 55 additions & 60 deletions framework/cached-packages/src/libra_framework_sdk_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,33 +219,33 @@ pub enum EntryFunctionCall {
should_pass: bool,
},

DonorDirectedMakeDonorDirectedTx {
DonorVoiceMakeDonorVoiceTx {
init_signers: Vec<AccountAddress>,
cfg_n_signers: u64,
},

DonorDirectedProposeLiquidateTx {
DonorVoiceProposeLiquidateTx {
multisig_address: AccountAddress,
},

DonorDirectedProposePaymentTx {
DonorVoiceProposePaymentTx {
multisig_address: AccountAddress,
payee: AccountAddress,
value: u64,
description: Vec<u8>,
},

DonorDirectedProposeVetoTx {
DonorVoiceProposeVetoTx {
multisig_address: AccountAddress,
id: u64,
},

DonorDirectedVoteLiquidationTx {
DonorVoiceVoteLiquidationTx {
multisig_address: AccountAddress,
},

/// Entry functiont to vote the veto.
DonorDirectedVoteVetoTx {
DonorVoiceVoteVetoTx {
multisig_address: AccountAddress,
id: u64,
},
Expand Down Expand Up @@ -680,30 +680,30 @@ impl EntryFunctionCall {
proposal_id,
should_pass,
} => diem_governance_vote(proposal_id, should_pass),
DonorDirectedMakeDonorDirectedTx {
DonorVoiceMakeDonorVoiceTx {
init_signers,
cfg_n_signers,
} => donor_directed_make_donor_directed_tx(init_signers, cfg_n_signers),
DonorDirectedProposeLiquidateTx { multisig_address } => {
donor_directed_propose_liquidate_tx(multisig_address)
} => donor_voice_make_donor_voice_tx(init_signers, cfg_n_signers),
DonorVoiceProposeLiquidateTx { multisig_address } => {
donor_voice_propose_liquidate_tx(multisig_address)
}
DonorDirectedProposePaymentTx {
DonorVoiceProposePaymentTx {
multisig_address,
payee,
value,
description,
} => donor_directed_propose_payment_tx(multisig_address, payee, value, description),
DonorDirectedProposeVetoTx {
} => donor_voice_propose_payment_tx(multisig_address, payee, value, description),
DonorVoiceProposeVetoTx {
multisig_address,
id,
} => donor_directed_propose_veto_tx(multisig_address, id),
DonorDirectedVoteLiquidationTx { multisig_address } => {
donor_directed_vote_liquidation_tx(multisig_address)
} => donor_voice_propose_veto_tx(multisig_address, id),
DonorVoiceVoteLiquidationTx { multisig_address } => {
donor_voice_vote_liquidation_tx(multisig_address)
}
DonorDirectedVoteVetoTx {
DonorVoiceVoteVetoTx {
multisig_address,
id,
} => donor_directed_vote_veto_tx(multisig_address, id),
} => donor_voice_vote_veto_tx(multisig_address, id),
JailUnjailByVoucher { addr } => jail_unjail_by_voucher(addr),
GasCoinClaimMintCapability {} => gas_coin_claim_mint_capability(),
GasCoinDelegateMintCapability { to } => gas_coin_delegate_mint_capability(to),
Expand Down Expand Up @@ -1380,7 +1380,7 @@ pub fn diem_governance_vote(proposal_id: u64, should_pass: bool) -> TransactionP
))
}

pub fn donor_directed_make_donor_directed_tx(
pub fn donor_voice_make_donor_voice_tx(
init_signers: Vec<AccountAddress>,
cfg_n_signers: u64,
) -> TransactionPayload {
Expand All @@ -1390,9 +1390,9 @@ pub fn donor_directed_make_donor_directed_tx(
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1,
]),
ident_str!("donor_directed").to_owned(),
ident_str!("donor_voice").to_owned(),
),
ident_str!("make_donor_directed_tx").to_owned(),
ident_str!("make_donor_voice_tx").to_owned(),
vec![],
vec![
bcs::to_bytes(&init_signers).unwrap(),
Expand All @@ -1401,22 +1401,22 @@ pub fn donor_directed_make_donor_directed_tx(
))
}

pub fn donor_directed_propose_liquidate_tx(multisig_address: AccountAddress) -> TransactionPayload {
pub fn donor_voice_propose_liquidate_tx(multisig_address: AccountAddress) -> TransactionPayload {
TransactionPayload::EntryFunction(EntryFunction::new(
ModuleId::new(
AccountAddress::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1,
]),
ident_str!("donor_directed").to_owned(),
ident_str!("donor_voice").to_owned(),
),
ident_str!("propose_liquidate_tx").to_owned(),
vec![],
vec![bcs::to_bytes(&multisig_address).unwrap()],
))
}

pub fn donor_directed_propose_payment_tx(
pub fn donor_voice_propose_payment_tx(
multisig_address: AccountAddress,
payee: AccountAddress,
value: u64,
Expand All @@ -1428,7 +1428,7 @@ pub fn donor_directed_propose_payment_tx(
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1,
]),
ident_str!("donor_directed").to_owned(),
ident_str!("donor_voice").to_owned(),
),
ident_str!("propose_payment_tx").to_owned(),
vec![],
Expand All @@ -1441,7 +1441,7 @@ pub fn donor_directed_propose_payment_tx(
))
}

pub fn donor_directed_propose_veto_tx(
pub fn donor_voice_propose_veto_tx(
multisig_address: AccountAddress,
id: u64,
) -> TransactionPayload {
Expand All @@ -1451,7 +1451,7 @@ pub fn donor_directed_propose_veto_tx(
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1,
]),
ident_str!("donor_directed").to_owned(),
ident_str!("donor_voice").to_owned(),
),
ident_str!("propose_veto_tx").to_owned(),
vec![],
Expand All @@ -1462,14 +1462,14 @@ pub fn donor_directed_propose_veto_tx(
))
}

pub fn donor_directed_vote_liquidation_tx(multisig_address: AccountAddress) -> TransactionPayload {
pub fn donor_voice_vote_liquidation_tx(multisig_address: AccountAddress) -> TransactionPayload {
TransactionPayload::EntryFunction(EntryFunction::new(
ModuleId::new(
AccountAddress::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1,
]),
ident_str!("donor_directed").to_owned(),
ident_str!("donor_voice").to_owned(),
),
ident_str!("vote_liquidation_tx").to_owned(),
vec![],
Expand All @@ -1478,17 +1478,14 @@ pub fn donor_directed_vote_liquidation_tx(multisig_address: AccountAddress) -> T
}

/// Entry functiont to vote the veto.
pub fn donor_directed_vote_veto_tx(
multisig_address: AccountAddress,
id: u64,
) -> TransactionPayload {
pub fn donor_voice_vote_veto_tx(multisig_address: AccountAddress, id: u64) -> TransactionPayload {
TransactionPayload::EntryFunction(EntryFunction::new(
ModuleId::new(
AccountAddress::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1,
]),
ident_str!("donor_directed").to_owned(),
ident_str!("donor_voice").to_owned(),
),
ident_str!("vote_veto_tx").to_owned(),
vec![],
Expand Down Expand Up @@ -2642,11 +2639,11 @@ mod decoder {
}
}

pub fn donor_directed_make_donor_directed_tx(
pub fn donor_voice_make_donor_voice_tx(
payload: &TransactionPayload,
) -> Option<EntryFunctionCall> {
if let TransactionPayload::EntryFunction(script) = payload {
Some(EntryFunctionCall::DonorDirectedMakeDonorDirectedTx {
Some(EntryFunctionCall::DonorVoiceMakeDonorVoiceTx {
init_signers: bcs::from_bytes(script.args().get(0)?).ok()?,
cfg_n_signers: bcs::from_bytes(script.args().get(1)?).ok()?,
})
Expand All @@ -2655,23 +2652,23 @@ mod decoder {
}
}

pub fn donor_directed_propose_liquidate_tx(
pub fn donor_voice_propose_liquidate_tx(
payload: &TransactionPayload,
) -> Option<EntryFunctionCall> {
if let TransactionPayload::EntryFunction(script) = payload {
Some(EntryFunctionCall::DonorDirectedProposeLiquidateTx {
Some(EntryFunctionCall::DonorVoiceProposeLiquidateTx {
multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?,
})
} else {
None
}
}

pub fn donor_directed_propose_payment_tx(
pub fn donor_voice_propose_payment_tx(
payload: &TransactionPayload,
) -> Option<EntryFunctionCall> {
if let TransactionPayload::EntryFunction(script) = payload {
Some(EntryFunctionCall::DonorDirectedProposePaymentTx {
Some(EntryFunctionCall::DonorVoiceProposePaymentTx {
multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?,
payee: bcs::from_bytes(script.args().get(1)?).ok()?,
value: bcs::from_bytes(script.args().get(2)?).ok()?,
Expand All @@ -2682,11 +2679,9 @@ mod decoder {
}
}

pub fn donor_directed_propose_veto_tx(
payload: &TransactionPayload,
) -> Option<EntryFunctionCall> {
pub fn donor_voice_propose_veto_tx(payload: &TransactionPayload) -> Option<EntryFunctionCall> {
if let TransactionPayload::EntryFunction(script) = payload {
Some(EntryFunctionCall::DonorDirectedProposeVetoTx {
Some(EntryFunctionCall::DonorVoiceProposeVetoTx {
multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?,
id: bcs::from_bytes(script.args().get(1)?).ok()?,
})
Expand All @@ -2695,21 +2690,21 @@ mod decoder {
}
}

pub fn donor_directed_vote_liquidation_tx(
pub fn donor_voice_vote_liquidation_tx(
payload: &TransactionPayload,
) -> Option<EntryFunctionCall> {
if let TransactionPayload::EntryFunction(script) = payload {
Some(EntryFunctionCall::DonorDirectedVoteLiquidationTx {
Some(EntryFunctionCall::DonorVoiceVoteLiquidationTx {
multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?,
})
} else {
None
}
}

pub fn donor_directed_vote_veto_tx(payload: &TransactionPayload) -> Option<EntryFunctionCall> {
pub fn donor_voice_vote_veto_tx(payload: &TransactionPayload) -> Option<EntryFunctionCall> {
if let TransactionPayload::EntryFunction(script) = payload {
Some(EntryFunctionCall::DonorDirectedVoteVetoTx {
Some(EntryFunctionCall::DonorVoiceVoteVetoTx {
multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?,
id: bcs::from_bytes(script.args().get(1)?).ok()?,
})
Expand Down Expand Up @@ -3312,28 +3307,28 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy<EntryFunctionDecoderMa
Box::new(decoder::diem_governance_vote),
);
map.insert(
"donor_directed_make_donor_directed_tx".to_string(),
Box::new(decoder::donor_directed_make_donor_directed_tx),
"donor_voice_make_donor_voice_tx".to_string(),
Box::new(decoder::donor_voice_make_donor_voice_tx),
);
map.insert(
"donor_directed_propose_liquidate_tx".to_string(),
Box::new(decoder::donor_directed_propose_liquidate_tx),
"donor_voice_propose_liquidate_tx".to_string(),
Box::new(decoder::donor_voice_propose_liquidate_tx),
);
map.insert(
"donor_directed_propose_payment_tx".to_string(),
Box::new(decoder::donor_directed_propose_payment_tx),
"donor_voice_propose_payment_tx".to_string(),
Box::new(decoder::donor_voice_propose_payment_tx),
);
map.insert(
"donor_directed_propose_veto_tx".to_string(),
Box::new(decoder::donor_directed_propose_veto_tx),
"donor_voice_propose_veto_tx".to_string(),
Box::new(decoder::donor_voice_propose_veto_tx),
);
map.insert(
"donor_directed_vote_liquidation_tx".to_string(),
Box::new(decoder::donor_directed_vote_liquidation_tx),
"donor_voice_vote_liquidation_tx".to_string(),
Box::new(decoder::donor_voice_vote_liquidation_tx),
);
map.insert(
"donor_directed_vote_veto_tx".to_string(),
Box::new(decoder::donor_directed_vote_veto_tx),
"donor_voice_vote_veto_tx".to_string(),
Box::new(decoder::donor_voice_vote_veto_tx),
);
map.insert(
"jail_unjail_by_voucher".to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module diem_framework::genesis {
use ol_framework::infra_escrow;
use ol_framework::tower_state;
use ol_framework::safe;
use ol_framework::donor_directed;
use ol_framework::donor_voice;
use ol_framework::epoch_helper;
use ol_framework::burn;
use ol_framework::fee_maker;
Expand Down Expand Up @@ -145,7 +145,7 @@ module diem_framework::genesis {
slow_wallet::initialize(&diem_framework_account);
tower_state::initialize(&diem_framework_account);
safe::initialize(&diem_framework_account);
donor_directed::initialize(&diem_framework_account);
donor_voice::initialize(&diem_framework_account);
epoch_helper::initialize(&diem_framework_account);
epoch_boundary::initialize(&diem_framework_account);
burn::initialize(&diem_framework_account);
Expand Down
2 changes: 1 addition & 1 deletion framework/libra-framework/sources/ol_sources/burn.move
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/// It doesn't require any permission to be a recipient of matching funds, it's open to all (including attackers). However, this mechanism uses a market to establish which of the opt-in accounts will receive funds. And this is a simple weighting algorithm which more heavily weights toward recent donations. Evidently there are attacks possible,

// It will be costly and unpredictable (but not impossible) for attacker to close the loop, on Matching funds to themselves. It's evident that an attacker can get return on investment by re-weighting the Match Index to favor a recipient controlled by them. This is especially the case if there is low participation in the market (i.e. very few people are donating out of band).
// Mitigations: There are conditions to qualify for matching. Matching accounts must be Community Wallets, which mostly means they are a Donor Directed with multi-sig enabled. See more under donor_directed.move, but in short there is a multisig to authorize transactions, and donors to that account can vote to delay, freeze, and ultimately liquidate the donor directed account. Since the attacker can clearly own all the multisig authorities on the Donor Directed account, there is another condition which is checked for: the multisig signers cannot be related by Ancestry, meaning the attacker needs to collect conspirators from across the network graph. Lastly, any system burns (e.g. proof-of-fee) from other honest users to that account gives them governance and possibly legal claims against that wallet, there is afterall an off-chain world.
// Mitigations: There are conditions to qualify for matching. Matching accounts must be Community Wallets, which mostly means they are a Donor Directed with multi-sig enabled. See more under donor_voice.move, but in short there is a multisig to authorize transactions, and donors to that account can vote to delay, freeze, and ultimately liquidate the donor directed account. Since the attacker can clearly own all the multisig authorities on the Donor Directed account, there is another condition which is checked for: the multisig signers cannot be related by Ancestry, meaning the attacker needs to collect conspirators from across the network graph. Lastly, any system burns (e.g. proof-of-fee) from other honest users to that account gives them governance and possibly legal claims against that wallet, there is afterall an off-chain world.
// The expectation is that there is no additional overhead to honest actors, but considerable uncertainty and cost to abusers. Probabilistically there will be will some attacks or anti-social behavior. However, not all is lost: abusive behavior requires playing many other games honestly. The expectation of this experiment is that abuse is de-minimis compared to the total coin supply. As the saying goes: You can only prevent all theft if you also prevent all sales.

module ol_framework::burn {
Expand Down
Loading

0 comments on commit 3a9aac0

Please sign in to comment.