Implement full task validation (signatures/vrfs/wincount/etc.)
This commit is contained in:
parent
ab069fd34d
commit
003f49174b
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
26
consensus/utils.go
Normal 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
|
||||
}
|
@ -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
3
go.mod
@ -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
3
go.sum
@ -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=
|
||||
|
@ -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)
|
||||
}
|
||||
|
32
node/node.go
32
node/node.go
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user