Do massive overhaul of consensus part due to new architecture with blockchain, implement chain selection
This commit is contained in:
parent
012d4a68c3
commit
4e7294e046
@ -17,7 +17,7 @@ type BeaconResult struct {
|
|||||||
|
|
||||||
type BeaconNetworks []BeaconNetwork
|
type BeaconNetworks []BeaconNetwork
|
||||||
|
|
||||||
func (bn BeaconNetworks) BeaconNetworkForRound(e types.DrandRound) BeaconAPI {
|
func (bn BeaconNetworks) BeaconNetworkForRound(e uint64) BeaconAPI {
|
||||||
for i := len(bn) - 1; i >= 0; i-- {
|
for i := len(bn) - 1; i >= 0; i-- {
|
||||||
bp := bn[i]
|
bp := bn[i]
|
||||||
if e >= bp.Start {
|
if e >= bp.Start {
|
||||||
@ -28,7 +28,7 @@ func (bn BeaconNetworks) BeaconNetworkForRound(e types.DrandRound) BeaconAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BeaconNetwork struct {
|
type BeaconNetwork struct {
|
||||||
Start types.DrandRound
|
Start uint64
|
||||||
Beacon BeaconAPI
|
Beacon BeaconAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Arceliar/phony"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/consensus"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/beacon"
|
"github.com/Secured-Finance/dione/beacon"
|
||||||
"github.com/drand/drand/chain"
|
"github.com/drand/drand/chain"
|
||||||
"github.com/drand/drand/client"
|
"github.com/drand/drand/client"
|
||||||
@ -30,16 +34,17 @@ var log = logrus.WithFields(logrus.Fields{
|
|||||||
})
|
})
|
||||||
|
|
||||||
type DrandBeacon struct {
|
type DrandBeacon struct {
|
||||||
|
phony.Inbox
|
||||||
DrandClient client.Client
|
DrandClient client.Client
|
||||||
PublicKey kyber.Point
|
PublicKey kyber.Point
|
||||||
drandResultChannel <-chan client.Result
|
drandResultChannel <-chan client.Result
|
||||||
|
|
||||||
cacheLock sync.Mutex
|
cacheLock sync.Mutex
|
||||||
localCache map[uint64]types.BeaconEntry
|
localCache map[uint64]types.BeaconEntry
|
||||||
latestDrandRound uint64
|
latestDrandRound uint64
|
||||||
|
consensusManager *consensus.PBFTConsensusManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) {
|
func NewDrandBeacon(ps *pubsub.PubSub, pcm *consensus.PBFTConsensusManager) (*DrandBeacon, error) {
|
||||||
cfg := config.NewDrandConfig()
|
cfg := config.NewDrandConfig()
|
||||||
|
|
||||||
drandChain, err := chain.InfoFromJSON(bytes.NewReader([]byte(cfg.ChainInfo)))
|
drandChain, err := chain.InfoFromJSON(bytes.NewReader([]byte(cfg.ChainInfo)))
|
||||||
@ -80,6 +85,7 @@ func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) {
|
|||||||
db := &DrandBeacon{
|
db := &DrandBeacon{
|
||||||
DrandClient: drandClient,
|
DrandClient: drandClient,
|
||||||
localCache: make(map[uint64]types.BeaconEntry),
|
localCache: make(map[uint64]types.BeaconEntry),
|
||||||
|
consensusManager: pcm,
|
||||||
}
|
}
|
||||||
|
|
||||||
db.PublicKey = drandChain.PublicKey
|
db.PublicKey = drandChain.PublicKey
|
||||||
@ -116,6 +122,7 @@ func (db *DrandBeacon) loop(ctx context.Context) {
|
|||||||
{
|
{
|
||||||
db.cacheValue(newBeaconResultFromDrandResult(res))
|
db.cacheValue(newBeaconResultFromDrandResult(res))
|
||||||
db.updateLatestDrandRound(res.Round())
|
db.updateLatestDrandRound(res.Round())
|
||||||
|
db.consensusManager.NewDrandRound(db, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
blockchain/pool/blockpool.go
Normal file
57
blockchain/pool/blockpool.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
"github.com/Secured-Finance/dione/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlockPool is pool for blocks that isn't not validated or committed yet
|
||||||
|
type BlockPool struct {
|
||||||
|
knownBlocks cache.Cache
|
||||||
|
acceptedBlocks cache.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockPool() (*BlockPool, error) {
|
||||||
|
bp := &BlockPool{
|
||||||
|
acceptedBlocks: cache.NewInMemoryCache(), // here we need to use separate cache
|
||||||
|
}
|
||||||
|
|
||||||
|
return bp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bp *BlockPool) AddBlock(block *types.Block) error {
|
||||||
|
return bp.knownBlocks.Store(hex.EncodeToString(block.Header.Hash), block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bp *BlockPool) GetBlock(blockhash []byte) (*types.Block, error) {
|
||||||
|
var block *types.Block
|
||||||
|
return block, bp.knownBlocks.Get(hex.EncodeToString(blockhash), &block)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PruneBlocks cleans known blocks list. It is called when new consensus round starts.
|
||||||
|
func (bp *BlockPool) PruneBlocks() {
|
||||||
|
for k := range bp.knownBlocks.Items() {
|
||||||
|
bp.knownBlocks.Delete(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bp *BlockPool) AddAcceptedBlock(block *types.Block) error {
|
||||||
|
return bp.acceptedBlocks.Store(hex.EncodeToString(block.Header.Hash), block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bp *BlockPool) GetAllAcceptedBlocks() []*types.Block {
|
||||||
|
var blocks []*types.Block
|
||||||
|
for _, v := range bp.acceptedBlocks.Items() {
|
||||||
|
blocks = append(blocks, v.(*types.Block))
|
||||||
|
}
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// PruneAcceptedBlocks cleans accepted blocks list. It is called when new consensus round starts.
|
||||||
|
func (bp *BlockPool) PruneAcceptedBlocks() {
|
||||||
|
for k := range bp.acceptedBlocks.Items() {
|
||||||
|
bp.acceptedBlocks.Delete(k)
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,13 @@ package types
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"github.com/wealdtech/go-merkletree"
|
"github.com/wealdtech/go-merkletree"
|
||||||
"github.com/wealdtech/go-merkletree/keccak256"
|
"github.com/wealdtech/go-merkletree/keccak256"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/wallet"
|
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,6 +25,7 @@ type BlockHeader struct {
|
|||||||
LastHash []byte
|
LastHash []byte
|
||||||
LastHashProof *merkletree.Proof
|
LastHashProof *merkletree.Proof
|
||||||
Proposer peer.ID
|
Proposer peer.ID
|
||||||
|
ProposerEth common.Address
|
||||||
Signature []byte
|
Signature []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,12 +40,8 @@ func GenesisBlock() *Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *wallet.LocalWallet) (*Block, error) {
|
func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth common.Address, privateKey crypto.PrivKey) (*Block, error) {
|
||||||
timestamp := time.Now().Unix()
|
timestamp := time.Now().Unix()
|
||||||
proposer, err := wallet.GetDefault()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract hashes from transactions
|
// extract hashes from transactions
|
||||||
var txHashes [][]byte
|
var txHashes [][]byte
|
||||||
@ -58,8 +58,8 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *walle
|
|||||||
// fetch merkle tree root hash (block hash)
|
// fetch merkle tree root hash (block hash)
|
||||||
blockHash := tree.Root()
|
blockHash := tree.Root()
|
||||||
|
|
||||||
// sign this block hash
|
// sign the block hash
|
||||||
s, err := wallet.Sign(proposer, blockHash)
|
s, err := privateKey.Sign(blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -69,12 +69,18 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *walle
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proposer, err := peer.IDFromPrivateKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
block := &Block{
|
block := &Block{
|
||||||
Header: &BlockHeader{
|
Header: &BlockHeader{
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
Height: lastBlockHeader.Height + 1,
|
Height: lastBlockHeader.Height + 1,
|
||||||
Proposer: proposer,
|
Proposer: proposer,
|
||||||
Signature: s.Data,
|
ProposerEth: minerEth,
|
||||||
|
Signature: s,
|
||||||
Hash: blockHash,
|
Hash: blockHash,
|
||||||
LastHash: lastBlockHeader.Hash,
|
LastHash: lastBlockHeader.Hash,
|
||||||
LastHashProof: lastHashProof,
|
LastHashProof: lastHashProof,
|
||||||
|
@ -28,3 +28,5 @@ func NewDrandConfig() *DrandConfig {
|
|||||||
}
|
}
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DrandChainGenesisTime = uint64(1603603302)
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
var ExpectedLeadersPerEpoch = int64(5)
|
|
||||||
|
|
||||||
var TasksPerEpoch = uint64(ExpectedLeadersPerEpoch)
|
|
||||||
|
|
||||||
var ChainGenesis = uint64(1603603302)
|
|
||||||
|
|
||||||
var TaskEpochInterval = uint64(15)
|
|
5
config/win_config.go
Normal file
5
config/win_config.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "math/big"
|
||||||
|
|
||||||
|
var ExpectedLeadersPerEpoch = big.NewInt(5)
|
@ -1,11 +1,21 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/cache"
|
"github.com/Secured-Finance/dione/blockchain"
|
||||||
|
|
||||||
|
"github.com/drand/drand/client"
|
||||||
|
|
||||||
|
"github.com/Arceliar/phony"
|
||||||
|
|
||||||
|
types3 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/pool"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/consensus/types"
|
"github.com/Secured-Finance/dione/consensus/types"
|
||||||
|
|
||||||
@ -13,65 +23,87 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/pubsub"
|
"github.com/Secured-Finance/dione/pubsub"
|
||||||
types2 "github.com/Secured-Finance/dione/types"
|
)
|
||||||
|
|
||||||
|
type StateStatus uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateStatusUnknown = iota
|
||||||
|
|
||||||
|
StateStatusPrePrepared
|
||||||
|
StateStatusPrepared
|
||||||
|
StateStatusCommited
|
||||||
)
|
)
|
||||||
|
|
||||||
type PBFTConsensusManager struct {
|
type PBFTConsensusManager struct {
|
||||||
|
phony.Inbox
|
||||||
psb *pubsub.PubSubRouter
|
psb *pubsub.PubSubRouter
|
||||||
minApprovals int
|
minApprovals int // FIXME
|
||||||
privKey []byte
|
privKey crypto.PrivKey
|
||||||
msgLog *MessageLog
|
msgLog *ConsensusMessageLog
|
||||||
validator *ConsensusValidator
|
validator *ConsensusValidator
|
||||||
consensusMap map[string]*Consensus
|
|
||||||
ethereumClient *ethclient.EthereumClient
|
ethereumClient *ethclient.EthereumClient
|
||||||
miner *Miner
|
miner *Miner
|
||||||
cache cache.Cache
|
blockPool pool.BlockPool
|
||||||
|
blockchain blockchain.BlockChain
|
||||||
|
state *State
|
||||||
}
|
}
|
||||||
|
|
||||||
type Consensus struct {
|
type State struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
Finished bool
|
drandRound uint64
|
||||||
IsCurrentMinerLeader bool
|
randomness []byte
|
||||||
Task *types2.DioneTask
|
blockHeight uint64
|
||||||
|
status StateStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey []byte, ethereumClient *ethclient.EthereumClient, miner *Miner, evc cache.Cache) *PBFTConsensusManager {
|
func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey crypto.PrivKey, ethereumClient *ethclient.EthereumClient, miner *Miner) *PBFTConsensusManager {
|
||||||
pcm := &PBFTConsensusManager{}
|
pcm := &PBFTConsensusManager{}
|
||||||
pcm.psb = psb
|
pcm.psb = psb
|
||||||
pcm.miner = miner
|
pcm.miner = miner
|
||||||
pcm.validator = NewConsensusValidator(evc, miner)
|
pcm.validator = NewConsensusValidator(miner)
|
||||||
pcm.msgLog = NewMessageLog()
|
pcm.msgLog = NewConsensusMessageLog()
|
||||||
pcm.minApprovals = minApprovals
|
pcm.minApprovals = minApprovals
|
||||||
pcm.privKey = privKey
|
pcm.privKey = privKey
|
||||||
pcm.ethereumClient = ethereumClient
|
pcm.ethereumClient = ethereumClient
|
||||||
pcm.cache = evc
|
pcm.state = &State{}
|
||||||
pcm.consensusMap = map[string]*Consensus{}
|
pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types.PrePrepareMessage{})
|
||||||
pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types2.DioneTask{})
|
pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types.PrepareMessage{})
|
||||||
pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types2.DioneTask{})
|
pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types.CommitMessage{})
|
||||||
pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types2.DioneTask{})
|
|
||||||
return pcm
|
return pcm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcm *PBFTConsensusManager) Propose(task types2.DioneTask) error {
|
func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error {
|
||||||
pcm.createConsensusInfo(&task, true)
|
pcm.state.mutex.Lock()
|
||||||
|
defer pcm.state.mutex.Unlock()
|
||||||
prePrepareMsg, err := CreatePrePrepareWithTaskSignature(&task, pcm.privKey)
|
prePrepareMsg, err := NewMessage(types.ConsensusMessage{Block: blk}, types.ConsensusMessageTypePrePrepare, pcm.privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pcm.psb.BroadcastToServiceTopic(prePrepareMsg)
|
pcm.psb.BroadcastToServiceTopic(prePrepareMsg)
|
||||||
|
pcm.state.status = StateStatusPrePrepared
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage) {
|
func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage) {
|
||||||
cmsg, err := unmarshalPayload(message)
|
pcm.state.mutex.Lock()
|
||||||
if err != nil {
|
defer pcm.state.mutex.Unlock()
|
||||||
|
prePrepare, ok := message.Payload.(types.PrePrepareMessage)
|
||||||
|
if !ok {
|
||||||
|
logrus.Warn("failed to convert payload to PrePrepare message")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmsg.Task.Miner == pcm.miner.address {
|
if prePrepare.Block.Header.Proposer == pcm.miner.address {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmsg := types.ConsensusMessage{
|
||||||
|
Type: types.ConsensusMessageTypePrePrepare,
|
||||||
|
From: message.From,
|
||||||
|
Block: prePrepare.Block,
|
||||||
|
}
|
||||||
|
|
||||||
if pcm.msgLog.Exists(cmsg) {
|
if pcm.msgLog.Exists(cmsg) {
|
||||||
logrus.Debugf("received existing pre_prepare msg, dropping...")
|
logrus.Debugf("received existing pre_prepare msg, dropping...")
|
||||||
return
|
return
|
||||||
@ -82,21 +114,43 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
pcm.msgLog.AddMessage(cmsg)
|
pcm.msgLog.AddMessage(cmsg)
|
||||||
|
pcm.blockPool.AddBlock(cmsg.Block)
|
||||||
|
|
||||||
prepareMsg, err := NewMessage(message, pubsub.PrepareMessageType)
|
prepareMsg, err := NewMessage(cmsg, types.ConsensusMessageTypePrepare, pcm.privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failed to create prepare message: %v", err)
|
logrus.Errorf("failed to create prepare message: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pcm.createConsensusInfo(&cmsg.Task, false)
|
pcm.psb.BroadcastToServiceTopic(prepareMsg)
|
||||||
|
pcm.state.status = StateStatusPrePrepared
|
||||||
pcm.psb.BroadcastToServiceTopic(&prepareMsg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) {
|
func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) {
|
||||||
cmsg, err := unmarshalPayload(message)
|
pcm.state.mutex.Lock()
|
||||||
|
defer pcm.state.mutex.Unlock()
|
||||||
|
prepare, ok := message.Payload.(types.PrepareMessage)
|
||||||
|
if !ok {
|
||||||
|
logrus.Warn("failed to convert payload to Prepare message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmsg := types.ConsensusMessage{
|
||||||
|
Type: types.ConsensusMessageTypePrepare,
|
||||||
|
From: message.From,
|
||||||
|
Blockhash: prepare.Blockhash,
|
||||||
|
Signature: prepare.Signature, // TODO check the signature
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, _ := message.From.ExtractPublicKey()
|
||||||
|
ok, err := pk.Verify(cmsg.Blockhash, cmsg.Signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logrus.Warnf("Failed to verify PREPARE message signature: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
logrus.Errorf("Signature of PREPARE message of peer %s isn't valid!", cmsg.From)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,18 +165,41 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) {
|
|||||||
|
|
||||||
pcm.msgLog.AddMessage(cmsg)
|
pcm.msgLog.AddMessage(cmsg)
|
||||||
|
|
||||||
if len(pcm.msgLog.Get(types.MessageTypePrepare, cmsg.Task.ConsensusID)) >= pcm.minApprovals {
|
if len(pcm.msgLog.Get(types.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= pcm.minApprovals {
|
||||||
commitMsg, err := NewMessage(message, pubsub.CommitMessageType)
|
commitMsg, err := NewMessage(cmsg, types.ConsensusMessageTypeCommit, pcm.privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failed to create commit message: %w", err)
|
logrus.Errorf("failed to create commit message: %v", err)
|
||||||
}
|
}
|
||||||
pcm.psb.BroadcastToServiceTopic(&commitMsg)
|
pcm.psb.BroadcastToServiceTopic(commitMsg)
|
||||||
|
pcm.state.status = StateStatusPrepared
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) {
|
func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) {
|
||||||
cmsg, err := unmarshalPayload(message)
|
pcm.state.mutex.Lock()
|
||||||
|
defer pcm.state.mutex.Unlock()
|
||||||
|
commit, ok := message.Payload.(types.CommitMessage)
|
||||||
|
if !ok {
|
||||||
|
logrus.Warn("failed to convert payload to Prepare message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmsg := types.ConsensusMessage{
|
||||||
|
Type: types.ConsensusMessageTypeCommit,
|
||||||
|
From: message.From,
|
||||||
|
Blockhash: commit.Blockhash,
|
||||||
|
Signature: commit.Signature, // TODO check the signature
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, _ := message.From.ExtractPublicKey()
|
||||||
|
ok, err := pk.Verify(cmsg.Blockhash, cmsg.Signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logrus.Warnf("Failed to verify COMMIT message signature: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
logrus.Errorf("Signature of COMMIT message of peer %s isn't valid!", cmsg.From)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,80 +214,70 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) {
|
|||||||
|
|
||||||
pcm.msgLog.AddMessage(cmsg)
|
pcm.msgLog.AddMessage(cmsg)
|
||||||
|
|
||||||
if len(pcm.msgLog.Get(types.MessageTypeCommit, cmsg.Task.ConsensusID)) >= pcm.minApprovals {
|
if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals {
|
||||||
info := pcm.GetConsensusInfo(cmsg.Task.ConsensusID)
|
block, err := pcm.blockPool.GetBlock(cmsg.Blockhash)
|
||||||
if info == nil {
|
|
||||||
logrus.Debugf("consensus doesn't exist in our consensus map - skipping...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
info.mutex.Lock()
|
|
||||||
defer info.mutex.Unlock()
|
|
||||||
if info.Finished {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if info.IsCurrentMinerLeader {
|
|
||||||
logrus.Infof("Submitting on-chain result for consensus ID: %s", cmsg.Task.ConsensusID)
|
|
||||||
reqID, ok := new(big.Int).SetString(cmsg.Task.RequestID, 10)
|
|
||||||
if !ok {
|
|
||||||
logrus.Errorf("Failed to parse request ID: %v", cmsg.Task.RequestID)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := pcm.ethereumClient.SubmitRequestAnswer(reqID, cmsg.Task.Payload)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Failed to submit on-chain result: %v", err)
|
logrus.Debug(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pcm.blockPool.AddAcceptedBlock(block)
|
||||||
|
pcm.state.status = StateStatusCommited
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info.Finished = true
|
func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Result) {
|
||||||
}
|
pcm.Act(from, func() {
|
||||||
|
pcm.state.mutex.Lock()
|
||||||
|
defer pcm.state.mutex.Unlock()
|
||||||
|
block, err := pcm.commitAcceptedBlocks()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcm *PBFTConsensusManager) createConsensusInfo(task *types2.DioneTask, isLeader bool) {
|
minedBlock, err := pcm.miner.MineBlock(res.Randomness(), res.Round(), block.Header)
|
||||||
if _, ok := pcm.consensusMap[task.ConsensusID]; !ok {
|
if err != nil {
|
||||||
pcm.consensusMap[task.ConsensusID] = &Consensus{
|
logrus.Errorf("Failed to mine the block: %s", err.Error())
|
||||||
IsCurrentMinerLeader: isLeader,
|
return
|
||||||
Task: task,
|
|
||||||
Finished: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcm *PBFTConsensusManager) GetConsensusInfo(consensusID string) *Consensus {
|
pcm.state.drandRound = res.Round()
|
||||||
c, ok := pcm.consensusMap[consensusID]
|
pcm.state.randomness = res.Randomness()
|
||||||
if !ok {
|
pcm.state.blockHeight = pcm.state.blockHeight + 1
|
||||||
return nil
|
|
||||||
|
// if we are round winner
|
||||||
|
if minedBlock != nil {
|
||||||
|
err = pcm.propose(minedBlock)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to propose the block: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c
|
func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) {
|
||||||
|
blocks := pcm.blockPool.GetAllAcceptedBlocks()
|
||||||
|
if blocks == nil {
|
||||||
|
return nil, errors.New("there is no accepted blocks")
|
||||||
|
}
|
||||||
|
var maxStake *big.Int
|
||||||
|
var selectedBlock *types3.Block
|
||||||
|
for _, v := range blocks {
|
||||||
|
stake, err := pcm.ethereumClient.GetMinerStake(v.Header.ProposerEth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalPayload(msg *pubsub.GenericMessage) (types.ConsensusMessage, error) {
|
if maxStake != nil {
|
||||||
task, ok := msg.Payload.(types2.DioneTask)
|
if stake.Cmp(maxStake) == -1 {
|
||||||
if !ok {
|
continue
|
||||||
return types.ConsensusMessage{}, fmt.Errorf("cannot convert payload to DioneTask")
|
|
||||||
}
|
|
||||||
var consensusMessageType types.MessageType
|
|
||||||
switch msg.Type {
|
|
||||||
case pubsub.PrePrepareMessageType:
|
|
||||||
{
|
|
||||||
consensusMessageType = types.MessageTypePrePrepare
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case pubsub.PrepareMessageType:
|
|
||||||
{
|
|
||||||
consensusMessageType = types.MessageTypePrepare
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case pubsub.CommitMessageType:
|
|
||||||
{
|
|
||||||
consensusMessageType = types.MessageTypeCommit
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmsg := types.ConsensusMessage{
|
maxStake = stake
|
||||||
Type: consensusMessageType,
|
selectedBlock = v
|
||||||
From: msg.From,
|
|
||||||
Task: task,
|
|
||||||
}
|
}
|
||||||
return cmsg, nil
|
logrus.Debugf("Selected block of miner %s", selectedBlock.Header.ProposerEth.Hex())
|
||||||
|
pcm.blockPool.PruneAcceptedBlocks()
|
||||||
|
return selectedBlock, pcm.blockchain.StoreBlock(selectedBlock)
|
||||||
}
|
}
|
||||||
|
@ -1,153 +1,127 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Secured-Finance/dione/cache"
|
|
||||||
types2 "github.com/Secured-Finance/dione/consensus/types"
|
types2 "github.com/Secured-Finance/dione/consensus/types"
|
||||||
"github.com/Secured-Finance/dione/consensus/validation"
|
|
||||||
"github.com/Secured-Finance/dione/contracts/dioneOracle"
|
|
||||||
"github.com/Secured-Finance/dione/types"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/filecoin-project/go-state-types/crypto"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConsensusValidator struct {
|
type ConsensusValidator struct {
|
||||||
validationFuncMap map[types2.MessageType]func(msg types2.ConsensusMessage) bool
|
validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool
|
||||||
cache cache.Cache
|
|
||||||
miner *Miner
|
miner *Miner
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator {
|
func NewConsensusValidator(miner *Miner) *ConsensusValidator {
|
||||||
cv := &ConsensusValidator{
|
cv := &ConsensusValidator{
|
||||||
cache: ec,
|
|
||||||
miner: miner,
|
miner: miner,
|
||||||
}
|
}
|
||||||
|
|
||||||
cv.validationFuncMap = map[types2.MessageType]func(msg types2.ConsensusMessage) bool{
|
cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool{
|
||||||
types2.MessageTypePrePrepare: func(msg types2.ConsensusMessage) bool {
|
// FIXME it all
|
||||||
// TODO here we need to do validation of tx itself
|
//types2.ConsensusMessageTypePrePrepare: func(msg types2.PrePrepareMessage) bool {
|
||||||
|
// // TODO here we need to do validation of block itself
|
||||||
// === verify task signature ===
|
//
|
||||||
err := VerifyTaskSignature(msg.Task)
|
// // === verify task signature ===
|
||||||
if err != nil {
|
// err := VerifyTaskSignature(msg.Task)
|
||||||
logrus.Errorf("unable to verify signature: %v", err)
|
// if err != nil {
|
||||||
return false
|
// logrus.Errorf("unable to verify signature: %v", err)
|
||||||
}
|
// return false
|
||||||
/////////////////////////////////
|
// }
|
||||||
|
// /////////////////////////////////
|
||||||
// === verify if request exists in cache ===
|
//
|
||||||
var requestEvent *dioneOracle.DioneOracleNewOracleRequest
|
// // === verify if request exists in cache ===
|
||||||
err = cv.cache.Get("request_"+msg.Task.RequestID, &requestEvent)
|
// var requestEvent *dioneOracle.DioneOracleNewOracleRequest
|
||||||
if err != nil {
|
// err = cv.cache.Get("request_"+msg.Task.RequestID, &requestEvent)
|
||||||
logrus.Errorf("the request doesn't exist in the cache or has been failed to decode: %v", err)
|
// if err != nil {
|
||||||
return false
|
// logrus.Errorf("the request doesn't exist in the cache or has been failed to decode: %v", err)
|
||||||
}
|
// return false
|
||||||
|
// }
|
||||||
if requestEvent.OriginChain != msg.Task.OriginChain ||
|
//
|
||||||
requestEvent.RequestType != msg.Task.RequestType ||
|
// if requestEvent.OriginChain != msg.Task.OriginChain ||
|
||||||
requestEvent.RequestParams != msg.Task.RequestParams {
|
// requestEvent.RequestType != msg.Task.RequestType ||
|
||||||
|
// requestEvent.RequestParams != msg.Task.RequestParams {
|
||||||
logrus.Errorf("the incoming task and cached request requestEvent don't match!")
|
//
|
||||||
return false
|
// logrus.Errorf("the incoming task and cached request requestEvent don't match!")
|
||||||
}
|
// return false
|
||||||
/////////////////////////////////
|
// }
|
||||||
|
// /////////////////////////////////
|
||||||
// === verify election proof wincount preliminarily ===
|
//
|
||||||
if msg.Task.ElectionProof.WinCount < 1 {
|
// // === verify election proof wincount preliminarily ===
|
||||||
logrus.Error("miner isn't a winner!")
|
// if msg.Task.ElectionProof.WinCount < 1 {
|
||||||
return false
|
// logrus.Error("miner isn't a winner!")
|
||||||
}
|
// return false
|
||||||
/////////////////////////////////
|
// }
|
||||||
|
// /////////////////////////////////
|
||||||
// === verify miner's eligibility to propose this task ===
|
//
|
||||||
err = cv.miner.IsMinerEligibleToProposeTask(common.HexToAddress(msg.Task.MinerEth))
|
// // === verify miner's eligibility to propose this task ===
|
||||||
if err != nil {
|
// err = cv.miner.IsMinerEligibleToProposeBlock(common.HexToAddress(msg.Task.MinerEth))
|
||||||
logrus.Errorf("miner is not eligible to propose task: %v", err)
|
// if err != nil {
|
||||||
return false
|
// logrus.Errorf("miner is not eligible to propose task: %v", err)
|
||||||
}
|
// return false
|
||||||
/////////////////////////////////
|
// }
|
||||||
|
// /////////////////////////////////
|
||||||
// === verify election proof vrf ===
|
//
|
||||||
minerAddressMarshalled, err := msg.Task.Miner.MarshalBinary()
|
// // === verify election proof vrf ===
|
||||||
if err != nil {
|
// minerAddressMarshalled, err := msg.Task.Miner.MarshalBinary()
|
||||||
logrus.Errorf("failed to marshal miner address: %v", err)
|
// if err != nil {
|
||||||
return false
|
// logrus.Errorf("failed to marshal miner address: %v", err)
|
||||||
}
|
// return false
|
||||||
electionProofRandomness, err := DrawRandomness(
|
// }
|
||||||
msg.Task.BeaconEntries[1].Data,
|
// electionProofRandomness, err := DrawRandomness(
|
||||||
crypto.DomainSeparationTag_ElectionProofProduction,
|
// msg.Task.BeaconEntries[1].Data,
|
||||||
msg.Task.DrandRound,
|
// crypto.DomainSeparationTag_ElectionProofProduction,
|
||||||
minerAddressMarshalled,
|
// msg.Task.DrandRound,
|
||||||
)
|
// minerAddressMarshalled,
|
||||||
if err != nil {
|
// )
|
||||||
logrus.Errorf("failed to draw electionProofRandomness: %v", err)
|
// if err != nil {
|
||||||
return false
|
// logrus.Errorf("failed to draw electionProofRandomness: %v", err)
|
||||||
}
|
// return false
|
||||||
err = VerifyVRF(msg.Task.Miner, electionProofRandomness, msg.Task.ElectionProof.VRFProof)
|
// }
|
||||||
if err != nil {
|
// err = VerifyVRF(msg.Task.Miner, electionProofRandomness, msg.Task.ElectionProof.VRFProof)
|
||||||
logrus.Errorf("failed to verify election proof vrf: %v", err)
|
// if err != nil {
|
||||||
}
|
// logrus.Errorf("failed to verify election proof vrf: %v", err)
|
||||||
//////////////////////////////////////
|
// }
|
||||||
|
// //////////////////////////////////////
|
||||||
// === verify ticket vrf ===
|
//
|
||||||
ticketRandomness, err := DrawRandomness(
|
// // === compute wincount locally and verify values ===
|
||||||
msg.Task.BeaconEntries[1].Data,
|
// mStake, nStake, err := cv.miner.GetStakeInfo(common.HexToAddress(msg.Task.MinerEth))
|
||||||
crypto.DomainSeparationTag_TicketProduction,
|
// if err != nil {
|
||||||
msg.Task.DrandRound-types.TicketRandomnessLookback,
|
// logrus.Errorf("failed to get miner stake: %v", err)
|
||||||
minerAddressMarshalled,
|
// return false
|
||||||
)
|
// }
|
||||||
if err != nil {
|
// actualWinCount := msg.Task.ElectionProof.ComputeWinCount(*mStake, *nStake)
|
||||||
logrus.Errorf("failed to draw ticket electionProofRandomness: %v", err)
|
// if msg.Task.ElectionProof.WinCount != actualWinCount {
|
||||||
return false
|
// logrus.Errorf("locally computed wincount isn't matching received value!", err)
|
||||||
}
|
// return false
|
||||||
|
// }
|
||||||
err = VerifyVRF(msg.Task.Miner, ticketRandomness, msg.Task.Ticket.VRFProof)
|
// //////////////////////////////////////
|
||||||
if err != nil {
|
//
|
||||||
logrus.Errorf("failed to verify ticket vrf: %v", err)
|
// // === validate payload by specific-chain checks ===
|
||||||
}
|
// if validationFunc := validation.GetValidationMethod(msg.Task.OriginChain, msg.Task.RequestType); validationFunc != nil {
|
||||||
//////////////////////////////////////
|
// err := validationFunc(msg.Task.Payload)
|
||||||
|
// if err != nil {
|
||||||
// === compute wincount locally and verify values ===
|
// logrus.Errorf("payload validation has failed: %v", err)
|
||||||
mStake, nStake, err := cv.miner.GetStakeInfo(common.HexToAddress(msg.Task.MinerEth))
|
// return false
|
||||||
if err != nil {
|
// }
|
||||||
logrus.Errorf("failed to get miner stake: %v", err)
|
// } else {
|
||||||
return false
|
// logrus.Debugf("Origin chain [%v]/request type[%v] doesn't have any payload validation!", msg.Task.OriginChain, msg.Task.RequestType)
|
||||||
}
|
// }
|
||||||
actualWinCount := msg.Task.ElectionProof.ComputeWinCount(*mStake, *nStake)
|
// /////////////////////////////////
|
||||||
if msg.Task.ElectionProof.WinCount != actualWinCount {
|
//
|
||||||
logrus.Errorf("locally computed wincount isn't matching received value!", err)
|
// return true
|
||||||
return false
|
//},
|
||||||
}
|
//types2.ConsensusMessageTypePrepare: func(msg types2.PrePrepareMessage) bool {
|
||||||
//////////////////////////////////////
|
// err := VerifyTaskSignature(msg.Task)
|
||||||
|
// if err != nil {
|
||||||
// === validate payload by specific-chain checks ===
|
// return false
|
||||||
if validationFunc := validation.GetValidationMethod(msg.Task.OriginChain, msg.Task.RequestType); validationFunc != nil {
|
// }
|
||||||
err := validationFunc(msg.Task.Payload)
|
// return true
|
||||||
if err != nil {
|
//},
|
||||||
logrus.Errorf("payload validation has failed: %v", err)
|
//types2.ConsensusMessageTypeCommit: func(msg types2.PrePrepareMessage) bool {
|
||||||
return false
|
// err := VerifyTaskSignature(msg.Task)
|
||||||
}
|
// if err != nil {
|
||||||
} else {
|
// return false
|
||||||
logrus.Debugf("Origin chain [%v]/request type[%v] doesn't have any payload validation!", msg.Task.OriginChain, msg.Task.RequestType)
|
// }
|
||||||
}
|
// return true
|
||||||
/////////////////////////////////
|
//},
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
types2.MessageTypePrepare: func(msg types2.ConsensusMessage) bool {
|
|
||||||
err := VerifyTaskSignature(msg.Task)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
types2.MessageTypeCommit: func(msg types2.ConsensusMessage) bool {
|
|
||||||
err := VerifyTaskSignature(msg.Task)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cv
|
return cv
|
||||||
|
@ -2,17 +2,11 @@ package consensus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/contracts/dioneDispute"
|
"github.com/Secured-Finance/dione/contracts/dioneDispute"
|
||||||
"github.com/Secured-Finance/dione/contracts/dioneOracle"
|
"github.com/Secured-Finance/dione/contracts/dioneOracle"
|
||||||
"github.com/Secured-Finance/dione/ethclient"
|
"github.com/Secured-Finance/dione/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DisputeManager struct {
|
type DisputeManager struct {
|
||||||
@ -69,92 +63,94 @@ func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dm *DisputeManager) onNewSubmission(submittion *dioneOracle.DioneOracleSubmittedOracleRequest) {
|
func (dm *DisputeManager) onNewSubmission(submittion *dioneOracle.DioneOracleSubmittedOracleRequest) {
|
||||||
c := dm.pcm.GetConsensusInfo(submittion.ReqID.String())
|
//c := dm.pcm.GetConsensusInfo(submittion.ReqID.String())
|
||||||
if c == nil {
|
//if c == nil {
|
||||||
// todo: warn
|
// // todo: warn
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
dm.submissionMap[submittion.ReqID.String()] = submittion
|
//dm.submissionMap[submittion.ReqID.String()] = submittion
|
||||||
|
//
|
||||||
submHashBytes := sha3.Sum256(submittion.Data)
|
//submHashBytes := sha3.Sum256(submittion.Data)
|
||||||
localHashBytes := sha3.Sum256(c.Task.Payload)
|
//localHashBytes := sha3.Sum256(c.Task.Payload)
|
||||||
submHash := hex.EncodeToString(submHashBytes[:])
|
//submHash := hex.EncodeToString(submHashBytes[:])
|
||||||
localHash := hex.EncodeToString(localHashBytes[:])
|
//localHash := hex.EncodeToString(localHashBytes[:])
|
||||||
if submHash != localHash {
|
//if submHash != localHash {
|
||||||
logrus.Debugf("submission of request id %s isn't valid - beginning dispute", c.Task.RequestID)
|
// logrus.Debugf("submission of request id %s isn't valid - beginning dispute", c.Task.RequestID)
|
||||||
addr := common.HexToAddress(c.Task.MinerEth)
|
// addr := common.HexToAddress(c.Task.MinerEth)
|
||||||
reqID, ok := big.NewInt(0).SetString(c.Task.RequestID, 10)
|
// reqID, ok := big.NewInt(0).SetString(c.Task.RequestID, 10)
|
||||||
if !ok {
|
// if !ok {
|
||||||
logrus.Errorf("cannot parse request id: %s", c.Task.RequestID)
|
// logrus.Errorf("cannot parse request id: %s", c.Task.RequestID)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
err := dm.ethClient.BeginDispute(addr, reqID)
|
// err := dm.ethClient.BeginDispute(addr, reqID)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
logrus.Errorf(err.Error())
|
// logrus.Errorf(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
disputeFinishTimer := time.NewTimer(dm.voteWindow)
|
// disputeFinishTimer := time.NewTimer(dm.voteWindow)
|
||||||
go func() {
|
// go func() {
|
||||||
for {
|
// for {
|
||||||
select {
|
// select {
|
||||||
case <-dm.ctx.Done():
|
// case <-dm.ctx.Done():
|
||||||
return
|
// return
|
||||||
case <-disputeFinishTimer.C:
|
// case <-disputeFinishTimer.C:
|
||||||
{
|
// {
|
||||||
d, ok := dm.disputeMap[reqID.String()]
|
// d, ok := dm.disputeMap[reqID.String()]
|
||||||
if !ok {
|
// if !ok {
|
||||||
logrus.Error("cannot finish dispute: it doesn't exist in manager's dispute map!")
|
// logrus.Error("cannot finish dispute: it doesn't exist in manager's dispute map!")
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
err := dm.ethClient.FinishDispute(d.Dhash)
|
// err := dm.ethClient.FinishDispute(d.Dhash)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
logrus.Errorf(err.Error())
|
// logrus.Errorf(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
disputeFinishTimer.Stop()
|
// disputeFinishTimer.Stop()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}()
|
// }()
|
||||||
}
|
//}
|
||||||
|
// TODO refactor due to new architecture with blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dm *DisputeManager) onNewDispute(dispute *dioneDispute.DioneDisputeNewDispute) {
|
func (dm *DisputeManager) onNewDispute(dispute *dioneDispute.DioneDisputeNewDispute) {
|
||||||
c := dm.pcm.GetConsensusInfo(dispute.RequestID.String())
|
//c := dm.pcm.GetConsensusInfo(dispute.RequestID.String())
|
||||||
if c == nil {
|
//if c == nil {
|
||||||
// todo: warn
|
// // todo: warn
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
subm, ok := dm.submissionMap[dispute.RequestID.String()]
|
//subm, ok := dm.submissionMap[dispute.RequestID.String()]
|
||||||
if !ok {
|
//if !ok {
|
||||||
// todo: warn
|
// // todo: warn
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
dm.disputeMap[dispute.RequestID.String()] = dispute
|
//dm.disputeMap[dispute.RequestID.String()] = dispute
|
||||||
|
//
|
||||||
if dispute.DisputeInitiator.Hex() == dm.ethClient.GetEthAddress().Hex() {
|
//if dispute.DisputeInitiator.Hex() == dm.ethClient.GetEthAddress().Hex() {
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
submHashBytes := sha3.Sum256(subm.Data)
|
//submHashBytes := sha3.Sum256(subm.Data)
|
||||||
localHashBytes := sha3.Sum256(c.Task.Payload)
|
//localHashBytes := sha3.Sum256(c.Task.Payload)
|
||||||
submHash := hex.EncodeToString(submHashBytes[:])
|
//submHash := hex.EncodeToString(submHashBytes[:])
|
||||||
localHash := hex.EncodeToString(localHashBytes[:])
|
//localHash := hex.EncodeToString(localHashBytes[:])
|
||||||
if submHash == localHash {
|
//if submHash == localHash {
|
||||||
err := dm.ethClient.VoteDispute(dispute.Dhash, false)
|
// err := dm.ethClient.VoteDispute(dispute.Dhash, false)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
logrus.Errorf(err.Error())
|
// logrus.Errorf(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
err := dm.ethClient.VoteDispute(dispute.Dhash, true)
|
//err := dm.ethClient.VoteDispute(dispute.Dhash, true)
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
logrus.Errorf(err.Error())
|
// logrus.Errorf(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
// TODO refactor due to new architecture with blockchain
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
big2 "github.com/filecoin-project/go-state-types/big"
|
"github.com/Secured-Finance/dione/blockchain/pool"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/sigs"
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/rpc"
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/beacon"
|
"github.com/Secured-Finance/dione/beacon"
|
||||||
"github.com/Secured-Finance/dione/contracts/dioneOracle"
|
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/ethclient"
|
"github.com/Secured-Finance/dione/ethclient"
|
||||||
"github.com/Secured-Finance/dione/types"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/filecoin-project/go-state-types/crypto"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/xerrors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Miner struct {
|
type Miner struct {
|
||||||
@ -28,9 +26,10 @@ type Miner struct {
|
|||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
beacon beacon.BeaconNetworks
|
beacon beacon.BeaconNetworks
|
||||||
ethClient *ethclient.EthereumClient
|
ethClient *ethclient.EthereumClient
|
||||||
minerStake types.BigInt
|
minerStake *big.Int
|
||||||
networkStake types.BigInt
|
networkStake *big.Int
|
||||||
privateKey []byte
|
privateKey crypto.PrivKey
|
||||||
|
mempool *pool.Mempool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMiner(
|
func NewMiner(
|
||||||
@ -38,7 +37,8 @@ func NewMiner(
|
|||||||
ethAddress common.Address,
|
ethAddress common.Address,
|
||||||
beacon beacon.BeaconNetworks,
|
beacon beacon.BeaconNetworks,
|
||||||
ethClient *ethclient.EthereumClient,
|
ethClient *ethclient.EthereumClient,
|
||||||
privateKey []byte,
|
privateKey crypto.PrivKey,
|
||||||
|
mempool *pool.Mempool,
|
||||||
) *Miner {
|
) *Miner {
|
||||||
return &Miner{
|
return &Miner{
|
||||||
address: address,
|
address: address,
|
||||||
@ -46,6 +46,7 @@ func NewMiner(
|
|||||||
beacon: beacon,
|
beacon: beacon,
|
||||||
ethClient: ethClient,
|
ethClient: ethClient,
|
||||||
privateKey: privateKey,
|
privateKey: privateKey,
|
||||||
|
mempool: mempool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,13 +65,13 @@ func (m *Miner) UpdateCurrentStakeInfo() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.minerStake = *mStake
|
m.minerStake = mStake
|
||||||
m.networkStake = *nStake
|
m.networkStake = nStake
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Miner) GetStakeInfo(miner common.Address) (*types.BigInt, *types.BigInt, error) {
|
func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, error) {
|
||||||
mStake, err := m.ethClient.GetMinerStake(miner)
|
mStake, err := m.ethClient.GetMinerStake(miner)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -88,100 +89,59 @@ func (m *Miner) GetStakeInfo(miner common.Address) (*types.BigInt, *types.BigInt
|
|||||||
return mStake, nStake, nil
|
return mStake, nStake, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Miner) MineTask(ctx context.Context, event *dioneOracle.DioneOracleNewOracleRequest) (*types.DioneTask, error) {
|
func (m *Miner) MineBlock(randomness []byte, drandRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) {
|
||||||
beaconValues, err := beacon.BeaconEntriesForTask(ctx, m.beacon)
|
logrus.Debug("attempting to mine the block at epoch: ", drandRound)
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to get beacon entries: %w", err)
|
|
||||||
}
|
|
||||||
logrus.Debug("attempting to mine the task at epoch: ", beaconValues[1].Round)
|
|
||||||
|
|
||||||
randomBase := beaconValues[1]
|
|
||||||
|
|
||||||
if err := m.UpdateCurrentStakeInfo(); err != nil {
|
if err := m.UpdateCurrentStakeInfo(); err != nil {
|
||||||
return nil, xerrors.Errorf("failed to update miner stake: %w", err)
|
return nil, fmt.Errorf("failed to update miner stake: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
ticket, err := m.computeTicket(&randomBase)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("scratching ticket failed: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
winner, err := IsRoundWinner(
|
winner, err := IsRoundWinner(
|
||||||
types.DrandRound(randomBase.Round),
|
drandRound,
|
||||||
m.address,
|
m.address,
|
||||||
randomBase,
|
randomness,
|
||||||
m.minerStake,
|
m.minerStake,
|
||||||
m.networkStake,
|
m.networkStake,
|
||||||
func(id peer.ID, bytes []byte) (*types.Signature, error) {
|
m.privateKey,
|
||||||
return sigs.Sign(types.SigTypeEd25519, m.privateKey, bytes)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to check if we win next round: %w", err)
|
return nil, fmt.Errorf("failed to check if we winned in next round: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if winner == nil {
|
if winner == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcMethod := rpc.GetRPCMethod(event.OriginChain, event.RequestType)
|
// TODO get rpc responses for oracle requests
|
||||||
if rpcMethod == nil {
|
//rpcMethod := rpc.GetRPCMethod(event.OriginChain, event.RequestType)
|
||||||
return nil, xerrors.Errorf("invalid rpc method name/type")
|
//if rpcMethod == nil {
|
||||||
|
// return nil, xerrors.Errorf("invalid rpc method name/type")
|
||||||
|
//}
|
||||||
|
//res, err := rpcMethod(event.RequestParams)
|
||||||
|
//if err != nil {
|
||||||
|
// return nil, xerrors.Errorf("couldn't do rpc request: %w", err)
|
||||||
|
//}
|
||||||
|
|
||||||
|
txs := m.mempool.GetAllTransactions()
|
||||||
|
if txs == nil {
|
||||||
|
return nil, fmt.Errorf("there is no txes for processing") // skip new consensus round because there is no transaction for processing
|
||||||
}
|
}
|
||||||
res, err := rpcMethod(event.RequestParams)
|
|
||||||
|
newBlock, err := types2.CreateBlock(lastBlockHeader, txs, m.ethAddress, m.privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("couldn't do rpc request: %w", err)
|
return nil, fmt.Errorf("failed to create new block: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.DioneTask{
|
return newBlock, nil
|
||||||
OriginChain: event.OriginChain,
|
|
||||||
RequestType: event.RequestType,
|
|
||||||
RequestParams: event.RequestParams,
|
|
||||||
RequestID: event.ReqID.String(),
|
|
||||||
ConsensusID: event.ReqID.String(),
|
|
||||||
Miner: m.address,
|
|
||||||
MinerEth: m.ethAddress.Hex(),
|
|
||||||
Ticket: ticket,
|
|
||||||
ElectionProof: winner,
|
|
||||||
BeaconEntries: beaconValues,
|
|
||||||
Payload: res,
|
|
||||||
DrandRound: types.DrandRound(randomBase.Round),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Miner) computeTicket(brand *types.BeaconEntry) (*types.Ticket, error) {
|
func (m *Miner) IsMinerEligibleToProposeBlock(ethAddress common.Address) error {
|
||||||
buf, err := m.address.MarshalBinary()
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to marshal address: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
round := types.DrandRound(brand.Round)
|
|
||||||
|
|
||||||
input, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_TicketProduction, round-types.TicketRandomnessLookback, buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
vrfOut, err := ComputeVRF(func(id peer.ID, bytes []byte) (*types.Signature, error) {
|
|
||||||
return sigs.Sign(types.SigTypeEd25519, m.privateKey, bytes)
|
|
||||||
}, m.address, input)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.Ticket{
|
|
||||||
VRFProof: vrfOut,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Miner) IsMinerEligibleToProposeTask(ethAddress common.Address) error {
|
|
||||||
mStake, err := m.ethClient.GetMinerStake(ethAddress)
|
mStake, err := m.ethClient.GetMinerStake(ethAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ok := mStake.GreaterThanEqual(big2.NewInt(ethclient.MinMinerStake))
|
if mStake.Cmp(big.NewInt(ethclient.MinMinerStake)) == -1 {
|
||||||
if !ok {
|
return errors.New("miner doesn't have enough staked tokens")
|
||||||
return xerrors.Errorf("miner doesn't have enough staked tokens")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
types2 "github.com/Secured-Finance/dione/consensus/types"
|
types2 "github.com/Secured-Finance/dione/consensus/types"
|
||||||
mapset "github.com/Secured-Finance/golang-set"
|
mapset "github.com/Secured-Finance/golang-set"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageLog struct {
|
type ConsensusMessageLog struct {
|
||||||
messages mapset.Set
|
messages mapset.Set
|
||||||
maxLogSize int
|
maxLogSize int
|
||||||
validationFuncMap map[types2.MessageType]func(message types2.ConsensusMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessageLog() *MessageLog {
|
func NewConsensusMessageLog() *ConsensusMessageLog {
|
||||||
msgLog := &MessageLog{
|
msgLog := &ConsensusMessageLog{
|
||||||
messages: mapset.NewSet(),
|
messages: mapset.NewSet(),
|
||||||
maxLogSize: 0, // TODO
|
maxLogSize: 0, // TODO
|
||||||
}
|
}
|
||||||
@ -20,23 +21,34 @@ func NewMessageLog() *MessageLog {
|
|||||||
return msgLog
|
return msgLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ml *MessageLog) AddMessage(msg types2.ConsensusMessage) {
|
func (ml *ConsensusMessageLog) AddMessage(msg types2.ConsensusMessage) {
|
||||||
ml.messages.Add(msg)
|
ml.messages.Add(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ml *MessageLog) Exists(msg types2.ConsensusMessage) bool {
|
func (ml *ConsensusMessageLog) Exists(msg types2.ConsensusMessage) bool {
|
||||||
return ml.messages.Contains(msg)
|
return ml.messages.Contains(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ml *MessageLog) Get(typ types2.MessageType, consensusID string) []*types2.ConsensusMessage {
|
func (ml *ConsensusMessageLog) Get(typ types2.ConsensusMessageType, blockhash []byte) []*types2.ConsensusMessage {
|
||||||
var result []*types2.ConsensusMessage
|
var result []*types2.ConsensusMessage
|
||||||
|
|
||||||
for v := range ml.messages.Iter() {
|
for v := range ml.messages.Iter() {
|
||||||
msg := v.(types2.ConsensusMessage)
|
msg := v.(types2.ConsensusMessage)
|
||||||
if msg.Type == typ && msg.Task.ConsensusID == consensusID {
|
if msg.Block != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
if msg.Type == typ {
|
||||||
|
var msgBlockHash []byte
|
||||||
|
if msg.Block != nil {
|
||||||
|
msgBlockHash = msg.Block.Header.Hash
|
||||||
|
} else {
|
||||||
|
msgBlockHash = msg.Blockhash
|
||||||
|
}
|
||||||
|
if bytes.Compare(msgBlockHash, blockhash) == 0 {
|
||||||
result = append(result, &msg)
|
result = append(result, &msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
25
consensus/types/consensus_message.go
Normal file
25
consensus/types/consensus_message.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
types3 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConsensusMessageType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConsensusMessageTypeUnknown = ConsensusMessageType(iota)
|
||||||
|
|
||||||
|
ConsensusMessageTypePrePrepare
|
||||||
|
ConsensusMessageTypePrepare
|
||||||
|
ConsensusMessageTypeCommit
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConsensusMessage is common struct for various consensus message types. It is stored in consensus message log.
|
||||||
|
type ConsensusMessage struct {
|
||||||
|
Type ConsensusMessageType
|
||||||
|
Blockhash []byte
|
||||||
|
Signature []byte
|
||||||
|
Block *types3.Block // it is optional, because not all message types have block included
|
||||||
|
From peer.ID
|
||||||
|
}
|
@ -1,22 +1,16 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Secured-Finance/dione/types"
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageType uint8
|
type PrePrepareMessage struct {
|
||||||
|
Block *types2.Block
|
||||||
const (
|
|
||||||
MessageTypeUnknown = MessageType(iota)
|
|
||||||
|
|
||||||
MessageTypePrePrepare
|
|
||||||
MessageTypePrepare
|
|
||||||
MessageTypeCommit
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConsensusMessage struct {
|
|
||||||
Task types.DioneTask
|
|
||||||
From peer.ID
|
|
||||||
Type MessageType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PrepareMessage struct {
|
||||||
|
Blockhash []byte
|
||||||
|
Signature []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommitMessage PrepareMessage
|
||||||
|
@ -3,8 +3,7 @@ package consensus
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"github.com/fxamacker/cbor/v2"
|
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/pubsub"
|
"github.com/Secured-Finance/dione/pubsub"
|
||||||
|
|
||||||
@ -18,23 +17,15 @@ import (
|
|||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/types"
|
"github.com/Secured-Finance/dione/types"
|
||||||
"github.com/filecoin-project/go-state-types/crypto"
|
crypto2 "github.com/filecoin-project/go-state-types/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SignFunc func(peer.ID, []byte) (*types.Signature, error)
|
type SignFunc func(peer.ID, []byte) (*types.Signature, error)
|
||||||
|
|
||||||
func ComputeVRF(sign SignFunc, worker peer.ID, sigInput []byte) ([]byte, error) {
|
func ComputeVRF(privKey crypto.PrivKey, sigInput []byte) ([]byte, error) {
|
||||||
sig, err := sign(worker, sigInput)
|
return privKey.Sign(sigInput)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if sig.Type != types.SigTypeEd25519 {
|
|
||||||
return nil, fmt.Errorf("miner worker address was not a Ed25519 key")
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig.Data, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error {
|
func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error {
|
||||||
@ -46,20 +37,20 @@ func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsRoundWinner(round types.DrandRound,
|
func IsRoundWinner(round uint64,
|
||||||
worker peer.ID, brand types.BeaconEntry, minerStake, networkStake types.BigInt, sign SignFunc) (*types.ElectionProof, error) {
|
worker peer.ID, randomness []byte, minerStake, networkStake *big.Int, privKey crypto.PrivKey) (*types.ElectionProof, error) {
|
||||||
|
|
||||||
buf, err := worker.MarshalBinary()
|
buf, err := worker.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to marshal address: %w", err)
|
return nil, xerrors.Errorf("failed to marshal address: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
electionRand, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf)
|
electionRand, err := DrawRandomness(randomness, crypto2.DomainSeparationTag_ElectionProofProduction, round, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
|
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
vrfout, err := ComputeVRF(sign, worker, electionRand)
|
vrfout, err := ComputeVRF(privKey, electionRand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
|
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
|
||||||
}
|
}
|
||||||
@ -74,7 +65,7 @@ func IsRoundWinner(round types.DrandRound,
|
|||||||
return ep, nil
|
return ep, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round types.DrandRound, entropy []byte) ([]byte, error) {
|
func DrawRandomness(rbase []byte, pers crypto2.DomainSeparationTag, round uint64, entropy []byte) ([]byte, error) {
|
||||||
h := blake2b.New256()
|
h := blake2b.New256()
|
||||||
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
|
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
|
||||||
return nil, xerrors.Errorf("deriving randomness: %v", err)
|
return nil, xerrors.Errorf("deriving randomness: %v", err)
|
||||||
@ -111,31 +102,46 @@ func VerifyTaskSignature(task types.DioneTask) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessage(msg *pubsub.GenericMessage, typ pubsub.PubSubMessageType) (pubsub.GenericMessage, error) {
|
func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, privKey crypto.PrivKey) (*pubsub.GenericMessage, error) {
|
||||||
var newMsg pubsub.GenericMessage
|
|
||||||
newMsg.Type = typ
|
|
||||||
newCMsg := msg.Payload
|
|
||||||
newMsg.Payload = newCMsg
|
|
||||||
return newMsg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreatePrePrepareWithTaskSignature(task *types.DioneTask, privateKey []byte) (*pubsub.GenericMessage, error) {
|
|
||||||
var message pubsub.GenericMessage
|
var message pubsub.GenericMessage
|
||||||
|
switch typ {
|
||||||
|
case types2.ConsensusMessageTypePrePrepare:
|
||||||
|
{
|
||||||
message.Type = pubsub.PrePrepareMessageType
|
message.Type = pubsub.PrePrepareMessageType
|
||||||
|
message.Payload = types2.PrePrepareMessage{
|
||||||
|
Block: cmsg.Block,
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case types2.ConsensusMessageTypePrepare:
|
||||||
|
{
|
||||||
|
message.Type = pubsub.PrepareMessageType
|
||||||
|
pm := types2.PrepareMessage{
|
||||||
|
Blockhash: cmsg.Blockhash,
|
||||||
|
}
|
||||||
|
signature, err := privKey.Sign(cmsg.Blockhash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create signature: %v", err)
|
||||||
|
}
|
||||||
|
pm.Signature = signature
|
||||||
|
message.Payload = pm
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case types2.ConsensusMessageTypeCommit:
|
||||||
|
{
|
||||||
|
message.Type = pubsub.CommitMessageType
|
||||||
|
pm := types2.CommitMessage{
|
||||||
|
Blockhash: cmsg.Blockhash,
|
||||||
|
}
|
||||||
|
signature, err := privKey.Sign(cmsg.Blockhash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create signature: %v", err)
|
||||||
|
}
|
||||||
|
pm.Signature = signature
|
||||||
|
message.Payload = pm
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cHash, err := hashstructure.Hash(task, hashstructure.FormatV2, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
signature, err := sigs.Sign(types.SigTypeEd25519, privateKey, []byte(fmt.Sprintf("%v", cHash)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
task.Signature = signature.Data
|
|
||||||
data, err := cbor.Marshal(types2.ConsensusMessage{Task: *task})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
message.Payload = data
|
|
||||||
return &message, nil
|
return &message, nil
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package ethclient
|
package ethclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Secured-Finance/dione/types"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -9,32 +10,22 @@ const (
|
|||||||
MinMinerStake = 1000
|
MinMinerStake = 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Getting total stake in DioneStaking contract, this function could
|
// GetTotalStake for getting total stake in DioneStaking contract
|
||||||
// be used for storing the total stake and veryfing the stake tokens
|
func (c *EthereumClient) GetTotalStake() (*big.Int, error) {
|
||||||
// on new tasks
|
|
||||||
func (c *EthereumClient) GetTotalStake() (*types.BigInt, error) {
|
|
||||||
var b types.BigInt
|
|
||||||
totalStake, err := c.dioneStaking.TotalStake()
|
totalStake, err := c.dioneStaking.TotalStake()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Int = totalStake
|
return totalStake, nil
|
||||||
return &b, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getting miner stake in DioneStaking contract, this function could
|
// GetMinerStake for getting specified miner stake in DioneStaking contract
|
||||||
// be used for storing the miner's stake and veryfing the stake tokens
|
func (c *EthereumClient) GetMinerStake(minerAddress common.Address) (*big.Int, error) {
|
||||||
// on new tasks
|
|
||||||
func (c *EthereumClient) GetMinerStake(minerAddress common.Address) (*types.BigInt, error) {
|
|
||||||
var b types.BigInt
|
|
||||||
minerStake, err := c.dioneStaking.MinerStake(minerAddress)
|
minerStake, err := c.dioneStaking.MinerStake(minerAddress)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Int = minerStake
|
return minerStake, nil
|
||||||
return &b, nil
|
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module github.com/Secured-Finance/dione
|
|||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 // indirect
|
||||||
github.com/Secured-Finance/go-libp2p-pex v1.1.0
|
github.com/Secured-Finance/go-libp2p-pex v1.1.0
|
||||||
github.com/Secured-Finance/golang-set v1.8.0
|
github.com/Secured-Finance/golang-set v1.8.0
|
||||||
github.com/aristanetworks/goarista v0.0.0-20210308203447-b196d8410f1d // indirect
|
github.com/aristanetworks/goarista v0.0.0-20210308203447-b196d8410f1d // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -28,6 +28,8 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
|
|||||||
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
|
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
|
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ=
|
||||||
|
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI=
|
||||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||||
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
||||||
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
|
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
|
||||||
|
78
node/node.go
78
node/node.go
@ -13,15 +13,12 @@ import (
|
|||||||
|
|
||||||
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
|
||||||
"github.com/fxamacker/cbor/v2"
|
|
||||||
|
|
||||||
gorpc "github.com/libp2p/go-libp2p-gorpc"
|
gorpc "github.com/libp2p/go-libp2p-gorpc"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/blockchain/pool"
|
"github.com/Secured-Finance/dione/blockchain/pool"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/blockchain/sync"
|
"github.com/Secured-Finance/dione/blockchain/sync"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/cache"
|
|
||||||
"github.com/Secured-Finance/dione/consensus"
|
"github.com/Secured-Finance/dione/consensus"
|
||||||
pubsub2 "github.com/Secured-Finance/dione/pubsub"
|
pubsub2 "github.com/Secured-Finance/dione/pubsub"
|
||||||
|
|
||||||
@ -34,8 +31,6 @@ import (
|
|||||||
|
|
||||||
"github.com/Secured-Finance/dione/rpc/filecoin"
|
"github.com/Secured-Finance/dione/rpc/filecoin"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/wallet"
|
|
||||||
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/beacon"
|
"github.com/Secured-Finance/dione/beacon"
|
||||||
@ -62,14 +57,14 @@ type Node struct {
|
|||||||
ConsensusManager *consensus.PBFTConsensusManager
|
ConsensusManager *consensus.PBFTConsensusManager
|
||||||
Miner *consensus.Miner
|
Miner *consensus.Miner
|
||||||
Beacon beacon.BeaconNetworks
|
Beacon beacon.BeaconNetworks
|
||||||
Wallet *wallet.LocalWallet
|
|
||||||
Cache cache.Cache
|
|
||||||
DisputeManager *consensus.DisputeManager
|
DisputeManager *consensus.DisputeManager
|
||||||
BlockPool *blockchain.BlockChain
|
BlockPool *blockchain.BlockChain
|
||||||
MemPool *pool.Mempool
|
MemPool *pool.Mempool
|
||||||
SyncManager sync.SyncManager
|
SyncManager sync.SyncManager
|
||||||
NetworkService *NetworkService
|
NetworkService *NetworkService
|
||||||
NetworkRPCHost *gorpc.Server
|
NetworkRPCHost *gorpc.Server
|
||||||
|
//Cache cache.Cache
|
||||||
|
//Wallet *wallet.LocalWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTime time.Duration) (*Node, error) {
|
func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTime time.Duration) (*Node, error) {
|
||||||
@ -119,24 +114,10 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim
|
|||||||
n.PeerDiscovery = peerDiscovery
|
n.PeerDiscovery = peerDiscovery
|
||||||
logrus.Info("Peer discovery subsystem has been initialized!")
|
logrus.Info("Peer discovery subsystem has been initialized!")
|
||||||
|
|
||||||
// get private key of libp2p host
|
|
||||||
rawPrivKey, err := prvKey.Raw()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize random beacon network subsystem
|
|
||||||
randomBeaconNetwork, err := provideBeacon(psb.Pubsub)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
n.Beacon = randomBeaconNetwork
|
|
||||||
logrus.Info("Random beacon subsystem has been initialized!")
|
|
||||||
|
|
||||||
// initialize event log cache subsystem
|
// initialize event log cache subsystem
|
||||||
c := provideCache(config)
|
//c := provideCache(config)
|
||||||
n.Cache = c
|
//n.Cache = c
|
||||||
logrus.Info("Event cache subsystem has initialized!")
|
//logrus.Info("Event cache subsystem has initialized!")
|
||||||
|
|
||||||
// == initialize blockchain modules
|
// == initialize blockchain modules
|
||||||
|
|
||||||
@ -177,17 +158,25 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim
|
|||||||
logrus.Info("Blockchain synchronization subsystem has been successfully initialized!")
|
logrus.Info("Blockchain synchronization subsystem has been successfully initialized!")
|
||||||
|
|
||||||
// initialize mining subsystem
|
// initialize mining subsystem
|
||||||
miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Beacon, n.Ethereum, rawPrivKey)
|
miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Beacon, n.Ethereum, prvKey, mp)
|
||||||
n.Miner = miner
|
n.Miner = miner
|
||||||
logrus.Info("Mining subsystem has initialized!")
|
logrus.Info("Mining subsystem has initialized!")
|
||||||
|
|
||||||
// initialize consensus subsystem
|
// initialize consensus subsystem
|
||||||
cManager := provideConsensusManager(psb, miner, ethClient, rawPrivKey, n.Config.ConsensusMinApprovals, c)
|
consensusManager := provideConsensusManager(psb, miner, ethClient, prvKey, n.Config.ConsensusMinApprovals)
|
||||||
n.ConsensusManager = cManager
|
n.ConsensusManager = consensusManager
|
||||||
logrus.Info("Consensus subsystem has initialized!")
|
logrus.Info("Consensus subsystem has initialized!")
|
||||||
|
|
||||||
|
// initialize random beacon network subsystem
|
||||||
|
randomBeaconNetwork, err := provideBeacon(psb.Pubsub, consensusManager)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
n.Beacon = randomBeaconNetwork
|
||||||
|
logrus.Info("Random beacon subsystem has been initialized!")
|
||||||
|
|
||||||
// initialize dispute subsystem
|
// initialize dispute subsystem
|
||||||
disputeManager, err := provideDisputeManager(context.TODO(), ethClient, cManager, config)
|
disputeManager, err := provideDisputeManager(context.TODO(), ethClient, consensusManager, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -195,11 +184,11 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim
|
|||||||
logrus.Info("Dispute subsystem has initialized!")
|
logrus.Info("Dispute subsystem has initialized!")
|
||||||
|
|
||||||
// initialize internal eth wallet
|
// initialize internal eth wallet
|
||||||
w, err := provideWallet(n.Host.ID(), rawPrivKey)
|
//w, err := provideWallet(n.Host.ID(), rawPrivKey)
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
logrus.Fatal(err)
|
// logrus.Fatal(err)
|
||||||
}
|
//}
|
||||||
n.Wallet = w
|
//n.Wallet = w
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
@ -272,35 +261,18 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) {
|
|||||||
EventLoop:
|
EventLoop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-eventChan:
|
case <-eventChan:
|
||||||
{
|
{
|
||||||
logrus.Info("Let's wait a little so that all nodes have time to receive the request")
|
logrus.Info("Let's wait a little so that all nodes have time to receive the request")
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
task, err := n.Miner.MineTask(context.TODO(), event)
|
// TODO make the rpc request and save response as tx payload
|
||||||
if err != nil {
|
tx := types2.CreateTransaction([]byte{})
|
||||||
logrus.Errorf("Failed to mine task: %v", err)
|
|
||||||
}
|
|
||||||
if task == nil {
|
|
||||||
logrus.Warnf("Task is nil!")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
payload, err := cbor.Marshal(task)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Failed to marshal request event")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tx := types2.CreateTransaction(payload)
|
|
||||||
err = n.MemPool.StoreTx(tx)
|
err = n.MemPool.StoreTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Failed to store tx in mempool: %s", err.Error())
|
logrus.Errorf("Failed to store tx in mempool: %s", err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
//logrus.Infof("Proposed new Dione task with ID: %s", event.ReqID.String())
|
|
||||||
//err = n.ConsensusManager.Propose(*task)
|
|
||||||
//if err != nil {
|
|
||||||
// logrus.Errorf("Failed to propose task: %v", err)
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
break EventLoop
|
break EventLoop
|
||||||
|
@ -58,17 +58,17 @@ func provideDisputeManager(ctx context.Context, ethClient *ethclient.EthereumCli
|
|||||||
return consensus.NewDisputeManager(ctx, ethClient, pcm, cfg.Ethereum.DisputeVoteWindow)
|
return consensus.NewDisputeManager(ctx, ethClient, pcm, cfg.Ethereum.DisputeVoteWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func provideMiner(peerID peer.ID, ethAddress common.Address, beacon beacon.BeaconNetworks, ethClient *ethclient.EthereumClient, privateKey []byte) *consensus.Miner {
|
func provideMiner(peerID peer.ID, ethAddress common.Address, beacon beacon.BeaconNetworks, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *consensus.Miner {
|
||||||
return consensus.NewMiner(peerID, ethAddress, beacon, ethClient, privateKey)
|
return consensus.NewMiner(peerID, ethAddress, beacon, ethClient, privateKey, mempool)
|
||||||
}
|
}
|
||||||
|
|
||||||
func provideBeacon(ps *pubsub2.PubSub) (beacon.BeaconNetworks, error) {
|
func provideBeacon(ps *pubsub2.PubSub, pcm *consensus.PBFTConsensusManager) (beacon.BeaconNetworks, error) {
|
||||||
networks := beacon.BeaconNetworks{}
|
networks := beacon.BeaconNetworks{}
|
||||||
bc, err := drand2.NewDrandBeacon(ps)
|
bc, err := drand2.NewDrandBeacon(ps, pcm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to setup drand beacon: %w", err)
|
return nil, fmt.Errorf("failed to setup drand beacon: %w", err)
|
||||||
}
|
}
|
||||||
networks = append(networks, beacon.BeaconNetwork{Start: types.DrandRound(config.ChainGenesis), Beacon: bc})
|
networks = append(networks, beacon.BeaconNetwork{Start: config.DrandChainGenesisTime, Beacon: bc})
|
||||||
// NOTE: currently we use only one network
|
// NOTE: currently we use only one network
|
||||||
return networks, nil
|
return networks, nil
|
||||||
}
|
}
|
||||||
@ -103,8 +103,8 @@ func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubR
|
|||||||
return pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap)
|
return pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func provideConsensusManager(psb *pubsub.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey []byte, minApprovals int, evc cache.Cache) *consensus.PBFTConsensusManager {
|
func provideConsensusManager(psb *pubsub.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, minApprovals int) *consensus.PBFTConsensusManager {
|
||||||
return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner, evc)
|
return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner)
|
||||||
}
|
}
|
||||||
|
|
||||||
func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) (host.Host, error) {
|
func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) (host.Host, error) {
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
package store
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/ethclient"
|
|
||||||
"github.com/Secured-Finance/dione/lib"
|
|
||||||
"github.com/Secured-Finance/dione/types"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
validation "github.com/go-ozzo/ozzo-validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DioneStakeInfo struct {
|
|
||||||
ID int
|
|
||||||
MinerStake *types.BigInt
|
|
||||||
TotalStake *types.BigInt
|
|
||||||
MinerAddress string
|
|
||||||
MinerEthWallet string
|
|
||||||
Timestamp time.Time
|
|
||||||
Ethereum *ethclient.EthereumClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDioneStakeInfo(minerStake, totalStake *types.BigInt, minerWallet, minerEthWallet string, ethereumClient *ethclient.EthereumClient) *DioneStakeInfo {
|
|
||||||
return &DioneStakeInfo{
|
|
||||||
MinerStake: minerStake,
|
|
||||||
TotalStake: totalStake,
|
|
||||||
MinerAddress: minerWallet,
|
|
||||||
MinerEthWallet: minerEthWallet,
|
|
||||||
Ethereum: ethereumClient,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DioneStakeInfo) UpdateMinerStake(minerEthAddress common.Address) error {
|
|
||||||
minerStake, err := d.Ethereum.GetMinerStake(minerEthAddress)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.MinerStake = minerStake
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DioneStakeInfo) UpdateTotalStake() error {
|
|
||||||
totalStake, err := d.Ethereum.GetTotalStake()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.TotalStake = totalStake
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put miner's staking information into the database
|
|
||||||
func (s *Store) CreateDioneStakeInfo(stakeStore *DioneStakeInfo) error {
|
|
||||||
|
|
||||||
if err := stakeStore.Validate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
now := lib.Clock.Now()
|
|
||||||
|
|
||||||
return s.db.QueryRow(
|
|
||||||
"INSERT INTO staking (miner_stake, total_stake, miner_address, miner_eth_wallet, timestamp) VALUES ($1, $2, $3, $4, $5) RETURNING id",
|
|
||||||
stakeStore.MinerStake,
|
|
||||||
stakeStore.TotalStake,
|
|
||||||
stakeStore.MinerAddress,
|
|
||||||
stakeStore.MinerEthWallet,
|
|
||||||
now,
|
|
||||||
).Scan(&stakeStore.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) GetLastStakeInfo(wallet, ethWallet string) (*DioneStakeInfo, error) {
|
|
||||||
var stake *DioneStakeInfo
|
|
||||||
if err := s.db.Select(&stake,
|
|
||||||
`SELECT miner_stake, total_stake, miner_address, miner_eth_wallet, timestamp FROM staking ORDER BY TIMESTAMP DESC LIMIT 1 WHERE miner_address=$1, miner_eth_wallet=$2`,
|
|
||||||
wallet,
|
|
||||||
ethWallet,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return stake, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before puting the data into the database validating all required fields
|
|
||||||
func (s *DioneStakeInfo) Validate() error {
|
|
||||||
return validation.ValidateStruct(
|
|
||||||
s,
|
|
||||||
validation.Field(&s.MinerStake, validation.Required, validation.By(types.ValidateBigInt(s.MinerStake.Int))),
|
|
||||||
validation.Field(&s.TotalStake, validation.Required, validation.By(types.ValidateBigInt(s.TotalStake.Int))),
|
|
||||||
validation.Field(&s.MinerAddress, validation.Required),
|
|
||||||
validation.Field(&s.MinerEthWallet, validation.Required),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,46 +1,44 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"github.com/Secured-Finance/dione/node"
|
// "github.com/Secured-Finance/dione/node"
|
||||||
"github.com/jmoiron/sqlx"
|
// "github.com/jmoiron/sqlx"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
// _ "github.com/mattn/go-sqlite3"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
type Store struct {
|
//type Store struct {
|
||||||
db *sqlx.DB
|
// db *sqlx.DB
|
||||||
node *node.Node
|
// node *node.Node
|
||||||
genesisTs uint64
|
// genesisTs uint64
|
||||||
StakeStorage DioneStakeInfo
|
//}
|
||||||
// genesisTask *types.DioneTask
|
//
|
||||||
}
|
//func NewStore(node *node.Node, genesisTs uint64) (*Store, error) {
|
||||||
|
// db, err := newDB(node.Config.Store.DatabaseURL)
|
||||||
func NewStore(node *node.Node, genesisTs uint64) (*Store, error) {
|
// if err != nil {
|
||||||
db, err := newDB(node.Config.Store.DatabaseURL)
|
// return nil, err
|
||||||
if err != nil {
|
// }
|
||||||
return nil, err
|
//
|
||||||
}
|
// defer db.Close()
|
||||||
|
//
|
||||||
defer db.Close()
|
// return &Store{
|
||||||
|
// db: db,
|
||||||
return &Store{
|
// node: node,
|
||||||
db: db,
|
// genesisTs: genesisTs,
|
||||||
node: node,
|
// }, nil
|
||||||
genesisTs: genesisTs,
|
//}
|
||||||
}, nil
|
//
|
||||||
}
|
//func newDB(databaseURL string) (*sqlx.DB, error) {
|
||||||
|
// db, err := sqlx.Connect("sqlite3", databaseURL)
|
||||||
func newDB(databaseURL string) (*sqlx.DB, error) {
|
// if err != nil {
|
||||||
db, err := sqlx.Connect("sqlite3", databaseURL)
|
// return nil, err
|
||||||
if err != nil {
|
// }
|
||||||
return nil, err
|
//
|
||||||
}
|
// if err := db.Ping(); err != nil {
|
||||||
|
// return nil, err
|
||||||
if err := db.Ping(); err != nil {
|
// }
|
||||||
return nil, err
|
//
|
||||||
}
|
// return db, nil
|
||||||
|
//}
|
||||||
return db, nil
|
//
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Discuss with ChronosX88 about using custom database to decrease I/O bound
|
// TODO: Discuss with ChronosX88 about using custom database to decrease I/O bound
|
||||||
// specify the migrations for stake storage;
|
// specify the migrations for stake storage;
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
big2 "github.com/filecoin-project/go-state-types/big"
|
|
||||||
validation "github.com/go-ozzo/ozzo-validation"
|
|
||||||
)
|
|
||||||
|
|
||||||
var EmptyInt = BigInt{}
|
|
||||||
|
|
||||||
type BigInt = big2.Int
|
|
||||||
|
|
||||||
func NewInt(i uint64) BigInt {
|
|
||||||
return BigInt{Int: big.NewInt(0).SetUint64(i)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BigFromBytes(b []byte) BigInt {
|
|
||||||
i := big.NewInt(0).SetBytes(b)
|
|
||||||
return BigInt{Int: i}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateBigInt(i *big.Int) validation.RuleFunc {
|
|
||||||
return func(value interface{}) error {
|
|
||||||
bigInt := i.IsInt64()
|
|
||||||
if !bigInt {
|
|
||||||
return errors.New("expected big integer")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
@ -99,13 +99,13 @@ func polyval(p []*big.Int, x *big.Int) *big.Int {
|
|||||||
|
|
||||||
// computes lambda in Q.256
|
// computes lambda in Q.256
|
||||||
func lambda(power, totalPower *big.Int) *big.Int {
|
func lambda(power, totalPower *big.Int) *big.Int {
|
||||||
lam := new(big.Int).Mul(power, tasksPerEpoch.Int) // Q.0
|
lam := new(big.Int).Mul(power, config.ExpectedLeadersPerEpoch) // Q.0
|
||||||
lam = lam.Lsh(lam, precision) // Q.256
|
lam = lam.Lsh(lam, precision) // Q.256
|
||||||
lam = lam.Div(lam /* Q.256 */, totalPower /* Q.0 */) // Q.256
|
lam = lam.Div(lam /* Q.256 */, totalPower /* Q.0 */) // Q.256
|
||||||
return lam
|
return lam
|
||||||
}
|
}
|
||||||
|
|
||||||
var MaxWinCount = 3 * int64(config.TasksPerEpoch)
|
var MaxWinCount = 3 * config.ExpectedLeadersPerEpoch.Int64()
|
||||||
|
|
||||||
type poiss struct {
|
type poiss struct {
|
||||||
lam *big.Int
|
lam *big.Int
|
||||||
@ -175,10 +175,10 @@ func (p *poiss) next() *big.Int {
|
|||||||
// ComputeWinCount uses VRFProof to compute number of wins
|
// ComputeWinCount uses VRFProof to compute number of wins
|
||||||
// The algorithm is based on Algorand's Sortition with Binomial distribution
|
// The algorithm is based on Algorand's Sortition with Binomial distribution
|
||||||
// replaced by Poisson distribution.
|
// replaced by Poisson distribution.
|
||||||
func (ep *ElectionProof) ComputeWinCount(power BigInt, totalPower BigInt) int64 {
|
func (ep *ElectionProof) ComputeWinCount(power *big.Int, totalPower *big.Int) int64 {
|
||||||
h := blake2b.Sum256(ep.VRFProof)
|
h := blake2b.Sum256(ep.VRFProof)
|
||||||
|
|
||||||
lhs := BigFromBytes(h[:]).Int // 256bits, assume Q.256 so [0, 1)
|
lhs := big.NewInt(0).SetBytes(h[:]) // 256bits, assume Q.256 so [0, 1)
|
||||||
|
|
||||||
// We are calculating upside-down CDF of Poisson distribution with
|
// We are calculating upside-down CDF of Poisson distribution with
|
||||||
// rate λ=power*E/totalPower
|
// rate λ=power*E/totalPower
|
||||||
@ -191,7 +191,7 @@ func (ep *ElectionProof) ComputeWinCount(power BigInt, totalPower BigInt) int64
|
|||||||
// rhs = 1 - pmf
|
// rhs = 1 - pmf
|
||||||
// for h(vrf) < rhs: j++; pmf = pmf * lam / j; rhs = rhs - pmf
|
// for h(vrf) < rhs: j++; pmf = pmf * lam / j; rhs = rhs - pmf
|
||||||
|
|
||||||
lam := lambda(power.Int, totalPower.Int) // Q.256
|
lam := lambda(power, totalPower) // Q.256
|
||||||
|
|
||||||
p, rhs := newPoiss(lam)
|
p, rhs := newPoiss(lam)
|
||||||
|
|
||||||
|
@ -1,33 +1,24 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DrandRound represents the round number in DRAND
|
const TicketRandomnessLookback = 1
|
||||||
type DrandRound int64
|
|
||||||
|
|
||||||
const TicketRandomnessLookback = DrandRound(1)
|
|
||||||
|
|
||||||
func (e DrandRound) String() string {
|
|
||||||
return strconv.FormatInt(int64(e), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DioneTask represents the values of task computation
|
// DioneTask represents the values of task computation
|
||||||
|
// DEPRECATED!
|
||||||
type DioneTask struct {
|
type DioneTask struct {
|
||||||
OriginChain uint8
|
OriginChain uint8
|
||||||
RequestType string
|
RequestType string
|
||||||
RequestParams string
|
RequestParams string
|
||||||
Miner peer.ID
|
Miner peer.ID
|
||||||
MinerEth string
|
MinerEth common.Address
|
||||||
Ticket *Ticket
|
|
||||||
ElectionProof *ElectionProof
|
ElectionProof *ElectionProof
|
||||||
BeaconEntries []BeaconEntry
|
BeaconEntries []BeaconEntry
|
||||||
DrandRound DrandRound
|
DrandRound uint64
|
||||||
Payload []byte
|
Payload []byte
|
||||||
RequestID string
|
RequestID string
|
||||||
ConsensusID string
|
ConsensusID string
|
||||||
@ -39,10 +30,9 @@ func NewDioneTask(
|
|||||||
requestType string,
|
requestType string,
|
||||||
requestParams string,
|
requestParams string,
|
||||||
miner peer.ID,
|
miner peer.ID,
|
||||||
ticket *Ticket,
|
|
||||||
electionProof *ElectionProof,
|
electionProof *ElectionProof,
|
||||||
beacon []BeaconEntry,
|
beacon []BeaconEntry,
|
||||||
drand DrandRound,
|
drandRound uint64,
|
||||||
payload []byte,
|
payload []byte,
|
||||||
) *DioneTask {
|
) *DioneTask {
|
||||||
return &DioneTask{
|
return &DioneTask{
|
||||||
@ -50,12 +40,9 @@ func NewDioneTask(
|
|||||||
RequestType: requestType,
|
RequestType: requestType,
|
||||||
RequestParams: requestParams,
|
RequestParams: requestParams,
|
||||||
Miner: miner,
|
Miner: miner,
|
||||||
Ticket: ticket,
|
|
||||||
ElectionProof: electionProof,
|
ElectionProof: electionProof,
|
||||||
BeaconEntries: beacon,
|
BeaconEntries: beacon,
|
||||||
DrandRound: drand,
|
DrandRound: drandRound,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tasksPerEpoch = NewInt(config.TasksPerEpoch)
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/minio/blake2b-simd"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Ticket struct {
|
|
||||||
VRFProof []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Ticket) Quality() float64 {
|
|
||||||
ticketHash := blake2b.Sum256(t.VRFProof)
|
|
||||||
ticketNum := BigFromBytes(ticketHash[:]).Int
|
|
||||||
ticketDenu := big.NewInt(1)
|
|
||||||
ticketDenu.Lsh(ticketDenu, 256)
|
|
||||||
tv, _ := new(big.Rat).SetFrac(ticketNum, ticketDenu).Float64()
|
|
||||||
tq := 1 - tv
|
|
||||||
return tq
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user