Skip to content

Commit 4c65699

Browse files
foriequal0mergify[bot]
authored andcommitted
Add stake to solo
1 parent 0cf3bf6 commit 4c65699

File tree

11 files changed

+814
-21
lines changed

11 files changed

+814
-21
lines changed

core/res/solo.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
"engine": {
44
"solo": {
55
"params": {
6-
"hit": {}
6+
"hit": {},
7+
"genesisStakes": {
8+
"tccq9h7vnl68frvqapzv3tujrxtxtwqdnxw6yamrrgd": 70000,
9+
"tccq9qvruafmf9vegjhkl0ruunkwp0d4lc8fgxknzh5": 20000,
10+
"tccq8snvxt5vfwthja7z7880dgs63x4njw2n5e5zm4h": 10000
11+
}
712
}
813
}
914
},

core/src/codechain_machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl CodeChainMachine {
208208
Ok(())
209209
}
210210

211-
fn min_cost(&self, action: &Action) -> u64 {
211+
pub fn min_cost(&self, action: &Action) -> u64 {
212212
match action {
213213
Action::MintAsset {
214214
..

core/src/consensus/solo/mod.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use cstate::{ActionHandler, HitHandler};
2222
use ctypes::machine::WithBalances;
2323

2424
use self::params::SoloParams;
25+
use super::stake;
26+
use super::validator_set;
2527
use super::{ConsensusEngine, Seal};
2628
use crate::block::{ExecutedBlock, IsBlock};
2729
use crate::codechain_machine::CodeChainMachine;
@@ -43,6 +45,10 @@ impl<M> Solo<M> {
4345
if params.enable_hit_handler {
4446
action_handlers.push(Arc::new(HitHandler::new()));
4547
}
48+
action_handlers.push(Arc::new(stake::Stake::new(
49+
params.genesis_stakes.clone(),
50+
Arc::new(validator_set::null_validator::NullValidator {}),
51+
)));
4652

4753
Solo {
4854
params,
@@ -79,9 +85,23 @@ impl ConsensusEngine<CodeChainMachine> for Solo<CodeChainMachine> {
7985

8086
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
8187
let author = *block.header().author();
82-
let total_reward = self.block_reward(block.header().number())
83-
+ self.block_fee(Box::new(block.transactions().to_owned().into_iter().map(Into::into)));
84-
self.machine.add_balance(block, &author, total_reward)
88+
let (total_reward, min_fee) = {
89+
let transactions = block.transactions();
90+
let block_reward = self.block_reward(block.header().number());
91+
let total_fee: u64 = transactions.iter().map(|tx| tx.fee).sum();
92+
let min_fee: u64 = transactions.iter().map(|tx| self.machine().min_cost(&tx.action)).sum();
93+
(block_reward + total_fee, min_fee)
94+
};
95+
96+
assert!(total_reward >= min_fee, "{} >= {}", total_reward, min_fee);
97+
let stakes = stake::get_stakes(block.state()).expect("Cannot get Stake status");
98+
for (address, share) in stake::fee_distribute(&author, min_fee, &stakes) {
99+
self.machine.add_balance(block, &address, share)?
100+
}
101+
if total_reward != min_fee {
102+
self.machine.add_balance(block, &author, total_reward - min_fee)?
103+
}
104+
Ok(())
85105
}
86106

87107
fn block_reward(&self, _block_number: u64) -> u64 {

core/src/consensus/solo/params.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,32 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

17+
use std::collections::HashMap;
18+
1719
use cjson;
20+
use ckey::{Address, PlatformAddress};
1821

1922
/// Params for a null engine.
2023
#[derive(Clone, Default)]
2124
pub struct SoloParams {
2225
/// base reward for a block.
2326
pub block_reward: u64,
2427
pub enable_hit_handler: bool,
28+
pub genesis_stakes: HashMap<Address, u64>,
2529
}
2630

2731
impl From<cjson::scheme::SoloParams> for SoloParams {
2832
fn from(p: cjson::scheme::SoloParams) -> Self {
2933
SoloParams {
3034
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
3135
enable_hit_handler: p.action_handlers.hit.is_some(),
36+
genesis_stakes: p
37+
.action_handlers
38+
.genesis_stakes
39+
.unwrap_or_default()
40+
.into_iter()
41+
.map(|(pa, amount)| (PlatformAddress::into_address(pa), amount))
42+
.collect(),
3243
}
3344
}
3445
}

core/src/consensus/validator_set/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use crate::codechain_machine::CodeChainMachine;
2727
use crate::error::Error;
2828
use crate::header::Header;
2929

30+
pub mod null_validator;
3031
pub mod validator_list;
3132

3233
/// Creates a validator set from validator public keys.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2019. Kodebox, Inc.
2+
// This file is part of CodeChain.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
18+
use ckey::{Address, Public};
19+
use primitives::H256;
20+
21+
use super::super::EpochChange;
22+
use super::validator_list::ValidatorList;
23+
use super::ValidatorSet;
24+
use crate::codechain_machine::CodeChainMachine;
25+
use crate::error::Error;
26+
use crate::header::Header;
27+
28+
/// Validator set containing a known set of public keys.
29+
#[derive(Clone, Debug, PartialEq, Eq, Default)]
30+
pub struct NullValidator {}
31+
32+
impl ValidatorSet for NullValidator {
33+
fn contains(&self, _bh: &H256, _public: &Public) -> bool {
34+
true
35+
}
36+
37+
fn contains_address(&self, _bh: &H256, _address: &Address) -> bool {
38+
true
39+
}
40+
41+
fn get(&self, _parent: &H256, _nonce: usize) -> Public {
42+
unimplemented!()
43+
}
44+
45+
fn get_address(&self, _parent: &H256, _nonce: usize) -> Address {
46+
unimplemented!()
47+
}
48+
49+
fn get_index(&self, _parent: &H256, _public: &Public) -> Option<usize> {
50+
unimplemented!()
51+
}
52+
53+
fn get_index_by_address(&self, _parent: &H256, _address: &Address) -> Option<usize> {
54+
unimplemented!()
55+
}
56+
57+
fn count(&self, _parent: &H256) -> usize {
58+
unimplemented!()
59+
}
60+
61+
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
62+
unimplemented!()
63+
}
64+
65+
fn signals_epoch_end(&self, _first: bool, _header: &Header) -> EpochChange {
66+
unimplemented!()
67+
}
68+
69+
fn epoch_set(
70+
&self,
71+
_first: bool,
72+
_machine: &CodeChainMachine,
73+
_number: u64,
74+
_proof: &[u8],
75+
) -> Result<(ValidatorList, Option<H256>), Error> {
76+
unimplemented!()
77+
}
78+
}

json/src/scheme/solo.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
use std::collections::HashMap;
1818

19+
use ckey::PlatformAddress;
20+
1921
use crate::uint::Uint;
2022

2123
/// Solo params deserialization.
@@ -32,6 +34,7 @@ pub struct SoloParams {
3234
#[serde(rename_all = "camelCase")]
3335
pub struct SoloActionHandlersParams {
3436
pub hit: Option<HashMap<(), ()>>,
37+
pub genesis_stakes: Option<HashMap<PlatformAddress, u64>>,
3538
}
3639

3740
/// Solo engine deserialization.
@@ -55,12 +58,14 @@ mod tests {
5558
let s = r#"{
5659
"params": {
5760
"blockReward": "0x0d",
58-
"hit": {}
61+
"hit": {},
62+
"genesisStakes": {}
5963
}
6064
}"#;
6165

6266
let deserialized: Solo = serde_json::from_str(s).unwrap();
6367
assert_eq!(deserialized.params.block_reward, Some(Uint(U256::from(0x0d))));
6468
assert_eq!(deserialized.params.action_handlers.hit, Some(HashMap::new()));
69+
assert_eq!(deserialized.params.action_handlers.genesis_stakes, Some(HashMap::new()));
6570
}
6671
}

test/src/e2e/reward.test.ts

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,21 @@
1515
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

1717
import { expect } from "chai";
18-
import { U64 } from "codechain-sdk/lib/core/classes";
18+
import { PlatformAddress, U64 } from "codechain-sdk/lib/core/classes";
1919
import "mocha";
20-
import { aliceAddress, aliceSecret, faucetAddress } from "../helper/constants";
20+
import {
21+
aliceAddress,
22+
aliceSecret,
23+
bobAddress,
24+
faucetAddress
25+
} from "../helper/constants";
2126
import CodeChain from "../helper/spawn";
2227

2328
describe("Reward = 50, 1 miner", function() {
29+
const MIN_FEE_PAY = 10;
30+
const BLOCK_REWARD = 50;
31+
const FAUCET_INITIAL_CCS = new U64("18000000000000000000");
32+
2433
let node: CodeChain;
2534

2635
beforeEach(async function() {
@@ -33,15 +42,30 @@ describe("Reward = 50, 1 miner", function() {
3342

3443
it("Mining an empty block", async function() {
3544
await node.sdk.rpc.devel.startSealing();
45+
expect(
46+
await node.sdk.rpc.chain.getBalance(faucetAddress)
47+
).to.deep.equal(FAUCET_INITIAL_CCS);
3648
expect(await node.sdk.rpc.chain.getBalance(aliceAddress)).to.deep.equal(
37-
new U64(50)
49+
new U64(BLOCK_REWARD)
50+
);
51+
expect(await node.sdk.rpc.chain.getBalance(bobAddress)).to.deep.equal(
52+
new U64(0)
3853
);
3954
});
4055

4156
it("Mining a block with 1 transaction", async function() {
4257
await node.sendPayTx({ fee: 10 });
58+
59+
expect(
60+
await node.sdk.rpc.chain.getBalance(faucetAddress)
61+
).to.deep.equal(
62+
FAUCET_INITIAL_CCS.minus(10 /* fee */).plus(7 /* share */)
63+
);
4364
expect(await node.sdk.rpc.chain.getBalance(aliceAddress)).to.deep.equal(
44-
new U64(50 + 10)
65+
new U64(2 /* share */).plus(BLOCK_REWARD)
66+
);
67+
expect(await node.sdk.rpc.chain.getBalance(bobAddress)).to.deep.equal(
68+
new U64(1 /* share */)
4569
);
4670
});
4771

@@ -60,33 +84,81 @@ describe("Reward = 50, 1 miner", function() {
6084
seq: 2
6185
});
6286
await node.sdk.rpc.devel.startSealing();
87+
88+
expect(
89+
await node.sdk.rpc.chain.getBalance(faucetAddress)
90+
).to.deep.equal(
91+
FAUCET_INITIAL_CCS.minus(10 + 10 + 15 /* fee */).plus(
92+
21 /* share */
93+
)
94+
);
6395
expect(await node.sdk.rpc.chain.getBalance(aliceAddress)).to.deep.equal(
64-
new U64(50 + 35)
96+
new U64(6 /* share */)
97+
.plus(10 + 10 + 15 - 3 * MIN_FEE_PAY /* share remaining */)
98+
.plus(BLOCK_REWARD)
99+
);
100+
expect(await node.sdk.rpc.chain.getBalance(bobAddress)).to.deep.equal(
101+
new U64(3 /* share */)
65102
);
66103
});
67104

68105
it("Mining a block with a transaction that pays the author", async function() {
69106
await node.pay(aliceAddress, 100);
107+
expect(
108+
await node.sdk.rpc.chain.getBalance(faucetAddress)
109+
).to.deep.equal(
110+
FAUCET_INITIAL_CCS.minus(100 /* pay */)
111+
.minus(10 /* fee */)
112+
.plus(7 /* share */)
113+
);
70114
expect(await node.sdk.rpc.chain.getBalance(aliceAddress)).to.deep.equal(
71-
new U64(50 + 10 + 100)
115+
new U64(100 /* pay */).plus(2 /* share */).plus(BLOCK_REWARD)
116+
);
117+
expect(await node.sdk.rpc.chain.getBalance(bobAddress)).to.deep.equal(
118+
new U64(1 /* share */)
72119
);
73120
});
74121

75122
it("Mining a block with a transaction which author pays someone in", async function() {
76-
await node.sendPayTx({ fee: 10 }); // +60
123+
await node.sendPayTx({ fee: 10 });
124+
expect(
125+
await node.sdk.rpc.chain.getBalance(faucetAddress)
126+
).to.deep.equal(
127+
FAUCET_INITIAL_CCS.minus(10 /* fee */).plus(7 /* share */)
128+
);
77129
expect(await node.sdk.rpc.chain.getBalance(aliceAddress)).to.deep.equal(
78-
new U64(60)
130+
new U64(2 /* share */).plus(BLOCK_REWARD)
131+
);
132+
expect(await node.sdk.rpc.chain.getBalance(bobAddress)).to.deep.equal(
133+
new U64(1 /* share */)
79134
);
80135

81136
const tx = await node.sdk.core
82137
.createPayTransaction({
83138
recipient: faucetAddress,
84-
quantity: 50
139+
quantity: 20
85140
})
86-
.sign({ secret: aliceSecret, seq: 0, fee: 10 }); // -60
87-
await node.sdk.rpc.chain.sendSignedTransaction(tx); // +60
141+
.sign({ secret: aliceSecret, seq: 0, fee: 10 });
142+
await node.sdk.rpc.chain.sendSignedTransaction(tx);
143+
144+
expect(
145+
await node.sdk.rpc.chain.getBalance(faucetAddress)
146+
).to.deep.equal(
147+
FAUCET_INITIAL_CCS.minus(10)
148+
.plus(7)
149+
.plus(20 /* pay */)
150+
.plus(7 /* share */)
151+
);
88152
expect(await node.sdk.rpc.chain.getBalance(aliceAddress)).to.deep.equal(
89-
new U64(60)
153+
new U64(2)
154+
.plus(BLOCK_REWARD)
155+
.minus(20 /* pay */)
156+
.minus(10 /* fee */)
157+
.plus(2 /* share */)
158+
.plus(BLOCK_REWARD)
159+
);
160+
expect(await node.sdk.rpc.chain.getBalance(bobAddress)).to.deep.equal(
161+
new U64(1 /* share*/).plus(1 /* share */)
90162
);
91163
});
92164

0 commit comments

Comments
 (0)