implement withdraw-reward

This commit is contained in:
Pascal
2020-07-24 17:23:12 -07:00
parent fffa3b8847
commit 07c0223fab
3 changed files with 26 additions and 28 deletions

View File

@@ -2676,7 +2676,6 @@ impl StacksChainState {
let miner_participant_principal = ClarityName::try_from(BOOT_CODE_MINER_REWARDS_PARTICIPANT.to_string()).unwrap();
let miner_available_name = ClarityName::try_from(BOOT_CODE_MINER_REWARDS_AVAILABLE.to_string()).unwrap();
let miner_authorized_name = ClarityName::try_from(BOOT_CODE_MINER_REWARDS_AUTHORIZED.to_string()).unwrap();
let miner_principal = Value::Tuple(TupleData::from_data(vec![
(miner_participant_principal, Value::Principal(PrincipalData::Standard(StandardPrincipalData::from(miner_reward.address.clone()))))])
@@ -2685,7 +2684,7 @@ impl StacksChainState {
let miner_reward_total = miner_reward.total();
clarity_tx.connection().as_transaction(|x| { x.with_clarity_db(|ref mut db| {
// (+ reward (get available (default-to {available: 0, authorized: false} (map-get rewards ((miner))))))
// (+ reward (get available (default-to {available: 0} (map-get rewards ((miner))))))
let miner_status_opt = db.fetch_entry(&miner_contract_id, BOOT_CODE_MINER_REWARDS_MAP, &miner_principal)?;
let new_miner_status =
match miner_status_opt {
@@ -2695,7 +2694,6 @@ impl StacksChainState {
// this miner doesn't have an entry in the contract yet
Value::Tuple(TupleData::from_data(vec![
(miner_available_name, Value::UInt(miner_reward_total)),
(miner_authorized_name, Value::Bool(false)),
]).expect("FATAL: failed to construct miner reward tuple"))
},
Some(ref miner_status) => {
@@ -2730,6 +2728,14 @@ impl StacksChainState {
debug!("Grant miner {} {} STX. Updated to: {}", miner_reward.address.to_string(), miner_reward_total,
&new_miner_status);
db.set_entry(&miner_contract_id, BOOT_CODE_MINER_REWARDS_MAP, miner_principal, new_miner_status)?;
// credit contract so it can disburse when miner withdraws
let miner_contract_principal = PrincipalData::Contract(QualifiedContractIdentifier::new(StandardPrincipalData::from(boot_code_address.clone()), ContractName::from(BOOT_CODE_MINER_CONTRACT_NAME)));
let cur_balance = db.get_account_stx_balance(&miner_contract_principal);
let final_balance = cur_balance.checked_add(miner_reward_total).expect("FATAL: STX reward overflow");
debug!("Balance available in {} {} STX", BOOT_CODE_MINER_CONTRACT_NAME, final_balance);
db.set_account_stx_balance(&miner_contract_principal, final_balance);
Ok(())
})}).map_err(Error::ClarityError)?;
Ok(())

View File

@@ -464,26 +464,26 @@ const STACKS_MINER_AUTH_KEY : &'static str = "a5879925788dcb3fe1f2737453e371ba04
const STACKS_BOOT_CODE : &'static [&'static str] = &[
r#"
(define-constant ERR-NO-PRINCIPAL 1)
(define-constant ERR-NOT-AUTHORIZED 2)
(define-constant AUTHORIZER 'ST3REJ5WQ42JGJZ6W77CX79JYMCVTKD73D6R6Z4R3) ;; addr of STACKS_MINER_AUTH_KEY
(define-constant ERR-NO-BALANCE u5)
(define-map rewards
((participant principal))
((available uint) (authorized bool))
((available uint))
)
(define-private (get-participant-info (participant principal))
(default-to {available: u0, authorized: false} (map-get? rewards {participant: participant})))
(default-to {available: u0} (map-get? rewards {participant: participant})))
(define-public (get-participant-reward (participant principal))
(define-read-only (get-participant-reward (participant principal))
(ok (get available (get-participant-info participant))))
(define-public (is-participant-authorized? (participant principal))
(ok (get authorized (get-participant-info participant))))
;; TODO: authorize STX withdrawals
;; TODO: withdraw STX
(define-public (withdraw-reward)
(let ((caller-address tx-sender) (contract-address (as-contract tx-sender)) (available (get available (get-participant-info tx-sender))))
(if (> available u0)
(match
(as-contract (stx-transfer? available contract-address caller-address))
value (begin (map-set rewards {participant: caller-address} {available: u0}) (ok available))
err-value (err err-value))
(err ERR-NO-BALANCE))))
"#
];
@@ -497,7 +497,6 @@ pub const BOOT_CODE_MINER_CONTRACT_NAME : &'static str = "miner-rewards";
pub const BOOT_CODE_MINER_REWARDS_MAP : &'static str = "rewards";
pub const BOOT_CODE_MINER_REWARDS_PARTICIPANT : &'static str = "participant";
pub const BOOT_CODE_MINER_REWARDS_AVAILABLE : &'static str = "available";
pub const BOOT_CODE_MINER_REWARDS_AUTHORIZED : &'static str = "authorized";
#[cfg(test)]
pub const MINER_REWARD_MATURITY : u64 = 2; // small for testing purposes

View File

@@ -1167,13 +1167,12 @@ pub mod test {
return None;
}
pub fn get_miner_status<'a>(clarity_tx: &mut ClarityTx<'a>, addr: &StacksAddress) -> Option<(bool, u128)> {
pub fn get_miner_status<'a>(clarity_tx: &mut ClarityTx<'a>, addr: &StacksAddress) -> Option<u128> {
let boot_code_address = StacksAddress::from_string(&STACKS_BOOT_CODE_CONTRACT_ADDRESS.to_string()).unwrap();
let miner_contract_id = QualifiedContractIdentifier::new(StandardPrincipalData::from(boot_code_address.clone()), ContractName::try_from(BOOT_CODE_MINER_CONTRACT_NAME.to_string()).unwrap());
let miner_participant_principal = ClarityName::try_from(BOOT_CODE_MINER_REWARDS_PARTICIPANT.to_string()).unwrap();
let miner_available_name = ClarityName::try_from(BOOT_CODE_MINER_REWARDS_AVAILABLE.to_string()).unwrap();
let miner_authorized_name = ClarityName::try_from(BOOT_CODE_MINER_REWARDS_AUTHORIZED.to_string()).unwrap();
let miner_principal = Value::Tuple(TupleData::from_data(vec![
(miner_participant_principal, Value::Principal(PrincipalData::Standard(StandardPrincipalData::from(addr.clone()))))])
@@ -1189,13 +1188,6 @@ pub mod test {
Some(ref miner_status) => {
match **miner_status {
Value::Tuple(ref tuple) => {
let authorized = match tuple.get(&miner_authorized_name).expect("FATAL: no miner authorized in tuple") {
Value::Bool(ref authorized) => *authorized,
_ => {
panic!("FATAL: miner reward data map is malformed");
}
};
let available = match tuple.get(&miner_available_name).expect("FATAL: no miner available in tuple") {
Value::UInt(ref available) => *available,
_ => {
@@ -1203,7 +1195,7 @@ pub mod test {
}
};
Some((authorized, available))
Some(available)
},
ref x => {
panic!("FATAL: miner status is not a tuple: {:?}", &x);
@@ -1384,8 +1376,8 @@ pub mod test {
test_debug!("Miner {} '{}' has no mature funds in this fork", miner.id, miner.origin_address().unwrap().to_string());
return total == 0;
}
Some((authorized, amount)) => {
test_debug!("Miner {} '{}' is authorized: {}, with amount: {} in this fork", miner.id, miner.origin_address().unwrap().to_string(), authorized, amount);
Some(amount) => {
test_debug!("Miner {} '{}' with amount: {} in this fork", miner.id, miner.origin_address().unwrap().to_string(), amount);
if amount != total {
test_debug!("Amount {} != {}", amount, total);
return false;
@@ -4414,6 +4406,7 @@ pub mod test {
// assert_eq!(stacks_block.txs.len(), 1);
}
}
// TODO: invalid block with duplicate microblock public key hash (okay between forks, but not
// within the same fork)
// TODO: (BLOCKED) build off of different points in the same microblock stream