mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-22 23:18:39 +08:00
implement withdraw-reward
This commit is contained in:
@@ -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(())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user