Implement full task validation (signatures/vrfs/wincount/etc.)

This commit is contained in:
ChronosX88 2020-11-27 20:16:08 +04:00
parent ab069fd34d
commit 003f49174b
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
14 changed files with 251 additions and 290 deletions

View File

@ -2,8 +2,7 @@ package consensus
import (
types2 "github.com/Secured-Finance/dione/consensus/types"
"github.com/Secured-Finance/dione/sigs"
"github.com/Secured-Finance/dione/types"
"github.com/sirupsen/logrus"
)
type CommitPool struct {
@ -19,18 +18,8 @@ func NewCommitPool() *CommitPool {
func (cp *CommitPool) CreateCommit(prepareMsg *types2.Message, privateKey []byte) (*types2.Message, error) {
var message types2.Message
message.Type = types2.MessageTypeCommit
var consensusMsg types2.ConsensusMessage
prepareCMessage := prepareMsg.Payload
consensusMsg.ConsensusID = prepareCMessage.ConsensusID
consensusMsg.RequestID = prepareMsg.Payload.RequestID
consensusMsg.CallbackAddress = prepareMsg.Payload.CallbackAddress
consensusMsg.Data = prepareCMessage.Data
signature, err := sigs.Sign(types.SigTypeEd25519, privateKey, []byte(prepareCMessage.Data))
if err != nil {
return nil, err
}
consensusMsg.Signature = signature.Data
message.Payload = consensusMsg
newCMsg := prepareMsg.Payload
message.Payload = newCMsg
return &message, nil
}
@ -47,8 +36,9 @@ func (cp *CommitPool) IsExistingCommit(commitMsg *types2.Message) bool {
func (cp *CommitPool) IsValidCommit(commit *types2.Message) bool {
consensusMsg := commit.Payload
err := sigs.Verify(&types.Signature{Type: types.SigTypeEd25519, Data: consensusMsg.Signature}, commit.From, []byte(consensusMsg.Data))
err := verifyTaskSignature(consensusMsg)
if err != nil {
logrus.Errorf("failed to verify task signature: %v", err)
return false
}
return true

View File

@ -4,8 +4,6 @@ import (
"math/big"
"sync"
"github.com/Secured-Finance/dione/sigs"
"github.com/Secured-Finance/dione/consensus/types"
"github.com/ethereum/go-ethereum/common"
@ -26,22 +24,19 @@ type PBFTConsensusManager struct {
commitPool *CommitPool
consensusInfo map[string]*ConsensusData
ethereumClient *ethclient.EthereumClient
miner *Miner
}
type ConsensusData struct {
// preparedCount int
// commitCount int
mutex sync.Mutex
alreadySubmitted bool
// result string
// test bool
// onConsensusFinishCallback func(finalData string)
}
func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey []byte, ethereumClient *ethclient.EthereumClient) *PBFTConsensusManager {
func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey []byte, ethereumClient *ethclient.EthereumClient, miner *Miner) *PBFTConsensusManager {
pcm := &PBFTConsensusManager{}
pcm.psb = psb
pcm.prePreparePool = NewPrePreparePool()
pcm.miner = miner
pcm.prePreparePool = NewPrePreparePool(miner)
pcm.preparePool = NewPreparePool()
pcm.commitPool = NewCommitPool()
pcm.minApprovals = minApprovals
@ -54,144 +49,11 @@ func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey
return pcm
}
//func (pcm *PBFTConsensusManager) NewTestConsensus(data string, consensusID string, onConsensusFinishCallback func(finalData string)) {
// //consensusID := uuid.New().String()
// cData := &ConsensusData{}
// cData.test = true
// cData.onConsensusFinishCallback = onConsensusFinishCallback
// pcm.Consensuses[consensusID] = cData
//
// // here we will create DioneTask
//
// msg := models.Message{}
// msg.Type = "prepared"
// msg.Payload = make(map[string]interface{})
// msg.Payload["consensusID"] = consensusID
// msg.Payload["data"] = data
// sign, err := sigs.Sign(types.SigTypeEd25519, pcm.privKey, []byte(data))
// if err != nil {
// logrus.Warnf("failed to sign data: %w", err)
// return
// }
// msg.Payload["signature"] = string(sign.Data)
// pcm.psb.BroadcastToServiceTopic(&msg)
//
// cData.State = ConsensusPrePrepared
// logrus.Debug("started new consensus: " + consensusID)
//}
//
//func (pcm *PBFTConsensusManager) handlePrePrepareMessage(sender peer.ID, message *models.Message) {
// consensusID := message.Payload["consensusID"].(string)
// if _, ok := pcm.Consensuses[consensusID]; !ok {
// logrus.Warn("Unknown consensus ID,: " + consensusID + ", creating consensusInfo")
// pcm.Consensuses[consensusID] = &ConsensusData{
// State: ConsensusPrePrepared,
// onConsensusFinishCallback: func(finalData string) {},
// }
// }
// data := pcm.Consensuses[consensusID]
// logrus.Debug("received pre_prepare msg")
//}
//
//func (pcm *PBFTConsensusManager) handlePreparedMessage(sender peer.ID, message *models.Message) {
// // TODO add check on view of the message
// consensusID := message.Payload["consensusID"].(string)
// if _, ok := pcm.Consensuses[consensusID]; !ok {
// logrus.Warn("Unknown consensus ID,: " + consensusID + ", creating consensusInfo")
// pcm.Consensuses[consensusID] = &ConsensusData{
// State: ConsensusPrePrepared,
// onConsensusFinishCallback: func(finalData string) {},
// }
// }
// data := pcm.Consensuses[consensusID]
// logrus.Debug("received prepared msg")
// //data := pcm.Consensuses[consensusID]
//
// // TODO
// // here we can validate miner which produced this task, is he winner, and so on
// // validation steps:
// // 1. validate sender eligibility to mine (check if it has minimal stake)
// // 2. validate sender wincount
// // 3. validate randomness
// // 4. validate vrf
// // 5. validate payload signature
// // 6. validate transaction (get from rpc client and compare with received)
//
// signStr := message.Payload["signature"].(string)
// signRaw := []byte(signStr)
// err := sigs.Verify(&types.Signature{Data: signRaw, Type: types.SigTypeEd25519}, sender, message.Payload["data"].([]byte))
// if err != nil {
// logrus.Warn("failed to verify data signature")
// return
// }
//
// data.mutex.Lock()
// defer data.mutex.Unlock()
// data.preparedCount++
//
// if data.preparedCount > 2*pcm.maxFaultNodes+1 {
// msg := models.Message{}
// msg.Payload = make(map[string]interface{})
// msg.Type = "commit"
// msg.Payload["consensusID"] = consensusID
// msg.Payload["data"] = message.Payload["data"]
// sign, err := sigs.Sign(types.SigTypeEd25519, pcm.privKey, message.Payload["data"].([]byte))
// if err != nil {
// logrus.Warnf("failed to sign data: %w", err)
// return
// }
// msg.Payload["signature"] = string(sign.Data)
// err = pcm.psb.BroadcastToServiceTopic(&msg)
// if err != nil {
// logrus.Warn("Unable to send COMMIT message: " + err.Error())
// return
// }
// data.State = ConsensusPrepared
// }
//}
//
//func (pcm *PBFTConsensusManager) handleCommitMessage(sender peer.ID, message *models.Message) {
// // TODO add check on view of the message
// // TODO add validation of data by hash to this stage
// consensusID := message.Payload["consensusID"].(string)
// if _, ok := pcm.Consensuses[consensusID]; !ok {
// logrus.Warn("Unknown consensus ID: " + consensusID)
// return
// }
// data := pcm.Consensuses[consensusID]
//
// data.mutex.Lock()
// defer data.mutex.Unlock()
// if data.State == ConsensusCommitted {
// logrus.Debug("consensus already finished, dropping COMMIT message")
// return
// }
//
// logrus.Debug("received commit msg")
//
// signStr := message.Payload["signature"].(string)
// signRaw := []byte(signStr)
// err := sigs.Verify(&types.Signature{Data: signRaw, Type: types.SigTypeEd25519}, sender, message.Payload["data"].([]byte))
// if err != nil {
// logrus.Warn("failed to verify data signature")
// return
// }
//
// data.commitCount++
//
// if data.commitCount > 2*pcm.maxFaultNodes+1 {
// data.State = ConsensusCommitted
// data.result = message.Payload["data"].(string)
// logrus.Debug("consensus successfully finished with result: " + data.result)
// data.onConsensusFinishCallback(data.result)
// }
//}
func (pcm *PBFTConsensusManager) Propose(consensusID, data string, requestID *big.Int, callbackAddress common.Address) error {
func (pcm *PBFTConsensusManager) Propose(consensusID string, task types2.DioneTask, requestID *big.Int, callbackAddress common.Address) error {
pcm.consensusInfo[consensusID] = &ConsensusData{}
reqIDRaw := requestID.String()
callbackAddressHex := callbackAddress.Hex()
prePrepareMsg, err := pcm.prePreparePool.CreatePrePrepare(consensusID, data, reqIDRaw, callbackAddressHex, pcm.privKey)
prePrepareMsg, err := pcm.prePreparePool.CreatePrePrepare(consensusID, task, reqIDRaw, callbackAddressHex, pcm.privKey)
if err != nil {
return err
}
@ -210,13 +72,7 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *types.Message) {
}
pcm.prePreparePool.AddPrePrepare(message)
err := pcm.resignMessage(message)
if err != nil {
logrus.Errorf(err.Error())
return
}
err = pcm.psb.BroadcastToServiceTopic(message)
err := pcm.psb.BroadcastToServiceTopic(message)
if err != nil {
logrus.Errorf(err.Error())
return
@ -240,12 +96,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *types.Message) {
}
pcm.preparePool.AddPrepare(message)
err := pcm.resignMessage(message)
if err != nil {
logrus.Errorf(err.Error())
return
}
err = pcm.psb.BroadcastToServiceTopic(message)
err := pcm.psb.BroadcastToServiceTopic(message)
if err != nil {
logrus.Errorf(err.Error())
return
@ -271,12 +122,7 @@ func (pcm *PBFTConsensusManager) handleCommit(message *types.Message) {
}
pcm.commitPool.AddCommit(message)
err := pcm.resignMessage(message)
if err != nil {
logrus.Errorf(err.Error())
return
}
err = pcm.psb.BroadcastToServiceTopic(message)
err := pcm.psb.BroadcastToServiceTopic(message)
if err != nil {
logrus.Errorf(err.Error())
return
@ -296,7 +142,7 @@ func (pcm *PBFTConsensusManager) handleCommit(message *types.Message) {
logrus.Errorf("Failed to parse big int: %v", consensusMsg.RequestID)
}
callbackAddress := common.HexToAddress(consensusMsg.CallbackAddress)
err := pcm.ethereumClient.SubmitRequestAnswer(reqID, consensusMsg.Data, callbackAddress)
err := pcm.ethereumClient.SubmitRequestAnswer(reqID, string(consensusMsg.Task.Payload), callbackAddress)
if err != nil {
logrus.Errorf("Failed to submit on-chain result: %w", err)
}
@ -304,12 +150,3 @@ func (pcm *PBFTConsensusManager) handleCommit(message *types.Message) {
}
}
}
func (pcm *PBFTConsensusManager) resignMessage(msg *types.Message) error {
sig, err := sigs.Sign(types2.SigTypeEd25519, pcm.privKey, []byte(msg.Payload.Data))
if err != nil {
return err
}
msg.Payload.Signature = sig.Data
return nil
}

View File

@ -1,9 +1,10 @@
package consensus
import (
"context"
"fmt"
"github.com/Secured-Finance/dione/sigs"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/types"
@ -11,10 +12,10 @@ import (
"golang.org/x/xerrors"
)
type SignFunc func(context.Context, peer.ID, []byte) (*types.Signature, error)
type SignFunc func(peer.ID, []byte) (*types.Signature, error)
func ComputeVRF(ctx context.Context, sign SignFunc, worker peer.ID, sigInput []byte) ([]byte, error) {
sig, err := sign(ctx, worker, sigInput)
func ComputeVRF(sign SignFunc, worker peer.ID, sigInput []byte) ([]byte, error) {
sig, err := sign(worker, sigInput)
if err != nil {
return nil, err
}
@ -26,22 +27,17 @@ func ComputeVRF(ctx context.Context, sign SignFunc, worker peer.ID, sigInput []b
return sig.Data, nil
}
func VerifyVRF(ctx context.Context, worker peer.ID, vrfBase, vrfproof []byte) error {
pKey, err := worker.ExtractPublicKey()
func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error {
err := sigs.Verify(&types.Signature{Type: types.SigTypeEd25519, Data: vrfproof}, worker, vrfBase)
if err != nil {
return xerrors.Errorf("failed to extract public key from worker address: %w", err)
}
valid, err := pKey.Verify(vrfBase, vrfproof)
if err != nil || !valid {
return xerrors.Errorf("vrf was invalid: %w", err)
}
return nil
}
func IsRoundWinner(ctx context.Context, round types.DrandRound,
worker peer.ID, brand types.BeaconEntry, minerStake, networkStake types.BigInt, a WalletAPI) (*types.ElectionProof, error) {
func IsRoundWinner(round types.DrandRound,
worker peer.ID, brand types.BeaconEntry, minerStake, networkStake types.BigInt, sign SignFunc) (*types.ElectionProof, error) {
buf, err := worker.MarshalBinary()
if err != nil {
@ -53,7 +49,7 @@ func IsRoundWinner(ctx context.Context, round types.DrandRound,
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
}
vrfout, err := ComputeVRF(ctx, a.WalletSign, worker, electionRand)
vrfout, err := ComputeVRF(sign, worker, electionRand)
if err != nil {
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
}

View File

@ -4,6 +4,10 @@ import (
"context"
"sync"
big2 "github.com/filecoin-project/go-state-types/big"
"github.com/Secured-Finance/dione/sigs"
"github.com/Secured-Finance/dione/rpc"
"github.com/Secured-Finance/dione/beacon"
@ -21,34 +25,30 @@ import (
type Miner struct {
address peer.ID
ethAddress common.Address
api WalletAPI
mutex sync.Mutex
beacon beacon.BeaconNetworks
ethClient *ethclient.EthereumClient
minerStake types.BigInt
networkStake types.BigInt
privateKey []byte
}
func NewMiner(
address peer.ID,
ethAddress common.Address,
api WalletAPI,
beacon beacon.BeaconNetworks,
ethClient *ethclient.EthereumClient,
privateKey []byte,
) *Miner {
return &Miner{
address: address,
ethAddress: ethAddress,
api: api,
beacon: beacon,
ethClient: ethClient,
privateKey: privateKey,
}
}
type WalletAPI interface {
WalletSign(context.Context, peer.ID, []byte) (*types.Signature, error)
}
func (m *Miner) UpdateCurrentStakeInfo() error {
mStake, err := m.ethClient.GetMinerStake(m.ethAddress)
@ -70,25 +70,52 @@ func (m *Miner) UpdateCurrentStakeInfo() error {
return nil
}
func (m *Miner) MineTask(ctx context.Context, event *oracleEmitter.OracleEmitterNewOracleRequest, sign SignFunc) (*types.DioneTask, error) {
bvals, err := beacon.BeaconEntriesForTask(ctx, m.beacon)
func (m *Miner) GetStakeInfo(miner common.Address) (*types.BigInt, *types.BigInt, error) {
mStake, err := m.ethClient.GetMinerStake(miner)
if err != nil {
logrus.Warn("Can't get miner stake", err)
return nil, nil, err
}
nStake, err := m.ethClient.GetTotalStake()
if err != nil {
logrus.Warn("Can't get miner stake", err)
return nil, nil, err
}
return mStake, nStake, nil
}
func (m *Miner) MineTask(ctx context.Context, event *oracleEmitter.OracleEmitterNewOracleRequest) (*types.DioneTask, error) {
beaconValues, err := beacon.BeaconEntriesForTask(ctx, m.beacon)
if err != nil {
return nil, xerrors.Errorf("failed to get beacon entries: %w", err)
}
logrus.Debug("attempting to mine the task at epoch: ", bvals[1].Round)
logrus.Debug("attempting to mine the task at epoch: ", beaconValues[1].Round)
rbase := bvals[1]
randomBase := beaconValues[1]
if err := m.UpdateCurrentStakeInfo(); err != nil {
return nil, xerrors.Errorf("failed to update miner stake: %w", err)
}
ticket, err := m.computeTicket(ctx, &rbase)
ticket, err := m.computeTicket(&randomBase)
if err != nil {
return nil, xerrors.Errorf("scratching ticket failed: %w", err)
}
winner, err := IsRoundWinner(ctx, types.DrandRound(rbase.Round), m.address, rbase, m.minerStake, m.networkStake, m.api)
winner, err := IsRoundWinner(
types.DrandRound(randomBase.Round),
m.address,
randomBase,
m.minerStake,
m.networkStake,
func(id peer.ID, bytes []byte) (*types.Signature, error) {
return sigs.Sign(types.SigTypeEd25519, m.privateKey, bytes)
},
)
if err != nil {
return nil, xerrors.Errorf("failed to check if we win next round: %w", err)
}
@ -106,22 +133,22 @@ func (m *Miner) MineTask(ctx context.Context, event *oracleEmitter.OracleEmitter
return nil, xerrors.Errorf("couldn't do rpc request: %w", err)
}
bres := []byte(res)
signature, err := sign(ctx, m.address, bres)
if err != nil {
return nil, xerrors.Errorf("Couldn't sign solana response: %w", err)
}
return &types.DioneTask{
OriginChain: event.OriginChain,
RequestType: event.RequestType,
RequestParams: event.RequestParams,
Miner: m.address,
MinerEth: m.ethAddress.Hex(),
Ticket: ticket,
ElectionProof: winner,
BeaconEntries: bvals,
BeaconEntries: beaconValues,
Payload: bres,
Signature: signature,
DrandRound: types.DrandRound(rbase.Round),
DrandRound: types.DrandRound(randomBase.Round),
}, nil
}
func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry) (*types.Ticket, error) {
func (m *Miner) computeTicket(brand *types.BeaconEntry) (*types.Ticket, error) {
buf, err := m.address.MarshalBinary()
if err != nil {
return nil, xerrors.Errorf("failed to marshal address: %w", err)
@ -134,7 +161,9 @@ func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry) (*t
return nil, err
}
vrfOut, err := ComputeVRF(ctx, m.api.WalletSign, m.address, input)
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
}
@ -143,3 +172,15 @@ func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry) (*t
VRFProof: vrfOut,
}, nil
}
func (m *Miner) IsMinerEligibleToProposeTask(ethAddress common.Address) error {
mStake, err := m.ethClient.GetMinerStake(ethAddress)
if err != nil {
return err
}
ok := mStake.GreaterThanEqual(big2.NewInt(ethclient.MinMinerStake))
if !ok {
return xerrors.Errorf("miner doesn't have enough staked tokens")
}
return nil
}

View File

@ -1,31 +1,44 @@
package consensus
import (
"fmt"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/ethereum/go-ethereum/common"
types2 "github.com/Secured-Finance/dione/consensus/types"
"github.com/Secured-Finance/dione/sigs"
"github.com/Secured-Finance/dione/types"
"github.com/mitchellh/hashstructure/v2"
"github.com/sirupsen/logrus"
)
type PrePreparePool struct {
prePrepareMsgs map[string][]*types2.Message
miner *Miner
}
func NewPrePreparePool() *PrePreparePool {
func NewPrePreparePool(miner *Miner) *PrePreparePool {
return &PrePreparePool{
prePrepareMsgs: map[string][]*types2.Message{},
miner: miner,
}
}
func (pp *PrePreparePool) CreatePrePrepare(consensusID, data string, requestID string, callbackAddress string, privateKey []byte) (*types2.Message, error) {
func (pp *PrePreparePool) CreatePrePrepare(consensusID string, task types.DioneTask, requestID string, callbackAddress string, privateKey []byte) (*types2.Message, error) {
var message types2.Message
message.Type = types2.MessageTypePrePrepare
var consensusMsg types2.ConsensusMessage
consensusMsg.ConsensusID = consensusID
consensusMsg.RequestID = requestID
consensusMsg.CallbackAddress = callbackAddress
consensusMsg.Data = data
signature, err := sigs.Sign(types.SigTypeEd25519, privateKey, []byte(data))
consensusMsg.Task = task
cHash, err := hashstructure.Hash(consensusMsg, 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
}
@ -48,11 +61,83 @@ func (ppp *PrePreparePool) IsExistingPrePrepare(prepareMsg *types2.Message) bool
func (ppp *PrePreparePool) IsValidPrePrepare(prePrepare *types2.Message) bool {
// TODO here we need to do validation of tx itself
consensusMsg := prePrepare.Payload
err := sigs.Verify(&types.Signature{Type: types.SigTypeEd25519, Data: consensusMsg.Signature}, prePrepare.From, []byte(consensusMsg.Data))
// === verify task signature ===
err := verifyTaskSignature(consensusMsg)
if err != nil {
logrus.Errorf("unable to verify signature: %v", err)
return false
}
/////////////////////////////////
// === verify election proof wincount preliminarily ===
if consensusMsg.Task.ElectionProof.WinCount < 1 {
logrus.Error("miner isn't a winner!")
return false
}
/////////////////////////////////
// === verify miner's eligibility to propose this task ===
err = ppp.miner.IsMinerEligibleToProposeTask(common.HexToAddress(consensusMsg.Task.MinerEth))
if err != nil {
logrus.Errorf("miner is not eligible to propose task: %v", err)
return false
}
/////////////////////////////////
// === verify election proof vrf ===
minerAddressMarshalled, err := prePrepare.Payload.Task.Miner.MarshalBinary()
if err != nil {
logrus.Errorf("failed to marshal miner address: %v", err)
return false
}
electionProofRandomness, err := DrawRandomness(
consensusMsg.Task.BeaconEntries[1].Data,
crypto.DomainSeparationTag_ElectionProofProduction,
consensusMsg.Task.DrandRound,
minerAddressMarshalled,
)
if err != nil {
logrus.Errorf("failed to draw electionProofRandomness: %v", err)
return false
}
err = VerifyVRF(consensusMsg.Task.Miner, electionProofRandomness, consensusMsg.Task.ElectionProof.VRFProof)
if err != nil {
logrus.Errorf("failed to verify election proof vrf: %v", err)
}
//////////////////////////////////////
// === verify ticket vrf ===
ticketRandomness, err := DrawRandomness(
consensusMsg.Task.BeaconEntries[1].Data,
crypto.DomainSeparationTag_TicketProduction,
consensusMsg.Task.DrandRound-types.TicketRandomnessLookback,
minerAddressMarshalled,
)
if err != nil {
logrus.Errorf("failed to draw ticket electionProofRandomness: %v", err)
return false
}
err = VerifyVRF(consensusMsg.Task.Miner, ticketRandomness, consensusMsg.Task.Ticket.VRFProof)
if err != nil {
logrus.Errorf("failed to verify ticket vrf: %v", err)
}
//////////////////////////////////////
// === compute wincount locally and verify values ===
mStake, nStake, err := ppp.miner.GetStakeInfo(common.HexToAddress(consensusMsg.Task.MinerEth))
if err != nil {
logrus.Errorf("failed to get miner stake: %v", err)
return false
}
actualWinCount := consensusMsg.Task.ElectionProof.ComputeWinCount(*mStake, *nStake)
if consensusMsg.Task.ElectionProof.WinCount != actualWinCount {
logrus.Errorf("locally computed wincount isn't matching received value!", err)
return false
}
//////////////////////////////////////
return true
}

View File

@ -2,8 +2,6 @@ package consensus
import (
types2 "github.com/Secured-Finance/dione/consensus/types"
"github.com/Secured-Finance/dione/sigs"
"github.com/Secured-Finance/dione/types"
)
type PreparePool struct {
@ -20,18 +18,8 @@ func NewPreparePool() *PreparePool {
func (pp *PreparePool) CreatePrepare(prePrepareMsg *types2.Message, privateKey []byte) (*types2.Message, error) {
var message types2.Message
message.Type = types2.MessageTypePrepare
var consensusMsg types2.ConsensusMessage
prepareCMessage := prePrepareMsg.Payload
consensusMsg.ConsensusID = prepareCMessage.ConsensusID
consensusMsg.RequestID = prePrepareMsg.Payload.RequestID
consensusMsg.CallbackAddress = prePrepareMsg.Payload.CallbackAddress
consensusMsg.Data = prepareCMessage.Data
signature, err := sigs.Sign(types.SigTypeEd25519, privateKey, []byte(prepareCMessage.Data))
if err != nil {
return nil, err
}
consensusMsg.Signature = signature.Data
message.Payload = consensusMsg
newCMsg := prePrepareMsg.Payload
message.Payload = newCMsg
return &message, nil
}
@ -48,7 +36,7 @@ func (pp *PreparePool) IsExistingPrepare(prepareMsg *types2.Message) bool {
func (pp *PreparePool) IsValidPrepare(prepare *types2.Message) bool {
consensusMsg := prepare.Payload
err := sigs.Verify(&types.Signature{Type: types.SigTypeEd25519, Data: consensusMsg.Signature}, prepare.From, []byte(consensusMsg.Data))
err := verifyTaskSignature(consensusMsg)
if err != nil {
return false
}

View File

@ -1,6 +1,7 @@
package types
import (
"github.com/Secured-Finance/dione/types"
"github.com/libp2p/go-libp2p-core/peer"
)
@ -15,12 +16,12 @@ const (
)
type ConsensusMessage struct {
_ struct{} `cbor:",toarray"`
_ struct{} `cbor:",toarray" hash:"-"`
ConsensusID string
Signature []byte
Signature []byte `hash:"-"`
RequestID string
CallbackAddress string
Data string
Task types.DioneTask
}
type Message struct {

26
consensus/utils.go Normal file
View File

@ -0,0 +1,26 @@
package consensus
import (
"fmt"
"github.com/Secured-Finance/dione/consensus/types"
"github.com/Secured-Finance/dione/sigs"
types2 "github.com/Secured-Finance/dione/types"
"github.com/mitchellh/hashstructure/v2"
)
func verifyTaskSignature(msg types.ConsensusMessage) error {
cHash, err := hashstructure.Hash(msg, hashstructure.FormatV2, nil)
if err != nil {
return err
}
err = sigs.Verify(
&types2.Signature{Type: types2.SigTypeEd25519, Data: msg.Signature},
msg.Task.Miner,
[]byte(fmt.Sprintf("%v", cHash)),
)
if err != nil {
return err
}
return nil
}

View File

@ -5,6 +5,10 @@ import (
"github.com/ethereum/go-ethereum/common"
)
const (
MinMinerStake = 1000
)
// Getting total stake in DioneStaking contract, this function could
// be used for storing the total stake and veryfing the stake tokens
// on new tasks

3
go.mod
View File

@ -44,11 +44,12 @@ require (
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-sqlite3 v1.9.0
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/mitchellh/hashstructure/v2 v2.0.1
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/multiformats/go-multiaddr v0.3.1
github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/onsi/ginkgo v1.14.0 // indirect
github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a
github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a // indirect
github.com/raulk/clock v1.1.0
github.com/rjeczalik/notify v0.9.2 // indirect
github.com/rs/cors v1.7.0 // indirect

3
go.sum
View File

@ -208,7 +208,6 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg=
github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ=
github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
@ -764,6 +763,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/hashstructure/v2 v2.0.1 h1:L60q1+q7cXE4JeEJJKMnh2brFIe3rZxCihYAB61ypAY=
github.com/mitchellh/hashstructure/v2 v2.0.1/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=

View File

@ -9,7 +9,7 @@ import (
func (n *Node) subscribeOnEthContracts(ctx context.Context) {
eventChan, subscription, err := n.Ethereum.SubscribeOnOracleEvents(ctx)
if err != nil {
logrus.Fatal("Can't subscribe on ethereum contracts, exiting... ", err)
logrus.Fatal("Couldn't subscribe on ethereum contracts, exiting... ", err)
}
go func() {
@ -18,16 +18,15 @@ func (n *Node) subscribeOnEthContracts(ctx context.Context) {
select {
case event := <-eventChan:
{
task, err := n.Miner.MineTask(ctx, event, n.Wallet.WalletSign)
task, err := n.Miner.MineTask(ctx, event)
if err != nil {
logrus.Fatal("Error with mining algorithm, exiting... ", err)
logrus.Fatal("Failed to mine task, exiting... ", err)
}
if task == nil {
continue
}
logrus.Info("Started new consensus round with ID: ", task.Signature)
err = n.ConsensusManager.Propose(event.RequestID.String(), string(task.Payload), event.RequestID, event.CallbackAddress)
logrus.Infof("Started new consensus round with ID: %s", event.RequestID.String())
err = n.ConsensusManager.Propose(event.RequestID.String(), *task, event.RequestID, event.CallbackAddress)
if err != nil {
logrus.Errorf("Failed to propose task: %w", err)
}

View File

@ -81,27 +81,31 @@ func (n *Node) setupNode(ctx context.Context, prvKey crypto.PrivKey, pexDiscover
}
n.setupPubsub()
err = n.setupConsensusManager(prvKey, n.Config.ConsensusMinApprovals)
rawPrivKey, err := prvKey.Raw()
if err != nil {
logrus.Fatalf("Failed to setup consensus manager: %w", err)
logrus.Fatal(err)
}
err = n.setupBeacon()
if err != nil {
logrus.Fatal(err)
}
err = n.setupWallet(prvKey)
err = n.setupMiner(rawPrivKey)
if err != nil {
logrus.Fatal(err)
}
err = n.setupMiner()
err = n.setupConsensusManager(rawPrivKey, n.Config.ConsensusMinApprovals)
if err != nil {
logrus.Fatalf("Failed to setup consensus manager: %w", err)
}
err = n.setupWallet(rawPrivKey)
if err != nil {
logrus.Fatal(err)
}
n.subscribeOnEthContracts(ctx)
}
func (n *Node) setupMiner() error {
n.Miner = consensus.NewMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Wallet, n.Beacon, n.Ethereum)
func (n *Node) setupMiner(privKey []byte) error {
n.Miner = consensus.NewMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Beacon, n.Ethereum, privKey)
return nil
}
@ -114,16 +118,12 @@ func (n *Node) setupBeacon() error {
return nil
}
func (n *Node) setupWallet(privKey crypto.PrivKey) error {
func (n *Node) setupWallet(privKey []byte) error {
// TODO make persistent keystore
kstore := wallet.NewMemKeyStore()
pKeyBytes, err := privKey.Raw()
if err != nil {
return xerrors.Errorf("failed to get raw private key: %w", err)
}
keyInfo := types.KeyInfo{
Type: types.KTEd25519,
PrivateKey: pKeyBytes,
PrivateKey: privKey,
}
kstore.Put(wallet.KNamePrefix+n.Host.ID().String(), keyInfo)
@ -167,12 +167,8 @@ func (n *Node) setupPubsub() {
//time.Sleep(3 * time.Second)
}
func (n *Node) setupConsensusManager(privateKey crypto.PrivKey, minApprovals int) error {
pkeyRaw, err := privateKey.Raw()
if err != nil {
return err
}
n.ConsensusManager = consensus.NewPBFTConsensusManager(n.PubSubRouter, minApprovals, pkeyRaw, n.Ethereum)
func (n *Node) setupConsensusManager(privateKey []byte, minApprovals int) error {
n.ConsensusManager = consensus.NewPBFTConsensusManager(n.PubSubRouter, minApprovals, privateKey, n.Ethereum, n.Miner)
return nil
}

View File

@ -8,15 +8,7 @@ import (
"github.com/Secured-Finance/dione/config"
)
type TaskType byte
const (
EthereumTaskType = TaskType(iota)
FilecoinTaskType
SolanaTaskType
)
// DrandRound represents the round number in DRAND
// DrandRound represents the round number in DRAND
type DrandRound int64
const TicketRandomnessLookback = DrandRound(1)
@ -25,36 +17,40 @@ func (e DrandRound) String() string {
return strconv.FormatInt(int64(e), 10)
}
// DioneTask represents the values of task computation
// Miner is an address of miner node
// DioneTask represents the values of task computation
type DioneTask struct {
_ struct{} `cbor:",toarray" hash:"-"`
OriginChain uint8
RequestType string
RequestParams string
Miner peer.ID
Type TaskType
MinerEth string
Ticket *Ticket
ElectionProof *ElectionProof
BeaconEntries []BeaconEntry
Signature *Signature
DrandRound DrandRound
Payload []byte
}
func NewDioneTask(
t TaskType,
originChain uint8,
requestType string,
requestParams string,
miner peer.ID,
ticket *Ticket,
electionProof *ElectionProof,
beacon []BeaconEntry,
sig *Signature,
drand DrandRound,
payload []byte,
) *DioneTask {
return &DioneTask{
Type: t,
OriginChain: originChain,
RequestType: requestType,
RequestParams: requestParams,
Miner: miner,
Ticket: ticket,
ElectionProof: electionProof,
BeaconEntries: beacon,
Signature: sig,
DrandRound: drand,
Payload: payload,
}