Merge pull request #784 from harmony-ek/cello_foundation_reward

Foundation node block reward for cello
pull/793/head
Eugene Kim 6 years ago committed by GitHub
commit 7ac35f04fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      cmd/client/txgen/main.go
  2. 33
      cmd/harmony/main.go
  3. 184
      consensus/consensus.go
  4. 1
      contracts/structs/structs.go
  5. 30
      core/types/shard_state.go
  6. 1
      node/node.go
  7. 9
      node/staking.go

@ -9,7 +9,10 @@ import (
"sync"
"time"
bls2 "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/internal/utils/contract"
"github.com/harmony-one/harmony/crypto/bls"
@ -111,6 +114,12 @@ func main() {
)
log.Root().SetHandler(h)
gsif, err := consensus.NewGenesisStakeInfoFinder()
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Cannot initialize stake info: %v\n", err)
os.Exit(1)
}
// Nodes containing blockchain data to mirror the shards' data in the network
nodes := []*node.Node{}
host, err := p2pimpl.NewHost(&selfPeer, nodePriKey)
@ -118,7 +127,23 @@ func main() {
panic("unable to new host in txgen")
}
for _, shardID := range shardIDs {
node := node.New(host, &consensus.Consensus{ShardID: shardID}, nil, false)
c := &consensus.Consensus{ShardID: shardID}
node := node.New(host, c, nil, false)
c.SetStakeInfoFinder(gsif)
c.ChainReader = node.Blockchain()
// Replace public keys with genesis accounts for the shard
c.PublicKeys = nil
startIdx := core.GenesisShardSize * shardID
endIdx := startIdx + core.GenesisShardSize
for _, acct := range contract.GenesisBLSAccounts[startIdx:endIdx] {
secretKey := bls2.SecretKey{}
if err := secretKey.SetHexString(acct.Private); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "cannot parse secret key: %v\n",
err)
os.Exit(1)
}
c.PublicKeys = append(c.PublicKeys, secretKey.GetPublicKey())
}
// Assign many fake addresses so we have enough address to play with at first
nodes = append(nodes, node)
}
@ -133,6 +158,9 @@ func main() {
clientNode := node.New(host, consensusObj, nil, false)
clientNode.Client = client.NewClient(clientNode.GetHost(), shardIDs)
consensusObj.SetStakeInfoFinder(gsif)
consensusObj.ChainReader = clientNode.Blockchain()
readySignal := make(chan uint32)
// This func is used to update the client's blockchain when new blocks are received from the leaders

@ -9,13 +9,13 @@ import (
"runtime"
"time"
"github.com/harmony-one/harmony/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus"
"github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/drand"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/profiler"
@ -197,20 +197,29 @@ func setUpConsensusAndNode(nodeConfig *nodeconfig.ConfigType) (*consensus.Consen
// Consensus object.
// TODO: consensus object shouldn't start here
// TODO(minhdoan): During refactoring, found out that the peers list is actually empty. Need to clean up the logic of consensus later.
consensus, err := consensus.New(nodeConfig.Host, nodeConfig.ShardID, nodeConfig.Leader, nodeConfig.ConsensusPriKey)
currentConsensus, err := consensus.New(nodeConfig.Host, nodeConfig.ShardID, nodeConfig.Leader, nodeConfig.ConsensusPriKey)
if err != nil {
fmt.Fprintf(os.Stderr, "Error :%v \n", err)
os.Exit(1)
}
consensus.MinPeers = *minPeers
currentConsensus.MinPeers = *minPeers
// Current node.
currentNode := node.New(nodeConfig.Host, consensus, nodeConfig.MainDB, *isArchival)
currentNode := node.New(nodeConfig.Host, currentConsensus, nodeConfig.MainDB, *isArchival)
currentNode.NodeConfig.SetRole(nodeconfig.NewNode)
currentNode.AccountKey = nodeConfig.StakingPriKey
utils.GetLogInstance().Info("node account set",
"address", crypto.PubkeyToAddress(currentNode.AccountKey.PublicKey))
if gsif, err := consensus.NewGenesisStakeInfoFinder(); err == nil {
currentConsensus.SetStakeInfoFinder(gsif)
} else {
_, _ = fmt.Fprintf(os.Stderr, "Cannot initialize stake info: %v\n", err)
os.Exit(1)
}
// TODO: refactor the creation of blockchain out of node.New()
consensus.ChainReader = currentNode.Blockchain()
currentConsensus.ChainReader = currentNode.Blockchain()
if *isGenesis {
// TODO: need change config file and use switch instead of complicated "if else" condition
@ -259,7 +268,7 @@ func setUpConsensusAndNode(nodeConfig *nodeconfig.ConfigType) (*consensus.Consen
currentNode.Consensus.RegisterRndChannel(dRand.RndChannel)
currentNode.DRand = dRand
if consensus.ShardID != 0 {
if currentConsensus.ShardID != 0 {
currentNode.AddBeaconChainDatabase(nodeConfig.BeaconDB)
}
// This needs to be executed after consensus and drand are setup
@ -268,14 +277,14 @@ func setUpConsensusAndNode(nodeConfig *nodeconfig.ConfigType) (*consensus.Consen
// Set the consensus ID to be the current block number
height := currentNode.Blockchain().CurrentBlock().NumberU64()
consensus.SetConsensusID(uint32(height))
currentConsensus.SetConsensusID(uint32(height))
utils.GetLogInstance().Info("Init Blockchain", "height", height)
// Assign closure functions to the consensus object
consensus.BlockVerifier = currentNode.VerifyNewBlock
consensus.OnConsensusDone = currentNode.PostConsensusProcessing
currentConsensus.BlockVerifier = currentNode.VerifyNewBlock
currentConsensus.OnConsensusDone = currentNode.PostConsensusProcessing
currentNode.State = node.NodeWaitToJoin
return consensus, currentNode
return currentConsensus, currentNode
}
func main() {

@ -7,25 +7,37 @@ import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"reflect"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
protobuf "github.com/golang/protobuf/proto"
"github.com/harmony-one/bls/ffi/go/bls"
libp2p_peer "github.com/libp2p/go-libp2p-peer"
"golang.org/x/crypto/sha3"
proto_discovery "github.com/harmony-one/harmony/api/proto/discovery"
msg_pb "github.com/harmony-one/harmony/api/proto/message"
consensus_engine "github.com/harmony-one/harmony/consensus/engine"
"github.com/harmony-one/harmony/contracts/structs"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/internal/utils/contract"
"github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/p2p/host"
libp2p_peer "github.com/libp2p/go-libp2p-peer"
"golang.org/x/crypto/sha3"
)
// Block reward per block signature.
// TODO ek – per sig per stake
var (
BlockReward = big.NewInt(params.Ether / 10)
)
// Consensus is the main struct with all states and data related to consensus process.
@ -112,6 +124,34 @@ type Consensus struct {
// The p2p host used to send/receive p2p messages
host p2p.Host
// Staking information finder
stakeInfoFinder StakeInfoFinder
}
// StakeInfoFinder returns the stake information finder instance this
// consensus uses, e.g. for block reward distribution.
func (consensus *Consensus) StakeInfoFinder() StakeInfoFinder {
return consensus.stakeInfoFinder
}
// SetStakeInfoFinder sets the stake information finder instance this
// consensus uses, e.g. for block reward distribution.
func (consensus *Consensus) SetStakeInfoFinder(stakeInfoFinder StakeInfoFinder) {
consensus.stakeInfoFinder = stakeInfoFinder
}
// StakeInfoFinder finds the staking account for the given consensus key.
type StakeInfoFinder interface {
// FindStakeInfoByNodeKey returns a list of staking information matching
// the given node key. Caller may modify the returned slice of StakeInfo
// struct pointers, but must not modify the StakeInfo structs themselves.
FindStakeInfoByNodeKey(key *bls.PublicKey) []*structs.StakeInfo
// FindStakeInfoByAccount returns a list of staking information matching
// the given account. Caller may modify the returned slice of StakeInfo
// struct pointers, but must not modify the StakeInfo structs themselves.
FindStakeInfoByAccount(addr common.Address) []*structs.StakeInfo
}
// BlockConsensusStatus used to keep track of the consensus status of multiple blocks received so far
@ -564,7 +604,10 @@ func (consensus *Consensus) VerifySeal(chain consensus_engine.ChainReader, heade
func (consensus *Consensus) Finalize(chain consensus_engine.ChainReader, header *types.Header, state *state.DB, txs []*types.Transaction, receipts []*types.Receipt) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root
// Header seems complete, assemble into a block and return
accumulateRewards(chain.Config(), state, header)
err := consensus.accumulateRewards(chain.Config(), state, header)
if err != nil {
ctxerror.Log15(utils.GetLogInstance().Error, err)
}
header.Root = state.IntermediateRoot(false)
return types.NewBlock(header, txs, receipts), nil
}
@ -607,8 +650,61 @@ func (consensus *Consensus) Prepare(chain consensus_engine.ChainReader, header *
// AccumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(config *params.ChainConfig, state *state.DB, header *types.Header) {
// TODO: implement mining rewards
func (consensus *Consensus) accumulateRewards(
config *params.ChainConfig, state *state.DB, header *types.Header,
) error {
logger := utils.GetLogInstance().New("parentHash", header.ParentHash)
if header.ParentHash == (common.Hash{}) {
// This block is a genesis block,
// without a parent block whose signer to reward.
return nil
}
if consensus.ChainReader == nil {
return errors.New("ChainReader is nil")
}
parent := consensus.ChainReader.GetHeaderByHash(header.ParentHash)
if parent == nil {
return ctxerror.New("cannot retrieve parent header",
"parentHash", header.ParentHash,
)
}
mask, err := bls_cosi.NewMask(consensus.PublicKeys, nil)
if err != nil {
return ctxerror.New("cannot create group sig mask").WithCause(err)
}
logger.Debug("accumulateRewards: setting group sig mask",
"destLen", mask.Len(),
"srcLen", len(parent.CommitBitmap),
)
if err := mask.SetMask(parent.CommitBitmap); err != nil {
return ctxerror.New("cannot set group sig mask bits").WithCause(err)
}
totalAmount := big.NewInt(0)
numAccounts := 0
signingKeys := mask.GetPubKeyFromMask(true)
for _, key := range signingKeys {
stakeInfos := consensus.stakeInfoFinder.FindStakeInfoByNodeKey(key)
if len(stakeInfos) == 0 {
logger.Error("accumulateRewards: node has no stake info",
"nodeKey", key.GetHexString())
continue
}
numAccounts += len(stakeInfos)
for _, stakeInfo := range stakeInfos {
utils.GetLogInstance().Info("accumulateRewards: rewarding",
"block", header.Hash(),
"account", stakeInfo.Account,
"node", stakeInfo.BlsPublicKey.Hex(),
"amount", BlockReward)
state.AddBalance(stakeInfo.Account, BlockReward)
totalAmount = new(big.Int).Add(totalAmount, BlockReward)
}
}
logger.Debug("accumulateRewards: paid out block reward",
"numSigs", len(signingKeys),
"numAccounts", numAccounts,
"totalAmount", totalAmount)
return nil
}
// GetSelfAddress returns the address in hex
@ -674,3 +770,81 @@ func (consensus *Consensus) GetNodeIDs() []libp2p_peer.ID {
func (consensus *Consensus) GetConsensusID() uint32 {
return consensus.consensusID
}
// GenesisStakeInfoFinder is a stake info finder implementation using only
// genesis accounts.
// When used for block reward, it rewards only foundational nodes.
type GenesisStakeInfoFinder struct {
byNodeKey map[types.BlsPublicKey][]*structs.StakeInfo
byAccount map[common.Address][]*structs.StakeInfo
}
// FindStakeInfoByNodeKey returns the genesis account matching the given node
// key, as a single-item StakeInfo list.
// It returns nil if the key is not a genesis node key.
func (f *GenesisStakeInfoFinder) FindStakeInfoByNodeKey(
key *bls.PublicKey,
) []*structs.StakeInfo {
var pk types.BlsPublicKey
if err := pk.FromLibBLSPublicKey(key); err != nil {
ctxerror.Log15(utils.GetLogInstance().Warn, ctxerror.New(
"cannot convert BLS public key",
).WithCause(err))
return nil
}
l, _ := f.byNodeKey[pk]
return l
}
// FindStakeInfoByAccount returns the genesis account matching the given
// address, as a single-item StakeInfo list.
// It returns nil if the address is not a genesis account.
func (f *GenesisStakeInfoFinder) FindStakeInfoByAccount(
addr common.Address,
) []*structs.StakeInfo {
l, _ := f.byAccount[addr]
return l
}
// NewGenesisStakeInfoFinder returns a stake info finder that can look up
// genesis nodes.
func NewGenesisStakeInfoFinder() (*GenesisStakeInfoFinder, error) {
f := &GenesisStakeInfoFinder{
byNodeKey: make(map[types.BlsPublicKey][]*structs.StakeInfo),
byAccount: make(map[common.Address][]*structs.StakeInfo),
}
for idx, account := range contract.GenesisAccounts {
blsSecretKeyHex := contract.GenesisBLSAccounts[idx].Private
blsSecretKey := bls.SecretKey{}
if err := blsSecretKey.SetHexString(blsSecretKeyHex); err != nil {
return nil, ctxerror.New("cannot convert BLS secret key",
"accountIndex", idx,
).WithCause(err)
}
pub := blsSecretKey.GetPublicKey()
var blsPublicKey types.BlsPublicKey
if err := blsPublicKey.FromLibBLSPublicKey(pub); err != nil {
return nil, ctxerror.New("cannot convert BLS public key",
"accountIndex", idx,
).WithCause(err)
}
addressBytes, err := hexutil.Decode(account.Address)
if err != nil {
return nil, ctxerror.New("cannot decode account address",
"accountIndex", idx,
).WithCause(err)
}
var address common.Address
address.SetBytes(addressBytes)
stakeInfo := &structs.StakeInfo{
Account: address,
BlsPublicKey: blsPublicKey,
BlockNum: common.Big0,
LockPeriodCount: big.NewInt(0x7fffffffffffffff),
Amount: common.Big0,
}
f.byNodeKey[blsPublicKey] = append(f.byNodeKey[blsPublicKey], stakeInfo)
f.byAccount[address] = append(f.byAccount[address], stakeInfo)
}
return f, nil
}

@ -21,6 +21,7 @@ type StakeInfoReturnValue struct {
// StakeInfo stores the staking information for a staker.
type StakeInfo struct {
Account common.Address
BlsPublicKey types.BlsPublicKey
BlockNum *big.Int
LockPeriodCount *big.Int // The number of locking period the token will be locked.

@ -6,7 +6,10 @@ import (
"sort"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
"golang.org/x/crypto/sha3"
"github.com/harmony-one/harmony/internal/ctxerror"
)
// EpochShardState is the shard state of an epoch
@ -21,6 +24,28 @@ type ShardState []Committee
// BlsPublicKey defines the bls public key
type BlsPublicKey [96]byte
// Hex returns the hex string of bls public key
func (pk BlsPublicKey) Hex() string {
return hex.EncodeToString(pk[:])
}
// FromLibBLSPublicKey replaces the key contents with the given key,
func (pk *BlsPublicKey) FromLibBLSPublicKey(key *bls.PublicKey) error {
bytes := key.Serialize()
if len(bytes) != len(pk) {
return ctxerror.New("BLS public key size mismatch",
"expected", len(pk),
"actual", len(bytes))
}
copy(pk[:], bytes)
return nil
}
// ToLibBLSPublicKey copies the key contents into the given key.
func (pk *BlsPublicKey) ToLibBLSPublicKey(key *bls.PublicKey) error {
return key.Deserialize(pk[:])
}
// NodeID represents node id (BLS address).
type NodeID struct {
EcdsaAddress string
@ -34,11 +59,6 @@ type Committee struct {
NodeList []NodeID
}
// Hex returns the hex string of bls public key
func (pk BlsPublicKey) Hex() string {
return hex.EncodeToString(pk[:])
}
// GetHashFromNodeList will sort the list, then use Keccak256 to hash the list
// notice that the input nodeList will be modified (sorted)
func GetHashFromNodeList(nodeList []NodeID) []byte {

@ -141,7 +141,6 @@ type Node struct {
//Node Account
AccountKey *ecdsa.PrivateKey
Address common.Address
// For test only
TestBankKeys []*ecdsa.PrivateKey

@ -51,10 +51,11 @@ func (node *Node) UpdateStakingList(stakeInfoReturnValue *structs.StakeInfoRetur
copy(blsPubKey[32:64], stakeInfoReturnValue.BlsPubicKeys2[i][:])
copy(blsPubKey[64:96], stakeInfoReturnValue.BlsPubicKeys2[i][:])
node.CurrentStakes[addr] = &structs.StakeInfo{
blsPubKey,
blockNum,
lockPeriodCount,
stakeInfoReturnValue.Amounts[i],
Account: addr,
BlsPublicKey: blsPubKey,
BlockNum: blockNum,
LockPeriodCount: lockPeriodCount,
Amount: stakeInfoReturnValue.Amounts[i],
}
}
}

Loading…
Cancel
Save