Merge pull request #16 from Secured-Finance/feat/blockchain

Implement blockchain for Dione, fix most consensus issues
This commit is contained in:
ChronosX88 2021-08-02 20:48:48 +03:00 committed by GitHub
commit 1d12828313
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 4119 additions and 1910 deletions

2
.gitignore vendored
View File

@ -25,3 +25,5 @@ eth-contracts/node_modules
/.dione-config-4.toml
.bootstrap_privkey
eth-contracts/build
.db
eth-contracts/secrets.json

View File

@ -4,9 +4,6 @@ import (
"context"
"fmt"
"github.com/Secured-Finance/dione/lib"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/types"
)
@ -17,7 +14,7 @@ type BeaconResult struct {
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-- {
bp := bn[i]
if e >= bp.Start {
@ -28,7 +25,7 @@ func (bn BeaconNetworks) BeaconNetworkForRound(e types.DrandRound) BeaconAPI {
}
type BeaconNetwork struct {
Start types.DrandRound
Start uint64
Beacon BeaconAPI
}
@ -37,111 +34,18 @@ type BeaconNetwork struct {
// valid for a specific chain epoch. Also to verify beacon entries that have
// been posted on chain.
type BeaconAPI interface {
Entry(context.Context, uint64) <-chan BeaconResult
Entry(context.Context, uint64) (types.BeaconEntry, error)
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
LatestBeaconRound() uint64
}
func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prevEpoch types.DrandRound, prevEntry types.BeaconEntry) error {
parentBeacon := beaconNetworks.BeaconNetworkForRound(prevEpoch)
currBeacon := beaconNetworks.BeaconNetworkForRound(t.DrandRound)
if parentBeacon != currBeacon {
if len(t.BeaconEntries) != 2 {
return fmt.Errorf("expected two beacon entries at beacon fork, got %d", len(t.BeaconEntries))
}
err := currBeacon.VerifyEntry(t.BeaconEntries[1], t.BeaconEntries[0])
if err != nil {
return fmt.Errorf("beacon at fork point invalid: (%v, %v): %w",
t.BeaconEntries[1], t.BeaconEntries[0], err)
}
return nil
}
// ValidateBlockBeacons is a function that verifies block randomness
func (bn BeaconNetworks) ValidateBlockBeacons(beaconNetworks BeaconNetworks, curEntry, prevEntry types.BeaconEntry) error {
defaultBeacon := beaconNetworks.BeaconNetworkForRound(0)
// TODO: fork logic
bNetwork := beaconNetworks.BeaconNetworkForRound(t.DrandRound)
if uint64(t.DrandRound) == prevEntry.Round {
if len(t.BeaconEntries) != 0 {
return fmt.Errorf("expected not to have any beacon entries in this task, got %d", len(t.BeaconEntries))
}
return nil
}
if len(t.BeaconEntries) == 0 {
return fmt.Errorf("expected to have beacon entries in this task, but didn't find any")
}
last := t.BeaconEntries[len(t.BeaconEntries)-1]
if last.Round != uint64(t.DrandRound) {
return fmt.Errorf("expected final beacon entry in task to be at round %d, got %d", uint64(t.DrandRound), last.Round)
}
for i, e := range t.BeaconEntries {
if err := bNetwork.VerifyEntry(e, prevEntry); err != nil {
return fmt.Errorf("beacon entry %d (%d - %x (%d)) was invalid: %w", i, e.Round, e.Data, len(e.Data), err)
}
prevEntry = e
if err := defaultBeacon.VerifyEntry(curEntry, prevEntry); err != nil {
return fmt.Errorf("beacon entry was invalid: %w", err)
}
return nil
}
func BeaconEntriesForTask(ctx context.Context, beaconNetworks BeaconNetworks) ([]types.BeaconEntry, error) {
beacon := beaconNetworks.BeaconNetworkForRound(0)
round := beacon.LatestBeaconRound()
//prevBeacon := beaconNetworks.BeaconNetworkForRound(prevRound)
//currBeacon := beaconNetworks.BeaconNetworkForRound(round)
//if prevBeacon != currBeacon {
// // Fork logic
// round := currBeacon.LatestBeaconRound()
// out := make([]types.BeaconEntry, 2)
// rch := currBeacon.Entry(ctx, round-1)
// res := <-rch
// if res.Err != nil {
// return nil, fmt.Errorf("getting entry %d returned error: %w", round-1, res.Err)
// }
// out[0] = res.Entry
// rch = currBeacon.Entry(ctx, round)
// res = <-rch
// if res.Err != nil {
// return nil, fmt.Errorf("getting entry %d returned error: %w", round, res.Err)
// }
// out[1] = res.Entry
// return out, nil
//}
start := lib.Clock.Now()
//if round == prev.Round {
// return nil, nil
//}
//
//// TODO: this is a sketchy way to handle the genesis block not having a beacon entry
//if prev.Round == 0 {
// prev.Round = round - 1
//}
out := make([]types.BeaconEntry, 2)
rch := beacon.Entry(ctx, round-1)
res := <-rch
if res.Err != nil {
return nil, fmt.Errorf("getting entry %d returned error: %w", round-1, res.Err)
}
out[0] = res.Entry
rch = beacon.Entry(ctx, round)
res = <-rch
if res.Err != nil {
return nil, fmt.Errorf("getting entry %d returned error: %w", round, res.Err)
}
out[1] = res.Entry
logrus.Debugf("fetching beacon entries: took %v, count of entries: %v", lib.Clock.Since(start), len(out))
//reverse(out)
return out, nil
}
func reverse(arr []types.BeaconEntry) {
for i := 0; i < len(arr)/2; i++ {
arr[i], arr[len(arr)-(1+i)] = arr[len(arr)-(1+i)], arr[i]
}
}

View File

@ -0,0 +1,8 @@
package beacon
// RandomnessType specifies a type of randomness.
type RandomnessType int64
const (
RandomnessTypeElectionProofProduction RandomnessType = 1 + iota
)

View File

@ -5,7 +5,8 @@ import (
"context"
"fmt"
"sync"
"time"
"github.com/asaskevich/EventBus"
"github.com/Secured-Finance/dione/beacon"
"github.com/drand/drand/chain"
@ -30,31 +31,17 @@ var log = logrus.WithFields(logrus.Fields{
"subsystem": "drand",
})
// DrandResponse structure representing response from drand network
type DrandResponse struct {
// PreviousSig is the previous signature generated
PreviousSig []byte
// Round is the round number this beacon is tied to
Round uint64
// Signature is the BLS deterministic signature over Round || PreviousRand
Signature []byte
// Randomness for specific round generated by Drand
Randomness []byte
}
type DrandBeacon struct {
DrandClient client.Client
PublicKey kyber.Point
Interval time.Duration
chainGenesisTime uint64
chainRoundTime uint64
drandGenesisTime uint64
drandResultChannel <-chan client.Result
cacheLock sync.Mutex
localCache map[uint64]types.BeaconEntry
latestDrandRound uint64
bus EventBus.Bus
}
func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub) (*DrandBeacon, error) {
func NewDrandBeacon(ps *pubsub.PubSub, bus EventBus.Bus) (*DrandBeacon, error) {
cfg := config.NewDrandConfig()
drandChain, err := chain.InfoFromJSON(bytes.NewReader([]byte(cfg.ChainInfo)))
@ -95,46 +82,69 @@ func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub) (*DrandBeacon
db := &DrandBeacon{
DrandClient: drandClient,
localCache: make(map[uint64]types.BeaconEntry),
bus: bus,
PublicKey: drandChain.PublicKey,
}
db.PublicKey = drandChain.PublicKey
db.Interval = drandChain.Period
db.drandGenesisTime = uint64(drandChain.GenesisTime)
db.chainRoundTime = interval
db.chainGenesisTime = genesisTs
return db, nil
}
func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan beacon.BeaconResult {
out := make(chan beacon.BeaconResult, 1)
func (db *DrandBeacon) Run(ctx context.Context) error {
db.drandResultChannel = db.DrandClient.Watch(ctx)
err := db.getLatestDrandResult()
if err != nil {
return err
}
go db.loop(ctx)
return nil
}
func (db *DrandBeacon) getLatestDrandResult() error {
latestDround, err := db.DrandClient.Get(context.TODO(), 0)
if err != nil {
log.Errorf("failed to get latest drand round: %v", err)
return err
}
db.cacheValue(newBeaconEntryFromDrandResult(latestDround))
db.updateLatestDrandRound(latestDround.Round())
return nil
}
func (db *DrandBeacon) loop(ctx context.Context) {
for {
select {
case <-ctx.Done():
{
logrus.Debug("Stopping watching new DRAND entries...")
return
}
case res := <-db.drandResultChannel:
{
db.cacheValue(newBeaconEntryFromDrandResult(res))
db.updateLatestDrandRound(res.Round())
db.bus.Publish("beacon:newEntry", types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()}))
}
}
}
}
func (db *DrandBeacon) Entry(ctx context.Context, round uint64) (types.BeaconEntry, error) {
if round != 0 {
be := db.getCachedValue(round)
if be != nil {
out <- beacon.BeaconResult{Entry: *be}
close(out)
return out
return *be, nil
}
}
go func() {
start := lib.Clock.Now()
log.Infof("start fetching randomness: round %v", round)
resp, err := db.DrandClient.Get(ctx, round)
var br beacon.BeaconResult
if err != nil {
br.Err = fmt.Errorf("drand failed Get request: %w", err)
} else {
br.Entry.Round = resp.Round()
br.Entry.Data = resp.Signature()
return types.BeaconEntry{}, fmt.Errorf("drand failed Get request: %w", err)
}
log.Infof("done fetching randomness: round %v, took %v", round, lib.Clock.Since(start))
out <- br
close(out)
}()
return out
return newBeaconEntryFromDrandResult(resp), nil
}
func (db *DrandBeacon) cacheValue(res types.BeaconEntry) {
db.cacheLock.Lock()
@ -152,6 +162,12 @@ func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry {
return &v
}
func (db *DrandBeacon) updateLatestDrandRound(round uint64) {
db.cacheLock.Lock()
defer db.cacheLock.Unlock()
db.latestDrandRound = round
}
func (db *DrandBeacon) VerifyEntry(curr, prev types.BeaconEntry) error {
if prev.Round == 0 {
return nil
@ -160,23 +176,21 @@ func (db *DrandBeacon) VerifyEntry(curr, prev types.BeaconEntry) error {
return nil
}
b := &chain.Beacon{
PreviousSig: prev.Data,
PreviousSig: prev.Metadata["signature"].([]byte),
Round: curr.Round,
Signature: curr.Data,
Signature: curr.Metadata["signature"].([]byte),
}
err := chain.VerifyBeacon(db.PublicKey, b)
if err == nil {
db.cacheValue(curr)
}
return err
return chain.VerifyBeacon(db.PublicKey, b)
}
func (db *DrandBeacon) LatestBeaconRound() uint64 {
latestDround, err := db.DrandClient.Get(context.TODO(), 0)
if err != nil {
log.Errorf("failed to get latest drand round: %w", err)
db.cacheLock.Lock()
defer db.cacheLock.Unlock()
return db.latestDrandRound
}
return latestDround.Round()
func newBeaconEntryFromDrandResult(res client.Result) types.BeaconEntry {
return types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()})
}
var _ beacon.BeaconAPI = (*DrandBeacon)(nil)

29
beacon/utils.go Normal file
View File

@ -0,0 +1,29 @@
package beacon
import (
"encoding/binary"
"github.com/minio/blake2b-simd"
"golang.org/x/xerrors"
)
func DrawRandomness(rbase []byte, randomnessType RandomnessType, round uint64, entropy []byte) ([]byte, error) {
h := blake2b.New256()
if err := binary.Write(h, binary.BigEndian, int64(randomnessType)); err != nil {
return nil, xerrors.Errorf("deriving randomness: %v", err)
}
VRFDigest := blake2b.Sum256(rbase)
_, err := h.Write(VRFDigest[:])
if err != nil {
return nil, xerrors.Errorf("hashing VRFDigest: %w", err)
}
if err := binary.Write(h, binary.BigEndian, round); err != nil {
return nil, xerrors.Errorf("deriving randomness: %v", err)
}
_, err = h.Write(entropy)
if err != nil {
return nil, xerrors.Errorf("hashing entropy: %v", err)
}
return h.Sum(nil), nil
}

28
beacon/vrf.go Normal file
View File

@ -0,0 +1,28 @@
package beacon
import (
"fmt"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
)
func ComputeVRF(privKey crypto.PrivKey, sigInput []byte) ([]byte, error) {
return privKey.Sign(sigInput)
}
func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error {
pk, err := worker.ExtractPublicKey()
if err != nil {
return err
}
ok, err := pk.Verify(vrfBase, vrfproof)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("vrf was invalid")
}
return nil
}

434
blockchain/blockchain.go Normal file
View File

@ -0,0 +1,434 @@
package blockchain
import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"os"
"sync"
drand2 "github.com/Secured-Finance/dione/beacon/drand"
"github.com/Secured-Finance/dione/beacon"
"github.com/Secured-Finance/dione/consensus/validation"
"github.com/Secured-Finance/dione/types"
"github.com/sirupsen/logrus"
"github.com/wealdtech/go-merkletree"
"github.com/wealdtech/go-merkletree/keccak256"
"github.com/asaskevich/EventBus"
"github.com/Secured-Finance/dione/blockchain/utils"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/fxamacker/cbor/v2"
"github.com/ledgerwatch/lmdb-go/lmdb"
)
const (
DefaultBlockDataPrefix = "blockdata_"
DefaultBlockHeaderPrefix = "header_"
DefaultMetadataIndexName = "metadata"
LatestBlockHeightKey = "latest_block_height"
)
var (
ErrBlockNotFound = errors.New("block isn't found")
ErrLatestHeightNil = errors.New("latest block height is nil")
)
type BlockChain struct {
// db-related
dbEnv *lmdb.Env
db lmdb.DBI
metadataIndex *utils.Index
heightIndex *utils.Index
bus EventBus.Bus
miner *Miner
drandBeacon *drand2.DrandBeacon
}
func NewBlockChain(path string, bus EventBus.Bus, miner *Miner, db *drand2.DrandBeacon) (*BlockChain, error) {
chain := &BlockChain{
bus: bus,
miner: miner,
drandBeacon: db,
}
// configure lmdb env
env, err := lmdb.NewEnv()
if err != nil {
return nil, err
}
err = env.SetMaxDBs(1)
if err != nil {
return nil, err
}
err = env.SetMapSize(100 * 1024 * 1024 * 1024) // 100 GB
if err != nil {
return nil, err
}
err = os.MkdirAll(path, 0755)
if err != nil {
return nil, err
}
err = env.Open(path, 0, 0755)
if err != nil {
return nil, err
}
chain.dbEnv = env
var dbi lmdb.DBI
err = env.Update(func(txn *lmdb.Txn) error {
dbi, err = txn.OpenDBI("blocks", lmdb.Create)
return err
})
if err != nil {
return nil, err
}
chain.db = dbi
// create index instances
metadataIndex := utils.NewIndex(DefaultMetadataIndexName, env, dbi)
heightIndex := utils.NewIndex("height", env, dbi)
chain.metadataIndex = metadataIndex
chain.heightIndex = heightIndex
return chain, nil
}
func (bc *BlockChain) setLatestBlockHeight(height uint64) error {
err := bc.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height)
if err != nil {
return err
}
return nil
}
func (bc *BlockChain) GetLatestBlockHeight() (uint64, error) {
height, err := bc.metadataIndex.GetUint64([]byte(LatestBlockHeightKey))
if err != nil {
if err == utils.ErrIndexKeyNotFound {
return 0, ErrLatestHeightNil
}
return 0, err
}
return height, nil
}
func (bc *BlockChain) StoreBlock(block *types2.Block) error {
if exists, err := bc.HasBlock(block.Header.Hash); err != nil {
return err
} else if exists {
//return fmt.Errorf("block already exists in blockchain")
return nil
}
if block.Header.Height != 0 {
err := bc.ValidateBlock(block)
if err != nil {
return fmt.Errorf("failed to store block: %w", err)
}
}
err := bc.dbEnv.Update(func(txn *lmdb.Txn) error {
data, err := cbor.Marshal(block.Data)
if err != nil {
return err
}
headerData, err := cbor.Marshal(block.Header)
if err != nil {
return err
}
blockHash := hex.EncodeToString(block.Header.Hash)
err = txn.Put(bc.db, []byte(DefaultBlockDataPrefix+blockHash), data, 0)
if err != nil {
return err
}
err = txn.Put(bc.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching
return err
})
if err != nil {
return err
}
// update index "height -> block hash"
heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, block.Header.Height)
err = bc.heightIndex.PutBytes(heightBytes, block.Header.Hash)
if err != nil {
return err
}
// update latest block height
height, err := bc.GetLatestBlockHeight()
if err != nil && err != ErrLatestHeightNil {
return err
}
if err == ErrLatestHeightNil || block.Header.Height > height {
if err = bc.setLatestBlockHeight(block.Header.Height); err != nil {
return err
}
bc.bus.Publish("blockchain:latestBlockHeightUpdated", block)
}
bc.bus.Publish("blockchain:blockCommitted", block)
return nil
}
func (bc *BlockChain) HasBlock(blockHash []byte) (bool, error) {
var blockExists bool
err := bc.dbEnv.View(func(txn *lmdb.Txn) error {
h := hex.EncodeToString(blockHash)
_, err := txn.Get(bc.db, []byte(DefaultBlockHeaderPrefix+h)) // try to fetch block header
if err != nil {
if lmdb.IsNotFound(err) {
blockExists = false
return nil
}
return err
}
blockExists = true
return nil
})
if err != nil {
return false, err
}
return blockExists, nil
}
func (bc *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) {
var data []*types2.Transaction
err := bc.dbEnv.View(func(txn *lmdb.Txn) error {
h := hex.EncodeToString(blockHash)
blockData, err := txn.Get(bc.db, []byte(DefaultBlockDataPrefix+h))
if err != nil {
if lmdb.IsNotFound(err) {
return ErrBlockNotFound
}
return err
}
err = cbor.Unmarshal(blockData, &data)
return err
})
if err != nil {
return nil, err
}
return data, nil
}
func (bc *BlockChain) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) {
var blockHeader types2.BlockHeader
err := bc.dbEnv.View(func(txn *lmdb.Txn) error {
h := hex.EncodeToString(blockHash)
data, err := txn.Get(bc.db, []byte(DefaultBlockHeaderPrefix+h))
if err != nil {
if lmdb.IsNotFound(err) {
return ErrBlockNotFound
}
return err
}
err = cbor.Unmarshal(data, &blockHeader)
return err
})
if err != nil {
return nil, err
}
return &blockHeader, nil
}
func (bc *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) {
var block types2.Block
header, err := bc.FetchBlockHeader(blockHash)
if err != nil {
return nil, err
}
block.Header = header
data, err := bc.FetchBlockData(blockHash)
if err != nil {
return nil, err
}
block.Data = data
return &block, nil
}
func (bc *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) {
var heightBytes = make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, height)
blockHash, err := bc.heightIndex.GetBytes(heightBytes)
if err != nil {
if err == utils.ErrIndexKeyNotFound {
return nil, ErrBlockNotFound
}
}
block, err := bc.FetchBlock(blockHash)
if err != nil {
return nil, err
}
return block, nil
}
func (bc *BlockChain) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) {
var heightBytes = make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, height)
blockHash, err := bc.heightIndex.GetBytes(heightBytes)
if err != nil {
if err == utils.ErrIndexKeyNotFound {
return nil, ErrBlockNotFound
}
}
blockHeader, err := bc.FetchBlockHeader(blockHash)
if err != nil {
return nil, err
}
return blockHeader, nil
}
func (bc *BlockChain) ValidateBlock(block *types2.Block) error {
// === verify block signature ===
pubkey, err := block.Header.Proposer.ExtractPublicKey()
if err != nil {
return fmt.Errorf("unable to extract public key from block proposer's peer id: %w", err)
}
ok, err := pubkey.Verify(block.Header.Hash, block.Header.Signature)
if err != nil {
return fmt.Errorf("failed to verify block signature: %w", err)
}
if !ok {
return fmt.Errorf("signature of block %x is invalid", block.Header.Hash)
}
/////////////////////////////////
// === check last hash merkle proof ===
latestHeight, err := bc.GetLatestBlockHeight()
if err != nil {
return err
}
previousBlockHeader, err := bc.FetchBlockHeaderByHeight(latestHeight)
if err != nil {
return err
}
if !bytes.Equal(block.Header.LastHash, previousBlockHeader.Hash) {
return fmt.Errorf("block header has invalid last block hash (expected: %x, actual %x)", previousBlockHeader.Hash, block.Header.LastHash)
}
verified, err := merkletree.VerifyProofUsing(previousBlockHeader.Hash, true, block.Header.LastHashProof, [][]byte{block.Header.Hash}, keccak256.New())
if err != nil {
return fmt.Errorf("failed to verify last block hash merkle proof: %w", err)
}
if !verified {
return fmt.Errorf("merkle hash of block doesn't contain hash of previous block")
}
/////////////////////////////////
// === verify election proof wincount preliminarily ===
if block.Header.ElectionProof.WinCount < 1 {
return fmt.Errorf("block proposer %s is not a winner", block.Header.Proposer.String())
}
/////////////////////////////////
// === verify miner's eligibility to propose this task ===
err = bc.miner.IsMinerEligibleToProposeBlock(block.Header.ProposerEth)
if err != nil {
return fmt.Errorf("block proposer is not eligible to propose block: %w", err)
}
/////////////////////////////////
// === verify election proof vrf ===
proposerBuf, err := block.Header.Proposer.MarshalBinary()
if err != nil {
return err
}
res, err := bc.drandBeacon.Entry(context.TODO(), block.Header.ElectionProof.RandomnessRound)
if err != nil {
return err
}
eproofRandomness, err := beacon.DrawRandomness(
res.Data,
beacon.RandomnessTypeElectionProofProduction,
block.Header.Height,
proposerBuf,
)
if err != nil {
return fmt.Errorf("failed to draw ElectionProof randomness: %w", err)
}
err = beacon.VerifyVRF(*block.Header.Proposer, eproofRandomness, block.Header.ElectionProof.VRFProof)
if err != nil {
return fmt.Errorf("failed to verify election proof vrf: %w", err)
}
//////////////////////////////////////
// === compute wincount locally and verify values ===
mStake, nStake, err := bc.miner.GetStakeInfo(block.Header.ProposerEth)
if err != nil {
return fmt.Errorf("failed to get miner stake: %w", err)
}
actualWinCount := block.Header.ElectionProof.ComputeWinCount(mStake, nStake)
if block.Header.ElectionProof.WinCount != actualWinCount {
return fmt.Errorf("locally computed wincount of block is not matching to the received value")
}
//////////////////////////////////////
// === validate block transactions ===
result := make(chan error)
var wg sync.WaitGroup
for _, v := range block.Data {
wg.Add(1)
go func(v *types2.Transaction, c chan error) {
defer wg.Done()
if err := utils.VerifyTx(block.Header, v); err != nil {
c <- fmt.Errorf("failed to verify tx: %w", err)
return
}
var task types.DioneTask
err = cbor.Unmarshal(v.Data, &task)
if err != nil {
c <- fmt.Errorf("failed to unmarshal transaction payload: %w", err)
return
}
if validationFunc := validation.GetValidationMethod(task.OriginChain, task.RequestType); validationFunc != nil {
if err := validationFunc(&task); err != nil {
c <- fmt.Errorf("payload validation has been failed: %w", err)
return
}
} else {
logrus.WithFields(logrus.Fields{
"originChain": task.OriginChain,
"requestType": task.RequestType,
}).Debug("This origin chain/request type doesn't have any payload validation!")
}
}(v, result)
}
go func() {
wg.Wait()
close(result)
}()
for err := range result {
if err != nil {
return err
}
}
/////////////////////////////////
return nil
}

199
blockchain/miner.go Normal file
View File

@ -0,0 +1,199 @@
package blockchain
import (
"errors"
"fmt"
"math/big"
"github.com/libp2p/go-libp2p-core/host"
"github.com/asaskevich/EventBus"
"github.com/Secured-Finance/dione/beacon"
"github.com/Secured-Finance/dione/types"
"github.com/Secured-Finance/dione/blockchain/pool"
"github.com/libp2p/go-libp2p-core/crypto"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/ethclient"
"github.com/ethereum/go-ethereum/common"
"github.com/sirupsen/logrus"
)
var (
ErrNoTxForBlock = fmt.Errorf("no transactions for including into block")
)
type Miner struct {
bus EventBus.Bus
address peer.ID
ethClient *ethclient.EthereumClient
minerStake *big.Int
networkStake *big.Int
privateKey crypto.PrivKey
mempool *pool.Mempool
latestBlockHeader *types2.BlockHeader
blockchain *BlockChain
}
func NewMiner(
h host.Host,
ethClient *ethclient.EthereumClient,
privateKey crypto.PrivKey,
mempool *pool.Mempool,
bus EventBus.Bus,
) *Miner {
m := &Miner{
address: h.ID(),
ethClient: ethClient,
privateKey: privateKey,
mempool: mempool,
bus: bus,
}
return m
}
func (m *Miner) SetBlockchainInstance(b *BlockChain) {
m.blockchain = b
m.bus.SubscribeAsync("blockchain:latestBlockHeightUpdated", func(block *types2.Block) {
m.latestBlockHeader = block.Header
}, true)
height, _ := m.blockchain.GetLatestBlockHeight()
header, err := m.blockchain.FetchBlockHeaderByHeight(height)
if err != nil {
logrus.WithField("err", err.Error()).Fatal("Failed to initialize miner subsystem")
}
m.latestBlockHeader = header
logrus.Info("Mining subsystem has been initialized!")
}
func (m *Miner) UpdateCurrentStakeInfo() error {
mStake, err := m.ethClient.GetMinerStake(*m.ethClient.GetEthAddress())
if err != nil {
logrus.Warn("Can't get miner stake", err)
return err
}
nStake, err := m.ethClient.GetTotalStake()
if err != nil {
logrus.Warn("Can't get miner stake", err)
return err
}
m.minerStake = mStake
m.networkStake = nStake
return nil
}
func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, 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) MineBlock(randomness []byte, randomnessRound uint64) (*types2.Block, error) {
if m.latestBlockHeader == nil {
return nil, fmt.Errorf("latest block header is null")
}
logrus.WithField("height", m.latestBlockHeader.Height+1).Debug("Trying to mine new block...")
if err := m.UpdateCurrentStakeInfo(); err != nil {
return nil, fmt.Errorf("failed to update miner stake: %w", err)
}
winner, err := isRoundWinner(
m.latestBlockHeader.Height+1,
m.address,
randomness,
randomnessRound,
m.minerStake,
m.networkStake,
m.privateKey,
)
if err != nil {
return nil, fmt.Errorf("failed to check if we winned in next round: %w", err)
}
if winner == nil {
logrus.WithField("height", m.latestBlockHeader.Height+1).Debug("Block is not mined because we are not leader in consensus round")
return nil, nil
}
logrus.WithField("height", m.latestBlockHeader.Height+1).Infof("We have been elected in the current consensus round")
txs := m.mempool.GetTransactionsForNewBlock()
if txs == nil {
return nil, ErrNoTxForBlock // skip new consensus round because there is no transaction for processing
}
newBlock, err := types2.CreateBlock(m.latestBlockHeader, txs, *m.ethClient.GetEthAddress(), m.privateKey, winner)
if err != nil {
return nil, fmt.Errorf("failed to create new block: %w", err)
}
return newBlock, nil
}
func (m *Miner) IsMinerEligibleToProposeBlock(ethAddress common.Address) error {
mStake, err := m.ethClient.GetMinerStake(ethAddress)
if err != nil {
return err
}
if mStake.Cmp(big.NewInt(ethclient.MinMinerStake)) == -1 {
return errors.New("miner doesn't have enough staked tokens")
}
return nil
}
func isRoundWinner(round uint64,
worker peer.ID, randomness []byte, randomnessRound uint64, minerStake, networkStake *big.Int, privKey crypto.PrivKey) (*types.ElectionProof, error) {
buf, err := worker.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("failed to marshal address: %w", err)
}
electionRand, err := beacon.DrawRandomness(randomness, beacon.RandomnessTypeElectionProofProduction, round, buf)
if err != nil {
return nil, fmt.Errorf("failed to draw randomness: %w", err)
}
vrfout, err := beacon.ComputeVRF(privKey, electionRand)
if err != nil {
return nil, fmt.Errorf("failed to compute VRF: %w", err)
}
ep := &types.ElectionProof{VRFProof: vrfout, RandomnessRound: randomnessRound}
j := ep.ComputeWinCount(minerStake, networkStake)
ep.WinCount = j
if j < 1 {
return nil, nil
}
return ep, nil
}

107
blockchain/pool/mempool.go Normal file
View File

@ -0,0 +1,107 @@
package pool
import (
"encoding/hex"
"errors"
"sort"
"time"
"github.com/asaskevich/EventBus"
"github.com/sirupsen/logrus"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/consensus/policy"
"github.com/Secured-Finance/dione/cache"
)
const (
DefaultTxTTL = 10 * time.Minute
DefaultTxPrefix = "tx_"
)
var (
ErrTxNotFound = errors.New("tx isn't found in mempool")
)
type Mempool struct {
cache cache.Cache
bus EventBus.Bus
}
func NewMempool(bus EventBus.Bus) (*Mempool, error) {
mp := &Mempool{
cache: cache.NewInMemoryCache(), // here we need to use separate cache
bus: bus,
}
return mp, nil
}
func (mp *Mempool) StoreTx(tx *types2.Transaction) error {
hashStr := hex.EncodeToString(tx.Hash)
err := mp.cache.StoreWithTTL(DefaultTxPrefix+hashStr, tx, DefaultTxTTL)
logrus.WithField("txHash", hex.EncodeToString(tx.Hash)).Info("Submitted new transaction in mempool")
mp.bus.Publish("mempool:transactionAdded", tx)
return err
}
func (mp *Mempool) DeleteTx(txHash []byte) error {
hashStr := hex.EncodeToString(txHash)
var tx types2.Transaction
err := mp.cache.Get(DefaultTxPrefix+hashStr, &tx)
if err != nil {
return err
}
mp.cache.Delete(DefaultTxPrefix + hashStr)
logrus.WithField("txHash", hex.EncodeToString(txHash)).Debugf("Deleted transaction from mempool")
mp.bus.Publish("mempool:transactionRemoved", tx)
return nil
}
func (mp *Mempool) GetTransactionsForNewBlock() []*types2.Transaction {
var txForBlock []*types2.Transaction
allTxs := mp.GetAllTransactions()
sort.Slice(allTxs, func(i, j int) bool {
return allTxs[i].Timestamp.Before(allTxs[j].Timestamp)
})
for i := 0; i < policy.BlockMaxTransactionCount; i++ {
if len(allTxs) == 0 {
break
}
tx := allTxs[0] // get oldest tx
allTxs = allTxs[1:] // pop tx
txForBlock = append(txForBlock, tx)
}
return txForBlock
}
func (mp *Mempool) GetAllTransactions() []*types2.Transaction {
var allTxs []*types2.Transaction
for _, v := range mp.cache.Items() {
tx := v.(*types2.Transaction)
allTxs = append(allTxs, tx)
}
return allTxs
}
func (mp *Mempool) GetTransaction(hash []byte) (*types2.Transaction, error) {
hashStr := hex.EncodeToString(hash)
var tx types2.Transaction
err := mp.cache.Get(DefaultTxPrefix+hashStr, &tx)
if err != nil {
if errors.Is(err, cache.ErrNotFound) {
return nil, ErrTxNotFound
} else {
return nil, err
}
}
return &tx, nil
}

278
blockchain/sync/sync_mgr.go Normal file
View File

@ -0,0 +1,278 @@
package sync
import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"strings"
"sync"
"github.com/fxamacker/cbor/v2"
"github.com/asaskevich/EventBus"
"github.com/Secured-Finance/dione/blockchain/utils"
"github.com/Secured-Finance/dione/blockchain"
"github.com/Secured-Finance/dione/pubsub"
"github.com/Secured-Finance/dione/consensus/policy"
"github.com/wealdtech/go-merkletree/keccak256"
"github.com/wealdtech/go-merkletree"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/node/wire"
"github.com/libp2p/go-libp2p-core/peer"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/blockchain/pool"
gorpc "github.com/libp2p/go-libp2p-gorpc"
)
type SyncManager interface {
Run()
}
type syncManager struct {
blockpool *blockchain.BlockChain
mempool *pool.Mempool
wg sync.WaitGroup
ctx context.Context
ctxCancelFunc context.CancelFunc
initialSyncCompleted bool
bootstrapPeer peer.ID
rpcClient *gorpc.Client
psb *pubsub.PubSubRouter
bus EventBus.Bus
}
func NewSyncManager(bus EventBus.Bus, bc *blockchain.BlockChain, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID, psb *pubsub.PubSubRouter) SyncManager {
ctx, cancelFunc := context.WithCancel(context.Background())
sm := &syncManager{
bus: bus,
blockpool: bc,
mempool: mp,
ctx: ctx,
ctxCancelFunc: cancelFunc,
initialSyncCompleted: false,
bootstrapPeer: bootstrapPeer,
rpcClient: p2pRPCClient,
psb: psb,
}
return sm
}
func (sm *syncManager) Run() {
sm.psb.Hook(pubsub.NewTxMessageType, sm.onNewTransaction)
sm.psb.Hook(pubsub.NewBlockMessageType, sm.onNewBlock)
go func() {
if err := sm.initialSync(); err != nil {
logrus.Error(err)
}
}()
}
func (sm *syncManager) initialSync() error {
if err := sm.doInitialBlockPoolSync(); err != nil {
return err
}
if err := sm.doInitialMempoolSync(); err != nil {
return err
}
sm.bus.Publish("sync:initialSyncCompleted")
return nil
}
func (sm *syncManager) doInitialBlockPoolSync() error {
if sm.initialSyncCompleted {
return nil
}
ourLastHeight, _ := sm.blockpool.GetLatestBlockHeight()
if sm.bootstrapPeer == "" {
return nil // FIXME
}
var reply wire.LastBlockHeightReply
err := sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "LastBlockHeight", nil, &reply)
if err != nil {
return err
}
if reply.Height > ourLastHeight {
heightCount := reply.Height - ourLastHeight
var from uint64
to := ourLastHeight
var receivedBlocks []types2.Block
for heightCount > 0 {
from = to + 1
var addedVal uint64
if heightCount < policy.MaxBlockCountForRetrieving {
addedVal = heightCount
} else {
addedVal = policy.MaxBlockCountForRetrieving
}
heightCount -= addedVal
to += addedVal
var getBlocksReply wire.GetRangeOfBlocksReply
arg := wire.GetRangeOfBlocksArg{From: from, To: to}
err = sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "GetRangeOfBlocks", arg, &getBlocksReply)
if err != nil {
return err
}
receivedBlocks = append(receivedBlocks, getBlocksReply.Blocks...)
if len(getBlocksReply.FailedBlockHeights) != 0 {
logrus.Warnf("remote node is unable to retrieve block heights: %s", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(getBlocksReply.FailedBlockHeights)), ", "), "[]"))
// FIXME we definitely need to handle it, because in that case our chain isn't complete!
}
}
for _, b := range receivedBlocks {
err := sm.processReceivedBlock(b) // it should process the block synchronously
if err != nil {
logrus.Warnf("unable to process block %d: %s", b.Header.Height, err.Error())
continue
}
}
} else {
// FIXME probably we need to pick up better peer for syncing, because chain of current peer can be out-of-date as well
}
return nil
}
func (sm *syncManager) doInitialMempoolSync() error {
if sm.bootstrapPeer == "" {
return nil // FIXME
}
var reply wire.InvMessage
err := sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "Mempool", nil, &reply)
if err != nil {
return err
}
var txsToRetrieve [][]byte
for _, v := range reply.Inventory {
_, err = sm.mempool.GetTransaction(v.Hash)
if errors.Is(err, pool.ErrTxNotFound) {
txsToRetrieve = append(txsToRetrieve, v.Hash)
}
}
for {
var txHashes [][]byte
if len(txsToRetrieve) == 0 {
break
}
if len(txsToRetrieve) > policy.MaxTransactionCountForRetrieving {
txHashes = txsToRetrieve[:policy.MaxTransactionCountForRetrieving]
txsToRetrieve = txsToRetrieve[policy.MaxTransactionCountForRetrieving:]
} else {
txHashes = txsToRetrieve
txsToRetrieve = nil
}
getMempoolTxArg := wire.GetMempoolTxsArg{
Items: txHashes,
}
var getMempoolTxReply wire.GetMempoolTxsReply
err := sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "GetMempoolTxs", getMempoolTxArg, &getMempoolTxReply)
if err != nil {
return err
}
for _, v := range getMempoolTxReply.Transactions {
err := sm.mempool.StoreTx(&v)
if err != nil {
logrus.Warnf(err.Error())
}
}
// TODO handle not found transactions
}
return nil
}
func (sm *syncManager) processReceivedBlock(block types2.Block) error {
// validate block
previousBlockHeader, err := sm.blockpool.FetchBlockHeaderByHeight(block.Header.Height - 1)
if err != nil {
return fmt.Errorf("failed to retrieve previous block %d", block.Header.Height-1)
}
if bytes.Compare(block.Header.LastHash, previousBlockHeader.Hash) != 0 {
return fmt.Errorf("block header has invalid last block hash")
}
verified, err := merkletree.VerifyProofUsing(previousBlockHeader.Hash, false, block.Header.LastHashProof, [][]byte{block.Header.Hash}, keccak256.New())
if err != nil {
return fmt.Errorf("failed to verify last block hash merkle proof: %w", err)
}
if !verified {
return fmt.Errorf("merkle hash of current block doesn't contain hash of previous block")
}
// check if hashes of block transactions are present in the block hash merkle tree
for _, tx := range block.Data { // FIXME we need to do something with rejected txs
if err := utils.VerifyTx(block.Header, tx); err != nil {
return err
}
}
err = sm.blockpool.StoreBlock(&block)
if err != nil {
return fmt.Errorf("failed to store block in blockpool: %s", err.Error())
}
return nil
}
func (sm *syncManager) onNewTransaction(message *pubsub.PubSubMessage) {
var tx types2.Transaction
err := cbor.Unmarshal(message.Payload, &tx)
if err != nil {
logrus.Errorf("failed to convert payload to transaction: %s", err.Error())
return
}
// TODO add more checks on tx
if !tx.ValidateHash() {
logrus.WithField("txHash", hex.EncodeToString(tx.Hash)).Warn("failed to validate transaction hash, rejecting it")
return
}
err = sm.mempool.StoreTx(&tx)
if err != nil {
logrus.Warnf("failed to store incoming transaction in mempool: %s", err.Error())
}
}
func (sm *syncManager) onNewBlock(message *pubsub.PubSubMessage) {
var block types2.Block
err := cbor.Unmarshal(message.Payload, &block)
if err != nil {
logrus.WithField("err", err.Error()).Error("failed to unmarshal payload of NewBlock message")
return
}
err = sm.blockpool.StoreBlock(&block)
if err != nil {
logrus.WithFields(logrus.Fields{
"err": err.Error(),
"blockHash": fmt.Sprintf("%x", block.Header.Hash),
}).Error("failed to store block from NewBlock message")
return
}
}

109
blockchain/types/block.go Normal file
View File

@ -0,0 +1,109 @@
package types
import (
"encoding/binary"
"time"
"github.com/Secured-Finance/dione/types"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/ethereum/go-ethereum/common"
"github.com/wealdtech/go-merkletree"
"github.com/wealdtech/go-merkletree/keccak256"
"github.com/libp2p/go-libp2p-core/peer"
)
type Block struct {
Header *BlockHeader
Data []*Transaction
}
type BlockHeader struct {
Timestamp int64
Height uint64
Hash []byte
LastHash []byte
LastHashProof *merkletree.Proof
Proposer *peer.ID
ProposerEth common.Address
Signature []byte
BeaconEntry types.BeaconEntry
ElectionProof *types.ElectionProof
}
func GenesisBlock() *Block {
return &Block{
Header: &BlockHeader{
Timestamp: 1620845070,
Height: 0,
Hash: []byte("DIMICANDUM"),
},
Data: []*Transaction{},
}
}
func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth common.Address, privateKey crypto.PrivKey, eproof *types.ElectionProof) (*Block, error) {
timestamp := time.Now().UnixNano()
// extract hashes from transactions
var merkleHashes [][]byte
for _, tx := range txs {
merkleHashes = append(merkleHashes, tx.Hash)
}
merkleHashes = append(merkleHashes, lastBlockHeader.Hash)
timestampBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(timestampBytes, uint64(timestamp))
merkleHashes = append(merkleHashes, timestampBytes)
tree, err := merkletree.NewUsing(merkleHashes, keccak256.New(), true)
if err != nil {
return nil, err
}
// fetch merkle tree root hash (block hash)
blockHash := tree.Root()
// sign the block hash
s, err := privateKey.Sign(blockHash)
if err != nil {
return nil, err
}
lastHashProof, err := tree.GenerateProof(lastBlockHeader.Hash, 0)
if err != nil {
return nil, err
}
proposer, err := peer.IDFromPrivateKey(privateKey)
if err != nil {
return nil, err
}
for _, tx := range txs {
mp, err := tree.GenerateProof(tx.Hash, 0)
if err != nil {
return nil, err
}
tx.MerkleProof = mp
}
block := &Block{
Header: &BlockHeader{
Timestamp: timestamp,
Height: lastBlockHeader.Height + 1,
Proposer: &proposer,
ProposerEth: minerEth,
Signature: s,
Hash: blockHash,
LastHash: lastBlockHeader.Hash,
LastHashProof: lastHashProof,
ElectionProof: eproof,
},
Data: txs,
}
return block, nil
}

View File

@ -0,0 +1,36 @@
package types
import (
"bytes"
"encoding/hex"
"fmt"
"time"
"github.com/wealdtech/go-merkletree"
"github.com/ethereum/go-ethereum/crypto"
)
type Transaction struct {
Hash []byte
MerkleProof *merkletree.Proof // sets when transaction is added to block
Timestamp time.Time
Data []byte
}
func CreateTransaction(data []byte) *Transaction {
timestamp := time.Now()
encodedData := hex.EncodeToString(data)
hash := crypto.Keccak256([]byte(fmt.Sprintf("%s", encodedData)))
return &Transaction{
Hash: hash,
Timestamp: timestamp,
Data: data,
}
}
func (tx *Transaction) ValidateHash() bool {
encodedData := hex.EncodeToString(tx.Data)
h := crypto.Keccak256([]byte(fmt.Sprintf("%s", encodedData)))
return bytes.Equal(h, tx.Hash)
}

96
blockchain/utils/index.go Normal file
View File

@ -0,0 +1,96 @@
package utils
import (
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/ledgerwatch/lmdb-go/lmdb"
)
const (
DefaultIndexPrefix = "indexes/"
)
var (
ErrIndexKeyNotFound = fmt.Errorf("key is not found in the index")
)
type Index struct {
name string
dbEnv *lmdb.Env
db lmdb.DBI
}
func NewIndex(name string, dbEnv *lmdb.Env, db lmdb.DBI) *Index {
return &Index{
name: name,
db: db,
dbEnv: dbEnv,
}
}
func (i *Index) PutUint64(key []byte, value uint64) error {
return i.dbEnv.Update(func(txn *lmdb.Txn) error {
data := make([]byte, 8)
binary.LittleEndian.PutUint64(data, value)
return txn.Put(i.db, i.constructIndexKey(key), data, 0)
})
}
func (i *Index) GetUint64(key []byte) (uint64, error) {
var num uint64
err := i.dbEnv.View(func(txn *lmdb.Txn) error {
data, err := txn.Get(i.db, i.constructIndexKey(key))
if err != nil {
if lmdb.IsNotFound(err) {
return ErrIndexKeyNotFound
}
return err
}
num = binary.LittleEndian.Uint64(data)
return nil
})
if err != nil {
return 0, err
}
return num, nil
}
func (i *Index) PutBytes(key []byte, value []byte) error {
return i.dbEnv.Update(func(txn *lmdb.Txn) error {
return txn.Put(i.db, i.constructIndexKey(key), value, 0)
})
}
func (i *Index) GetBytes(key []byte) ([]byte, error) {
var data []byte
err := i.dbEnv.View(func(txn *lmdb.Txn) error {
valueData, err := txn.Get(i.db, i.constructIndexKey(key))
if err != nil {
if lmdb.IsNotFound(err) {
return ErrIndexKeyNotFound
}
return err
}
data = valueData
return nil
})
if err != nil {
return nil, err
}
return data, nil
}
func (i *Index) Delete(key []byte) error {
return i.dbEnv.Update(func(txn *lmdb.Txn) error {
return txn.Del(i.db, i.constructIndexKey(key), nil)
})
}
func (i *Index) constructIndexKey(key []byte) []byte {
k := hex.EncodeToString(key)
return []byte(fmt.Sprintf("%s/%s/%s", DefaultIndexPrefix, i.name, k))
}

View File

@ -0,0 +1,27 @@
package utils
import (
"fmt"
"github.com/Secured-Finance/dione/blockchain/types"
"github.com/wealdtech/go-merkletree"
"github.com/wealdtech/go-merkletree/keccak256"
)
func VerifyTx(blockHeader *types.BlockHeader, tx *types.Transaction) error {
if tx.MerkleProof == nil {
return fmt.Errorf("block transaction doesn't have merkle proof")
}
txProofVerified, err := merkletree.VerifyProofUsing(tx.Hash, true, tx.MerkleProof, [][]byte{blockHeader.Hash}, keccak256.New())
if err != nil {
return fmt.Errorf("failed to verify tx hash merkle proof: %s", err.Error())
}
if !txProofVerified {
return fmt.Errorf("transaction doesn't present in block hash merkle tree")
}
if !tx.ValidateHash() {
return fmt.Errorf("transaction hash is invalid")
}
return nil
}

17
cache/cache.go vendored Normal file
View File

@ -0,0 +1,17 @@
package cache
import (
"errors"
"time"
)
var ErrNotFound = errors.New("key doesn't exist in cache")
type Cache interface {
Store(key string, value interface{}) error
StoreWithTTL(key string, value interface{}, ttl time.Duration) error
Get(key string, value interface{}) error
Delete(key string)
Items() map[string]interface{}
Exists(key string) bool
}

View File

@ -1,9 +0,0 @@
package cache
import "github.com/Secured-Finance/dione/contracts/dioneOracle"
type EventCache interface {
Store(key string, event interface{}) error
GetOracleRequestEvent(key string) (*dioneOracle.DioneOracleNewOracleRequest, error)
Delete(key string)
}

View File

@ -1,50 +0,0 @@
package cache
import (
"github.com/Secured-Finance/dione/contracts/dioneOracle"
"github.com/VictoriaMetrics/fastcache"
"github.com/fxamacker/cbor/v2"
)
const (
// in megabytes
DefaultEventLogCacheCapacity = 32000000
)
type EventLogCache struct {
cache *fastcache.Cache
}
func NewEventLogCache() *EventLogCache {
return &EventLogCache{
cache: fastcache.New(DefaultEventLogCacheCapacity),
}
}
func (elc *EventLogCache) Store(key string, event interface{}) error {
mRes, err := cbor.Marshal(event)
if err != nil {
return err
}
elc.cache.SetBig([]byte(key), mRes)
return nil
}
func (elc *EventLogCache) GetOracleRequestEvent(key string) (*dioneOracle.DioneOracleNewOracleRequest, error) {
var mData []byte
mData = elc.cache.GetBig(mData, []byte(key))
var event *dioneOracle.DioneOracleNewOracleRequest
err := cbor.Unmarshal(mData, &event)
if err != nil {
return nil, err
}
return event, nil
}
func (elc *EventLogCache) Delete(key string) {
elc.cache.Del([]byte(key))
}

View File

@ -1,58 +0,0 @@
package cache
import (
"context"
"github.com/Secured-Finance/dione/config"
"github.com/Secured-Finance/dione/contracts/dioneOracle"
"github.com/fxamacker/cbor/v2"
"github.com/go-redis/redis/v8"
)
type EventRedisCache struct {
Client *redis.Client
ctx context.Context
}
func NewEventRedisCache(config *config.Config) *EventRedisCache {
client := redis.NewClient(&redis.Options{
Addr: config.Redis.Addr,
Password: config.Redis.Password,
DB: config.Redis.DB,
})
return &EventRedisCache{
Client: client,
ctx: context.Background(),
}
}
func (erc *EventRedisCache) Store(key string, event interface{}) error {
mRes, err := cbor.Marshal(event)
if err != nil {
return err
}
erc.Client.Set(erc.ctx, key, mRes, 0)
return nil
}
func (erc *EventRedisCache) GetOracleRequestEvent(key string) (*dioneOracle.DioneOracleNewOracleRequest, error) {
mData, err := erc.Client.Get(erc.ctx, key).Bytes()
if err != nil {
return nil, err
}
var event *dioneOracle.DioneOracleNewOracleRequest
err = cbor.Unmarshal(mData, &event)
if err != nil {
return nil, err
}
return event, nil
}
func (erc *EventRedisCache) Delete(key string) {
erc.Client.Del(erc.ctx, key)
}

69
cache/inmemory_cache.go vendored Normal file
View File

@ -0,0 +1,69 @@
package cache
import (
"fmt"
"reflect"
"time"
"github.com/patrickmn/go-cache"
)
const (
DefaultCacheExpiration = 5 * time.Minute
DefaultGCInterval = 10 * time.Minute
)
type InMemoryCache struct {
cache *cache.Cache
}
func NewInMemoryCache() Cache {
return &InMemoryCache{
cache: cache.New(DefaultCacheExpiration, DefaultGCInterval),
}
}
func (imc *InMemoryCache) Store(key string, value interface{}) error {
imc.cache.Set(key, value, cache.NoExpiration)
return nil
}
func (imc *InMemoryCache) StoreWithTTL(key string, value interface{}, ttl time.Duration) error {
imc.cache.Set(key, value, ttl)
return nil
}
func (imc *InMemoryCache) Get(key string, value interface{}) error {
v, exists := imc.cache.Get(key)
if !exists {
return ErrNotFound
}
reflectedValue := reflect.ValueOf(value)
if reflectedValue.Kind() != reflect.Ptr {
return fmt.Errorf("value isn't a pointer")
}
if reflectedValue.IsNil() {
reflectedValue.Set(reflect.New(reflectedValue.Type().Elem()))
}
reflectedValue.Elem().Set(reflect.ValueOf(v).Elem())
return nil
}
func (imc *InMemoryCache) Delete(key string) {
imc.cache.Delete(key)
}
func (imc *InMemoryCache) Items() map[string]interface{} {
m := make(map[string]interface{})
for k, v := range imc.cache.Items() {
m[k] = v.Object
}
return m
}
func (imc *InMemoryCache) Exists(key string) bool {
_, exists := imc.cache.Get(key)
return exists
}

98
cache/redis_cache.go vendored Normal file
View File

@ -0,0 +1,98 @@
package cache
import (
"bytes"
"context"
"encoding/gob"
"errors"
"time"
"github.com/Secured-Finance/dione/config"
"github.com/go-redis/redis/v8"
)
type RedisCache struct {
Client *redis.Client
ctx context.Context
}
func NewRedisCache(config *config.Config) *RedisCache {
client := redis.NewClient(&redis.Options{
Addr: config.Redis.Addr,
Password: config.Redis.Password,
DB: config.Redis.DB,
})
return &RedisCache{
Client: client,
ctx: context.Background(),
}
}
func (rc *RedisCache) Store(key string, value interface{}) error {
data, err := gobMarshal(value)
if err != nil {
return err
}
rc.Client.Set(rc.ctx, key, data, 0)
return nil
}
func (rc *RedisCache) StoreWithTTL(key string, value interface{}, ttl time.Duration) error {
data, err := gobMarshal(value)
if err != nil {
return err
}
rc.Client.Set(rc.ctx, key, data, ttl)
return nil
}
func gobMarshal(val interface{}) ([]byte, error) {
buf := bytes.NewBuffer(nil)
enc := gob.NewEncoder(buf)
err := enc.Encode(val)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func gobUnmarshal(data []byte, val interface{}) error {
buf := bytes.NewBuffer(data)
dec := gob.NewDecoder(buf)
return dec.Decode(&val)
}
func (rc *RedisCache) Get(key string, value interface{}) error {
data, err := rc.Client.Get(rc.ctx, key).Bytes()
if err != nil {
if errors.Is(err, redis.Nil) {
return ErrNotFound
}
return err
}
return gobUnmarshal(data, &value)
}
func (rc *RedisCache) Delete(key string) {
rc.Client.Del(rc.ctx, key)
}
func (rc *RedisCache) Items() map[string]interface{} {
return nil // TODO
}
func (rc *RedisCache) Exists(key string) bool {
res := rc.Client.Exists(context.TODO(), key)
if res.Err() != nil {
return false
}
if res.Val() == 0 {
return false
}
return true
}

View File

@ -14,11 +14,13 @@ type Config struct {
Rendezvous string `mapstructure:"rendezvous"`
Ethereum EthereumConfig `mapstructure:"ethereum"`
Filecoin FilecoinConfig `mapstructure:"filecoin"`
PubSub PubSubConfig `mapstructure:"pubSub"`
PubSub PubSubConfig `mapstructure:"pubsub"`
Store StoreConfig `mapstructure:"store"`
ConsensusMinApprovals int `mapstructure:"consensus_min_approvals"`
Redis RedisConfig `mapstructure:"redis"`
CacheType string `mapstructure:"cache_type"`
Blockchain BlockchainConfig `mapstructure:"blockchain"`
PrivateKeyPath string `mapstructure:"private_key_path"`
}
type EthereumConfig struct {
@ -39,8 +41,7 @@ type FilecoinConfig struct {
}
type PubSubConfig struct {
ProtocolID string `mapstructure:"protocolID"`
ServiceTopicName string `mapstructure:"serviceTopicName"`
ServiceTopicName string `mapstructure:"service_topic_name"`
}
type StoreConfig struct {
@ -53,6 +54,10 @@ type RedisConfig struct {
DB int `mapstructure:"redis_db"`
}
type BlockchainConfig struct {
DatabasePath string `mapstructure:"database_path"`
}
// NewConfig creates a new config based on default values or provided .env file
func NewConfig(configPath string) (*Config, error) {
dbName := "dione"
@ -64,12 +69,12 @@ func NewConfig(configPath string) (*Config, error) {
ListenAddr: "localhost",
ListenPort: 8000,
BootstrapNodes: []string{"/ip4/127.0.0.1/tcp/0"},
Rendezvous: "filecoin-p2p-oracle",
Rendezvous: "dione",
Ethereum: EthereumConfig{
PrivateKey: "",
},
PubSub: PubSubConfig{
ProtocolID: "p2p-oracle",
ServiceTopicName: "dione",
},
Store: StoreConfig{
DatabaseURL: dbURL,

View File

@ -28,3 +28,5 @@ func NewDrandConfig() *DrandConfig {
}
return cfg
}
var DrandChainGenesisTime = uint64(1603603302)

View File

@ -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
View File

@ -0,0 +1,5 @@
package config
import "math/big"
var ExpectedLeadersPerEpoch = big.NewInt(2)

View File

@ -1,169 +1,424 @@
package consensus
import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"sync"
"sort"
"time"
"github.com/Secured-Finance/dione/cache"
drand2 "github.com/Secured-Finance/dione/beacon/drand"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/fxamacker/cbor/v2"
"github.com/asaskevich/EventBus"
"github.com/Secured-Finance/dione/blockchain"
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"
types2 "github.com/Secured-Finance/dione/types"
"github.com/Secured-Finance/dione/ethclient"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/pubsub"
types2 "github.com/Secured-Finance/dione/types"
)
var (
ErrNoAcceptedBlocks = errors.New("there is no accepted blocks")
)
type PBFTConsensusManager struct {
bus EventBus.Bus
psb *pubsub.PubSubRouter
minApprovals int
privKey []byte
msgLog *MessageLog
privKey crypto.PrivKey
validator *ConsensusValidator
consensusMap map[string]*Consensus
ethereumClient *ethclient.EthereumClient
miner *Miner
eventCache cache.EventCache
miner *blockchain.Miner
consensusRoundPool *ConsensusStatePool
mempool *pool.Mempool
blockchain *blockchain.BlockChain
address peer.ID
stateChangeChannels map[string]map[State][]chan bool
}
type Consensus struct {
mutex sync.Mutex
Finished bool
IsCurrentMinerLeader bool
Task *types2.DioneTask
func NewPBFTConsensusManager(
bus EventBus.Bus,
psb *pubsub.PubSubRouter,
privKey crypto.PrivKey,
ethereumClient *ethclient.EthereumClient,
miner *blockchain.Miner,
bc *blockchain.BlockChain,
bp *ConsensusStatePool,
db *drand2.DrandBeacon,
mempool *pool.Mempool,
address peer.ID,
) *PBFTConsensusManager {
pcm := &PBFTConsensusManager{
psb: psb,
miner: miner,
validator: NewConsensusValidator(miner, bc, db),
privKey: privKey,
ethereumClient: ethereumClient,
bus: bus,
consensusRoundPool: bp,
mempool: mempool,
blockchain: bc,
address: address,
stateChangeChannels: map[string]map[State][]chan bool{},
}
func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey []byte, ethereumClient *ethclient.EthereumClient, miner *Miner, evc cache.EventCache) *PBFTConsensusManager {
pcm := &PBFTConsensusManager{}
pcm.psb = psb
pcm.miner = miner
pcm.validator = NewConsensusValidator(evc, miner)
pcm.msgLog = NewMessageLog()
pcm.minApprovals = minApprovals
pcm.privKey = privKey
pcm.ethereumClient = ethereumClient
pcm.eventCache = evc
pcm.consensusMap = map[string]*Consensus{}
pcm.psb.Hook(types.MessageTypePrePrepare, pcm.handlePrePrepare)
pcm.psb.Hook(types.MessageTypePrepare, pcm.handlePrepare)
pcm.psb.Hook(types.MessageTypeCommit, pcm.handleCommit)
pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare)
pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare)
pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit)
pcm.bus.SubscribeAsync("beacon:newEntry", func(entry types2.BeaconEntry) {
pcm.onNewBeaconEntry(entry)
}, true)
pcm.bus.SubscribeAsync("consensus:newState", func(block *types3.Block, newStateNumber int) {
newState := State(newStateNumber) // hacky, because reflection panics if we pass int to a handler which has type-alias for int
consensusMessageType := types.ConsensusMessageTypeUnknown
switch newState {
case StateStatusPrePrepared:
{
logrus.WithField("blockHash", fmt.Sprintf("%x", block.Header.Hash)).Debugf("Entered into PREPREPARED state")
if *block.Header.Proposer == pcm.address {
return
}
consensusMessageType = types.ConsensusMessageTypePrepare
break
}
case StateStatusPrepared:
{
consensusMessageType = types.ConsensusMessageTypeCommit
logrus.WithField("blockHash", fmt.Sprintf("%x", block.Header.Hash)).Debugf("Entered into PREPARED state")
break
}
case StateStatusCommited:
{
logrus.WithField("blockHash", fmt.Sprintf("%x", block.Header.Hash)).Debugf("Entered into COMMITTED state")
break
}
}
if consensusMessageType == types.ConsensusMessageTypeUnknown {
return
}
message, err := NewMessage(&types.ConsensusMessage{
Type: consensusMessageType,
Blockhash: block.Header.Hash,
}, pcm.privKey)
if err != nil {
logrus.Errorf("Failed to create consensus message: %v", err)
return
}
if err = pcm.psb.BroadcastToServiceTopic(message); err != nil {
logrus.Errorf("Failed to send consensus message: %s", err.Error())
return
}
}, true)
return pcm
}
func (pcm *PBFTConsensusManager) Propose(task types2.DioneTask) error {
pcm.createConsensusInfo(&task, true)
prePrepareMsg, err := CreatePrePrepareWithTaskSignature(&task, pcm.privKey)
func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error {
cmsg := &types.ConsensusMessage{
Type: StateStatusPrePrepared,
Block: blk,
Blockhash: blk.Header.Hash,
From: pcm.address,
}
prePrepareMsg, err := NewMessage(cmsg, pcm.privKey)
if err != nil {
return err
}
pcm.psb.BroadcastToServiceTopic(prePrepareMsg)
time.Sleep(1 * time.Second) // wait until all nodes will commit previous blocks
if err = pcm.consensusRoundPool.InsertMessageIntoLog(cmsg); err != nil {
return err
}
if err = pcm.psb.BroadcastToServiceTopic(prePrepareMsg); err != nil {
return err
}
logrus.WithField("blockHash", fmt.Sprintf("%x", blk.Header.Hash)).Debugf("Entered into PREPREPARED state")
return nil
}
func (pcm *PBFTConsensusManager) handlePrePrepare(message *types.Message) {
if message.Payload.Task.Miner == pcm.miner.address {
return
}
if pcm.msgLog.Exists(*message) {
logrus.Debugf("received existing pre_prepare msg, dropping...")
return
}
if !pcm.validator.Valid(*message) {
logrus.Warn("received invalid pre_prepare msg, dropping...")
return
}
pcm.msgLog.AddMessage(*message)
prepareMsg, err := NewMessage(message, types.MessageTypePrepare)
func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) {
var prePrepare types.PrePrepareMessage
err := cbor.Unmarshal(message.Payload, &prePrepare)
if err != nil {
logrus.Errorf("failed to create prepare message: %v", err)
}
pcm.createConsensusInfo(&message.Payload.Task, false)
pcm.psb.BroadcastToServiceTopic(&prepareMsg)
}
func (pcm *PBFTConsensusManager) handlePrepare(message *types.Message) {
if pcm.msgLog.Exists(*message) {
logrus.Debugf("received existing prepare msg, dropping...")
return
}
if !pcm.validator.Valid(*message) {
logrus.Warn("received invalid prepare msg, dropping...")
logrus.Errorf("failed to convert payload to PrePrepare message: %s", err.Error())
return
}
pcm.msgLog.AddMessage(*message)
if *prePrepare.Block.Header.Proposer == pcm.address {
return
}
if len(pcm.msgLog.GetMessagesByTypeAndConsensusID(types.MessageTypePrepare, message.Payload.Task.ConsensusID)) >= pcm.minApprovals {
commitMsg, err := NewMessage(message, types.MessageTypeCommit)
cmsg := &types.ConsensusMessage{
Type: types.ConsensusMessageTypePrePrepare,
From: message.From,
Block: prePrepare.Block,
Blockhash: prePrepare.Block.Header.Hash,
}
if !pcm.validator.Valid(cmsg) {
logrus.WithField("blockHash", hex.EncodeToString(cmsg.Block.Header.Hash)).Warn("Received invalid PREPREPARE for block")
return
}
err = pcm.consensusRoundPool.InsertMessageIntoLog(cmsg)
if err != nil {
logrus.Errorf("failed to create commit message: %w", err)
}
pcm.psb.BroadcastToServiceTopic(&commitMsg)
}
}
func (pcm *PBFTConsensusManager) handleCommit(message *types.Message) {
if pcm.msgLog.Exists(*message) {
logrus.Debugf("received existing commit msg, dropping...")
return
}
if !pcm.validator.Valid(*message) {
logrus.Warn("received invalid commit msg, dropping...")
logrus.WithField("err", err.Error()).Warn("Failed to add PREPARE message to log")
return
}
pcm.msgLog.AddMessage(*message)
logrus.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%x", cmsg.Block.Header.Hash),
"from": message.From.String(),
}).Debug("Received PREPREPARE message")
}
consensusMsg := message.Payload
if len(pcm.msgLog.GetMessagesByTypeAndConsensusID(types.MessageTypeCommit, message.Payload.Task.ConsensusID)) >= pcm.minApprovals {
info := pcm.GetConsensusInfo(consensusMsg.Task.ConsensusID)
if info == nil {
logrus.Debugf("consensus doesn't exist in our consensus map - skipping...")
func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) {
var prepare types.PrepareMessage
err := cbor.Unmarshal(message.Payload, &prepare)
if err != nil {
logrus.Errorf("failed to convert payload to Prepare message: %s", err.Error())
return
}
info.mutex.Lock()
defer info.mutex.Unlock()
if info.Finished {
cmsg := &types.ConsensusMessage{
Type: types.ConsensusMessageTypePrepare,
From: message.From,
Blockhash: prepare.Blockhash,
Signature: prepare.Signature,
}
if !pcm.validator.Valid(cmsg) {
logrus.Warnf("received invalid prepare msg for block %x", cmsg.Blockhash)
return
}
if info.IsCurrentMinerLeader {
logrus.Infof("Submitting on-chain result for consensus ID: %s", consensusMsg.Task.ConsensusID)
reqID, ok := new(big.Int).SetString(consensusMsg.Task.RequestID, 10)
err = pcm.consensusRoundPool.InsertMessageIntoLog(cmsg)
if err != nil {
logrus.WithField("err", err.Error()).Warn("Failed to add PREPARE message to log")
return
}
logrus.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%x", cmsg.Blockhash),
"from": message.From.String(),
}).Debug("Received PREPARE message")
}
func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) {
var commit types.CommitMessage
err := cbor.Unmarshal(message.Payload, &commit)
if err != nil {
logrus.Errorf("failed to convert payload to Commit message: %s", err.Error())
return
}
cmsg := &types.ConsensusMessage{
Type: types.ConsensusMessageTypeCommit,
From: message.From,
Blockhash: commit.Blockhash,
Signature: commit.Signature,
}
if !pcm.validator.Valid(cmsg) {
logrus.Warnf("received invalid commit msg for block %x", cmsg.Blockhash)
return
}
err = pcm.consensusRoundPool.InsertMessageIntoLog(cmsg)
if err != nil {
logrus.WithField("err", err.Error()).Warn("Failed to add COMMIT message to log")
return
}
logrus.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%x", cmsg.Blockhash),
"from": message.From.String(),
}).Debug("Received COMMIT message")
}
func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) {
block, err := pcm.commitAcceptedBlocks()
height, _ := pcm.blockchain.GetLatestBlockHeight()
if err != nil {
if errors.Is(err, ErrNoAcceptedBlocks) {
logrus.WithFields(logrus.Fields{
"height": height + 1,
}).Infof("No accepted blocks in the current consensus round")
} else {
logrus.WithFields(logrus.Fields{
"height": height + 1,
"err": err.Error(),
}).Errorf("Failed to select the block in the current consensus round")
return
}
}
if block != nil {
// broadcast new block
var newBlockMessage pubsub.PubSubMessage
newBlockMessage.Type = pubsub.NewBlockMessageType
blockSerialized, err := cbor.Marshal(block)
if err != nil {
logrus.Errorf("Failed to serialize block %x for broadcasting!", block.Header.Hash)
} else {
newBlockMessage.Payload = blockSerialized
pcm.psb.BroadcastToServiceTopic(&newBlockMessage)
}
// if we are miner of this block
// then post dione tasks to target chains (currently, only Ethereum)
if block.Header.Proposer.String() == pcm.address.String() {
pcm.submitTasksFromBlock(block)
}
}
for k, v := range pcm.stateChangeChannels {
for k1, j := range v {
for _, ch := range j {
ch <- true
close(ch)
}
delete(v, k1)
}
delete(pcm.stateChangeChannels, k)
}
minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round)
if err != nil {
if errors.Is(err, blockchain.ErrNoTxForBlock) {
logrus.Info("Sealing skipped, no transactions in mempool")
} else {
logrus.Errorf("Failed to mine the block: %s", err.Error())
}
return
}
// 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
}
}
}
func (pcm *PBFTConsensusManager) submitTasksFromBlock(block *types3.Block) {
for _, tx := range block.Data {
var task types2.DioneTask
err := cbor.Unmarshal(tx.Data, &task)
if err != nil {
logrus.WithFields(logrus.Fields{
"err": err.Error(),
"txHash": hex.EncodeToString(tx.Hash),
}).Error("Failed to unmarshal transaction payload")
continue // FIXME
}
reqIDNumber, ok := big.NewInt(0).SetString(task.RequestID, 10)
if !ok {
logrus.Errorf("Failed to parse request ID: %v", consensusMsg.Task.RequestID)
logrus.WithFields(logrus.Fields{
"txHash": hex.EncodeToString(tx.Hash),
}).Error("Failed to parse request id number in Dione task")
continue // FIXME
}
err := pcm.ethereumClient.SubmitRequestAnswer(reqID, consensusMsg.Task.Payload)
err = pcm.ethereumClient.SubmitRequestAnswer(reqIDNumber, task.Payload)
if err != nil {
logrus.Errorf("Failed to submit on-chain result: %v", err)
logrus.WithFields(logrus.Fields{
"err": err.Error(),
"txHash": hex.EncodeToString(tx.Hash),
"reqID": reqIDNumber.String(),
}).Error("Failed to submit task to ETH chain")
continue // FIXME
}
logrus.WithFields(logrus.Fields{
"txHash": hex.EncodeToString(tx.Hash),
"reqID": reqIDNumber.String(),
}).Debug("Dione task has been sucessfully submitted to ETH chain (DioneOracle contract)")
}
}
info.Finished = true
func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) {
blocks := pcm.consensusRoundPool.GetAllBlocksWithCommit()
if blocks == nil {
return nil, ErrNoAcceptedBlocks
}
var selectedBlock *types3.Block
sort.Slice(blocks, func(i, j int) bool {
iStake, err := pcm.ethereumClient.GetMinerStake(blocks[i].Block.Header.ProposerEth)
if err != nil {
logrus.Error(err)
return false
}
func (pcm *PBFTConsensusManager) createConsensusInfo(task *types2.DioneTask, isLeader bool) {
if _, ok := pcm.consensusMap[task.ConsensusID]; !ok {
pcm.consensusMap[task.ConsensusID] = &Consensus{
IsCurrentMinerLeader: isLeader,
Task: task,
Finished: false,
}
}
jStake, err := pcm.ethereumClient.GetMinerStake(blocks[j].Block.Header.ProposerEth)
if err != nil {
logrus.Error(err)
return false
}
func (pcm *PBFTConsensusManager) GetConsensusInfo(consensusID string) *Consensus {
c, ok := pcm.consensusMap[consensusID]
if !ok {
return nil
if iStake.Cmp(jStake) == -1 {
return false
} else if iStake.Cmp(jStake) == 1 {
return true
} else {
return blocks[i].Block.Header.Timestamp > blocks[j].Block.Header.Timestamp
}
})
selectedBlock = blocks[0].Block
stake, err := pcm.ethereumClient.GetMinerStake(selectedBlock.Header.ProposerEth)
if err != nil {
logrus.Error(err)
return nil, err
}
return c
logrus.WithFields(logrus.Fields{
"winCount": selectedBlock.Header.ElectionProof.WinCount,
"proposerStake": stake.String(),
}).Debug("Selected the block with maximal win count and proposer's stake.")
logrus.WithFields(logrus.Fields{
"hash": hex.EncodeToString(selectedBlock.Header.Hash),
"height": selectedBlock.Header.Height,
"miner": selectedBlock.Header.Proposer.String(),
}).Info("Committed new block")
pcm.consensusRoundPool.Prune()
for _, v := range selectedBlock.Data {
err := pcm.mempool.DeleteTx(v.Hash)
if err != nil {
logrus.WithFields(logrus.Fields{
"err": err.Error(),
"tx": hex.EncodeToString(v.Hash),
}).Errorf("Failed to delete committed tx from mempool")
continue
}
}
return selectedBlock, pcm.blockchain.StoreBlock(selectedBlock)
}

View File

@ -0,0 +1,126 @@
package consensus
import (
"encoding/hex"
"fmt"
"sync"
types2 "github.com/Secured-Finance/dione/consensus/types"
"github.com/Secured-Finance/dione/blockchain/pool"
"github.com/asaskevich/EventBus"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/blockchain/types"
)
type State uint8
const (
StateStatusUnknown = iota
StateStatusPrePrepared
StateStatusPrepared
StateStatusCommited
)
// ConsensusStatePool is pool for blocks that isn't not validated or committed yet
type ConsensusStatePool struct {
mempool *pool.Mempool
consensusInfoMap map[string]*ConsensusInfo
mapMutex sync.Mutex
bus EventBus.Bus
minApprovals int // FIXME
}
func NewConsensusRoundPool(mp *pool.Mempool, bus EventBus.Bus, minApprovals int) (*ConsensusStatePool, error) {
bp := &ConsensusStatePool{
consensusInfoMap: map[string]*ConsensusInfo{},
mempool: mp,
bus: bus,
minApprovals: minApprovals,
}
return bp, nil
}
type ConsensusInfo struct {
Blockhash []byte
Block *types.Block
State State
MessageLog *ConsensusMessageLog
}
func (crp *ConsensusStatePool) InsertMessageIntoLog(cmsg *types2.ConsensusMessage) error {
crp.mapMutex.Lock()
defer crp.mapMutex.Unlock()
consensusInfo, ok := crp.consensusInfoMap[hex.EncodeToString(cmsg.Blockhash)]
if !ok {
consensusInfo = &ConsensusInfo{
Block: cmsg.Block,
Blockhash: cmsg.Blockhash,
State: StateStatusUnknown,
MessageLog: NewConsensusMessageLog(),
}
crp.consensusInfoMap[hex.EncodeToString(cmsg.Blockhash)] = consensusInfo
}
added := consensusInfo.MessageLog.AddMessage(cmsg)
if !added {
return fmt.Errorf("consensus message already exists in message log")
}
crp.maybeUpdateConsensusState(consensusInfo, cmsg)
return nil
}
func (crp *ConsensusStatePool) maybeUpdateConsensusState(ci *ConsensusInfo, cmsg *types2.ConsensusMessage) {
if ci.State == StateStatusUnknown && cmsg.Type == types2.ConsensusMessageTypePrePrepare && cmsg.Block != nil {
ci.Block = cmsg.Block
logrus.WithField("hash", fmt.Sprintf("%x", cmsg.Block.Header.Hash)).Debug("New block discovered")
ci.State = StateStatusPrePrepared
crp.bus.Publish("consensus:newState", ci.Block, StateStatusPrePrepared)
}
if len(ci.MessageLog.Get(types2.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= crp.minApprovals-1 && ci.State == StateStatusPrePrepared { // FIXME approval across 2f nodes
ci.State = StateStatusPrepared
crp.bus.Publish("consensus:newState", ci.Block, StateStatusPrepared)
}
if len(ci.MessageLog.Get(types2.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= crp.minApprovals && ci.State == StateStatusPrepared { // FIXME approval across 2f+1 nodes
ci.State = StateStatusCommited
crp.bus.Publish("consensus:newState", ci.Block, StateStatusCommited)
}
}
// Prune cleans known blocks list. It is called when new consensus round starts.
func (crp *ConsensusStatePool) Prune() {
for k := range crp.consensusInfoMap {
delete(crp.consensusInfoMap, k)
}
crp.bus.Publish("blockpool:pruned")
}
func (crp *ConsensusStatePool) GetAllBlocksWithCommit() []*ConsensusInfo {
crp.mapMutex.Lock()
defer crp.mapMutex.Unlock()
var consensusInfos []*ConsensusInfo
for _, v := range crp.consensusInfoMap {
if v.State == StateStatusCommited {
consensusInfos = append(consensusInfos, v)
}
}
return consensusInfos
}
//func containsTx(s []*types.Transaction, e *types.Transaction) bool {
// for _, a := range s {
// if bytes.Equal(a.Hash, e.Hash) {
// return true
// }
// }
// return false
//}

View File

@ -1,156 +1,62 @@
package consensus
import (
"github.com/Secured-Finance/dione/cache"
types2 "github.com/Secured-Finance/dione/consensus/types"
"github.com/Secured-Finance/dione/consensus/validation"
"github.com/Secured-Finance/dione/types"
"github.com/ethereum/go-ethereum/common"
"github.com/filecoin-project/go-state-types/crypto"
"encoding/hex"
drand2 "github.com/Secured-Finance/dione/beacon/drand"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/blockchain"
types2 "github.com/Secured-Finance/dione/consensus/types"
)
type ConsensusValidator struct {
validationFuncMap map[types2.MessageType]func(msg types2.Message) bool
eventCache cache.EventCache
miner *Miner
validationFuncMap map[types2.ConsensusMessageType]func(msg *types2.ConsensusMessage) bool
miner *blockchain.Miner
beacon *drand2.DrandBeacon
blockchain *blockchain.BlockChain
}
func NewConsensusValidator(ec cache.EventCache, miner *Miner) *ConsensusValidator {
func NewConsensusValidator(miner *blockchain.Miner, bc *blockchain.BlockChain, db *drand2.DrandBeacon) *ConsensusValidator {
cv := &ConsensusValidator{
eventCache: ec,
miner: miner,
blockchain: bc,
beacon: db,
}
cv.validationFuncMap = map[types2.MessageType]func(msg types2.Message) bool{
types2.MessageTypePrePrepare: func(msg types2.Message) bool {
// TODO here we need to do validation of tx itself
consensusMsg := msg.Payload
// === verify task signature ===
err := VerifyTaskSignature(consensusMsg.Task)
if err != nil {
logrus.Errorf("unable to verify signature: %v", err)
return false
}
/////////////////////////////////
// === verify if request exists in event log cache ===
requestEvent, err := cv.eventCache.GetOracleRequestEvent("request_" + consensusMsg.Task.RequestID)
if err != nil {
logrus.Errorf("the incoming request task event doesn't exist in the EVC, or is broken: %v", err)
return false
}
if requestEvent.OriginChain != consensusMsg.Task.OriginChain ||
requestEvent.RequestType != consensusMsg.Task.RequestType ||
requestEvent.RequestParams != consensusMsg.Task.RequestParams {
logrus.Errorf("the incoming task and cached request event don't match!")
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 = cv.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 := consensusMsg.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 := cv.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
}
//////////////////////////////////////
// === validate payload by specific-chain checks ===
if validationFunc := validation.GetValidationMethod(consensusMsg.Task.OriginChain, consensusMsg.Task.RequestType); validationFunc != nil {
err := validationFunc(consensusMsg.Task.Payload)
if err != nil {
logrus.Errorf("payload validation has failed: %v", err)
return false
}
} else {
logrus.Debugf("Origin chain [%v]/request type[%v] doesn't have any payload validation!", consensusMsg.Task.OriginChain, consensusMsg.Task.RequestType)
}
/////////////////////////////////
return true
},
types2.MessageTypePrepare: func(msg types2.Message) bool {
err := VerifyTaskSignature(msg.Payload.Task)
if err != nil {
return false
}
return true
},
types2.MessageTypeCommit: func(msg types2.Message) bool {
err := VerifyTaskSignature(msg.Payload.Task)
if err != nil {
cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg *types2.ConsensusMessage) bool{
types2.ConsensusMessageTypePrePrepare: func(msg *types2.ConsensusMessage) bool {
if err := cv.blockchain.ValidateBlock(msg.Block); err != nil {
logrus.WithFields(logrus.Fields{
"blockHash": hex.EncodeToString(msg.Block.Header.Hash),
"err": err.Error(),
}).Error("failed to validate block from PrePrepare message")
return false
}
return true
},
types2.ConsensusMessageTypePrepare: checkSignatureForBlockhash,
types2.ConsensusMessageTypeCommit: checkSignatureForBlockhash,
}
return cv
}
func (cv *ConsensusValidator) Valid(msg types2.Message) bool {
func (cv *ConsensusValidator) Valid(msg *types2.ConsensusMessage) bool {
return cv.validationFuncMap[msg.Type](msg)
}
func checkSignatureForBlockhash(msg *types2.ConsensusMessage) bool {
pubKey, err := msg.From.ExtractPublicKey()
if err != nil {
// TODO logging
return false
}
ok, err := pubKey.Verify(msg.Blockhash, msg.Signature)
if err != nil {
// TODO logging
return false
}
return ok
}

View File

@ -5,14 +5,21 @@ import (
"encoding/hex"
"time"
"math/big"
"github.com/ethereum/go-ethereum/event"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/types"
"github.com/fxamacker/cbor/v2"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/sha3"
"github.com/Secured-Finance/dione/blockchain"
"github.com/Secured-Finance/dione/contracts/dioneDispute"
"github.com/Secured-Finance/dione/contracts/dioneOracle"
"github.com/Secured-Finance/dione/ethclient"
"github.com/ethereum/go-ethereum/common"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/sha3"
)
type DisputeManager struct {
@ -22,15 +29,22 @@ type DisputeManager struct {
submissionMap map[string]*dioneOracle.DioneOracleSubmittedOracleRequest
disputeMap map[string]*dioneDispute.DioneDisputeNewDispute
voteWindow time.Duration
blockchain *blockchain.BlockChain
submissionChan chan *dioneOracle.DioneOracleSubmittedOracleRequest
submissionSubscription event.Subscription
disputesChan chan *dioneDispute.DioneDisputeNewDispute
disputesSubscription event.Subscription
}
func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, pcm *PBFTConsensusManager, voteWindow int) (*DisputeManager, error) {
newSubmittionsChan, submSubscription, err := ethClient.SubscribeOnNewSubmittions(ctx)
func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, pcm *PBFTConsensusManager, voteWindow int, bc *blockchain.BlockChain) (*DisputeManager, error) {
submissionChan, submSubscription, err := ethClient.SubscribeOnNewSubmittions(ctx)
if err != nil {
return nil, err
}
newDisputesChan, dispSubscription, err := ethClient.SubscribeOnNewDisputes(ctx)
disputesChan, dispSubscription, err := ethClient.SubscribeOnNewDisputes(ctx)
if err != nil {
return nil, err
}
@ -42,54 +56,56 @@ func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient,
submissionMap: map[string]*dioneOracle.DioneOracleSubmittedOracleRequest{},
disputeMap: map[string]*dioneDispute.DioneDisputeNewDispute{},
voteWindow: time.Duration(voteWindow) * time.Second,
blockchain: bc,
submissionChan: submissionChan,
submissionSubscription: submSubscription,
disputesChan: disputesChan,
disputesSubscription: dispSubscription,
}
return dm, nil
}
func (dm *DisputeManager) Run(ctx context.Context) {
go func() {
for {
select {
case <-ctx.Done():
{
submSubscription.Unsubscribe()
dispSubscription.Unsubscribe()
dm.disputesSubscription.Unsubscribe()
dm.disputesSubscription.Unsubscribe()
return
}
case s := <-newSubmittionsChan:
case s := <-dm.submissionChan:
{
dm.onNewSubmission(s)
}
case d := <-newDisputesChan:
case d := <-dm.disputesChan:
{
dm.onNewDispute(d)
}
}
}
}()
return dm, nil
}
func (dm *DisputeManager) onNewSubmission(submittion *dioneOracle.DioneOracleSubmittedOracleRequest) {
c := dm.pcm.GetConsensusInfo(submittion.ReqID.String())
if c == nil {
// todo: warn
func (dm *DisputeManager) onNewSubmission(submission *dioneOracle.DioneOracleSubmittedOracleRequest) {
// find a block that contains the dione task with specified request id
task, block, err := dm.findTaskAndBlockWithRequestID(submission.ReqID.String())
if err != nil {
logrus.Error(err)
return
}
dm.submissionMap[submittion.ReqID.String()] = submittion
dm.submissionMap[submission.ReqID.String()] = submission
submHashBytes := sha3.Sum256(submittion.Data)
localHashBytes := sha3.Sum256(c.Task.Payload)
submHashBytes := sha3.Sum256(submission.Data)
localHashBytes := sha3.Sum256(task.Payload)
submHash := hex.EncodeToString(submHashBytes[:])
localHash := hex.EncodeToString(localHashBytes[:])
if submHash != localHash {
logrus.Debugf("submission of request id %s isn't valid - beginning dispute", c.Task.RequestID)
addr := common.HexToAddress(c.Task.MinerEth)
reqID, ok := big.NewInt(0).SetString(c.Task.RequestID, 10)
if !ok {
logrus.Errorf("cannot parse request id: %s", c.Task.RequestID)
return
}
err := dm.ethClient.BeginDispute(addr, reqID)
logrus.Debugf("submission of request id %s isn't valid - beginning dispute", submission.ReqID)
err := dm.ethClient.BeginDispute(block.Header.ProposerEth, submission.ReqID)
if err != nil {
logrus.Errorf(err.Error())
return
@ -102,7 +118,7 @@ func (dm *DisputeManager) onNewSubmission(submittion *dioneOracle.DioneOracleSub
return
case <-disputeFinishTimer.C:
{
d, ok := dm.disputeMap[reqID.String()]
d, ok := dm.disputeMap[submission.ReqID.String()]
if !ok {
logrus.Error("cannot finish dispute: it doesn't exist in manager's dispute map!")
return
@ -121,16 +137,45 @@ func (dm *DisputeManager) onNewSubmission(submittion *dioneOracle.DioneOracleSub
}
}
func (dm *DisputeManager) findTaskAndBlockWithRequestID(requestID string) (*types.DioneTask, *types2.Block, error) {
height, err := dm.blockchain.GetLatestBlockHeight()
if err != nil {
return nil, nil, err
}
for {
block, err := dm.blockchain.FetchBlockByHeight(height)
if err != nil {
return nil, nil, err
}
for _, v := range block.Data {
var task types.DioneTask
err := cbor.Unmarshal(v.Data, &task)
if err != nil {
logrus.Error(err)
continue
}
if task.RequestID == requestID {
return &task, block, nil
}
}
height--
}
}
func (dm *DisputeManager) onNewDispute(dispute *dioneDispute.DioneDisputeNewDispute) {
c := dm.pcm.GetConsensusInfo(dispute.RequestID.String())
if c == nil {
// todo: warn
task, _, err := dm.findTaskAndBlockWithRequestID(dispute.RequestID.String())
if err != nil {
logrus.Error(err)
return
}
subm, ok := dm.submissionMap[dispute.RequestID.String()]
if !ok {
// todo: warn
logrus.Warn("desired submission isn't found in map")
return
}
@ -141,7 +186,7 @@ func (dm *DisputeManager) onNewDispute(dispute *dioneDispute.DioneDisputeNewDisp
}
submHashBytes := sha3.Sum256(subm.Data)
localHashBytes := sha3.Sum256(c.Task.Payload)
localHashBytes := sha3.Sum256(task.Payload)
submHash := hex.EncodeToString(submHashBytes[:])
localHash := hex.EncodeToString(localHashBytes[:])
if submHash == localHash {
@ -152,7 +197,7 @@ func (dm *DisputeManager) onNewDispute(dispute *dioneDispute.DioneDisputeNewDisp
}
}
err := dm.ethClient.VoteDispute(dispute.Dhash, true)
err = dm.ethClient.VoteDispute(dispute.Dhash, true)
if err != nil {
logrus.Errorf(err.Error())
return

View File

@ -1,187 +0,0 @@
package consensus
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"
"github.com/Secured-Finance/dione/contracts/dioneOracle"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/ethclient"
"github.com/Secured-Finance/dione/types"
"github.com/ethereum/go-ethereum/common"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
type Miner struct {
address peer.ID
ethAddress common.Address
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,
beacon beacon.BeaconNetworks,
ethClient *ethclient.EthereumClient,
privateKey []byte,
) *Miner {
return &Miner{
address: address,
ethAddress: ethAddress,
beacon: beacon,
ethClient: ethClient,
privateKey: privateKey,
}
}
func (m *Miner) UpdateCurrentStakeInfo() error {
mStake, err := m.ethClient.GetMinerStake(m.ethAddress)
if err != nil {
logrus.Warn("Can't get miner stake", err)
return err
}
nStake, err := m.ethClient.GetTotalStake()
if err != nil {
logrus.Warn("Can't get miner stake", err)
return err
}
m.minerStake = *mStake
m.networkStake = *nStake
return nil
}
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 *dioneOracle.DioneOracleNewOracleRequest) (*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: ", beaconValues[1].Round)
randomBase := beaconValues[1]
if err := m.UpdateCurrentStakeInfo(); err != nil {
return nil, xerrors.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(
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)
}
if winner == nil {
return nil, nil
}
rpcMethod := rpc.GetRPCMethod(event.OriginChain, event.RequestType)
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)
}
return &types.DioneTask{
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) {
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)
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,42 +1,52 @@
package consensus
import (
"bytes"
types2 "github.com/Secured-Finance/dione/consensus/types"
mapset "github.com/Secured-Finance/golang-set"
)
type MessageLog struct {
type ConsensusMessageLog struct {
messages mapset.Set
maxLogSize int
validationFuncMap map[types2.MessageType]func(message types2.Message)
}
func NewMessageLog() *MessageLog {
msgLog := &MessageLog{
func NewConsensusMessageLog() *ConsensusMessageLog {
msgLog := &ConsensusMessageLog{
messages: mapset.NewSet(),
maxLogSize: 0, // TODO
}
return msgLog
}
func (ml *MessageLog) AddMessage(msg types2.Message) {
ml.messages.Add(msg)
func (ml *ConsensusMessageLog) AddMessage(msg *types2.ConsensusMessage) bool {
return ml.messages.Add(msg)
}
func (ml *MessageLog) Exists(msg types2.Message) bool {
func (ml *ConsensusMessageLog) Exists(msg *types2.ConsensusMessage) bool {
return ml.messages.Contains(msg)
}
func (ml *MessageLog) GetMessagesByTypeAndConsensusID(typ types2.MessageType, consensusID string) []types2.Message {
var result []types2.Message
func (ml *ConsensusMessageLog) Get(typ types2.ConsensusMessageType, blockhash []byte) []*types2.ConsensusMessage {
var result []*types2.ConsensusMessage
for v := range ml.messages.Iter() {
msg := v.(types2.Message)
if msg.Type == typ && msg.Payload.Task.ConsensusID == consensusID {
msg := v.(*types2.ConsensusMessage)
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)
}
}
}
return result
}

View File

@ -0,0 +1,7 @@
package policy
const (
BlockMaxTransactionCount = 100
MaxBlockCountForRetrieving = 500 // we do it just like in Bitcoin
MaxTransactionCountForRetrieving = 50000
)

View 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
}

View File

@ -1,26 +1,16 @@
package types
import (
"github.com/Secured-Finance/dione/types"
"github.com/libp2p/go-libp2p-core/peer"
types2 "github.com/Secured-Finance/dione/blockchain/types"
)
type MessageType uint8
const (
MessageTypeUnknown = MessageType(iota)
MessageTypePrePrepare
MessageTypePrepare
MessageTypeCommit
)
type ConsensusMessage struct {
Task types.DioneTask
type PrePrepareMessage struct {
Block *types2.Block
}
type Message struct {
Type MessageType
Payload ConsensusMessage
From peer.ID `cbor:"-"`
type PrepareMessage struct {
Blockhash []byte
Signature []byte
}
type CommitMessage PrepareMessage

View File

@ -1,133 +1,70 @@
package consensus
import (
"encoding/binary"
"fmt"
"github.com/fxamacker/cbor/v2"
"github.com/Secured-Finance/dione/pubsub"
types2 "github.com/Secured-Finance/dione/consensus/types"
"github.com/mitchellh/hashstructure/v2"
"github.com/Secured-Finance/dione/sigs"
"github.com/minio/blake2b-simd"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/types"
"github.com/filecoin-project/go-state-types/crypto"
"golang.org/x/xerrors"
"github.com/libp2p/go-libp2p-core/crypto"
)
type SignFunc func(peer.ID, []byte) (*types.Signature, error)
func ComputeVRF(sign SignFunc, worker peer.ID, sigInput []byte) ([]byte, error) {
sig, err := sign(worker, sigInput)
func NewMessage(cmsg *types2.ConsensusMessage, privKey crypto.PrivKey) (*pubsub.PubSubMessage, error) {
var message pubsub.PubSubMessage
switch cmsg.Type {
case types2.ConsensusMessageTypePrePrepare:
{
message.Type = pubsub.PrePrepareMessageType
msg := types2.PrePrepareMessage{
Block: cmsg.Block,
}
data, err := cbor.Marshal(msg)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert message to map: %s", err.Error())
}
if sig.Type != types.SigTypeEd25519 {
return nil, fmt.Errorf("miner worker address was not a Ed25519 key")
message.Payload = data
break
}
return sig.Data, nil
}
func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error {
err := sigs.Verify(&types.Signature{Type: types.SigTypeEd25519, Data: vrfproof}, []byte(worker), vrfBase)
case types2.ConsensusMessageTypePrepare:
{
message.Type = pubsub.PrepareMessageType
signature, err := privKey.Sign(cmsg.Blockhash)
if err != nil {
return xerrors.Errorf("vrf was invalid: %w", err)
return nil, fmt.Errorf("failed to create signature: %v", err)
}
return nil
pm := types2.PrepareMessage{
Blockhash: cmsg.Blockhash,
Signature: signature,
}
func IsRoundWinner(round types.DrandRound,
worker peer.ID, brand types.BeaconEntry, minerStake, networkStake types.BigInt, sign SignFunc) (*types.ElectionProof, error) {
buf, err := worker.MarshalBinary()
data, err := cbor.Marshal(pm)
if err != nil {
return nil, xerrors.Errorf("failed to marshal address: %w", err)
return nil, fmt.Errorf("failed to convert message to map: %s", err.Error())
}
electionRand, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf)
message.Payload = data
break
}
case types2.ConsensusMessageTypeCommit:
{
message.Type = pubsub.CommitMessageType
signature, err := privKey.Sign(cmsg.Blockhash)
if err != nil {
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
return nil, fmt.Errorf("failed to create signature: %v", err)
}
vrfout, err := ComputeVRF(sign, worker, electionRand)
pm := types2.CommitMessage{
Blockhash: cmsg.Blockhash,
Signature: signature,
}
data, err := cbor.Marshal(pm)
if err != nil {
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
return nil, fmt.Errorf("failed to convert message to map: %s", err.Error())
}
message.Payload = data
break
}
}
ep := &types.ElectionProof{VRFProof: vrfout}
j := ep.ComputeWinCount(minerStake, networkStake)
ep.WinCount = j
if j < 1 {
return nil, nil
}
return ep, nil
}
func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round types.DrandRound, entropy []byte) ([]byte, error) {
h := blake2b.New256()
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
return nil, xerrors.Errorf("deriving randomness: %v", err)
}
VRFDigest := blake2b.Sum256(rbase)
_, err := h.Write(VRFDigest[:])
if err != nil {
return nil, xerrors.Errorf("hashing VRFDigest: %w", err)
}
if err := binary.Write(h, binary.BigEndian, round); err != nil {
return nil, xerrors.Errorf("deriving randomness: %v", err)
}
_, err = h.Write(entropy)
if err != nil {
return nil, xerrors.Errorf("hashing entropy: %v", err)
}
return h.Sum(nil), nil
}
func VerifyTaskSignature(task types.DioneTask) error {
cHash, err := hashstructure.Hash(task, hashstructure.FormatV2, nil)
if err != nil {
return err
}
err = sigs.Verify(
&types.Signature{Type: types.SigTypeEd25519, Data: task.Signature},
[]byte(task.Miner),
[]byte(fmt.Sprintf("%v", cHash)),
)
if err != nil {
return err
}
return nil
}
func NewMessage(msg *types2.Message, typ types2.MessageType) (types2.Message, error) {
var newMsg types2.Message
newMsg.Type = typ
newCMsg := msg.Payload
newMsg.Payload = newCMsg
return newMsg, nil
}
func CreatePrePrepareWithTaskSignature(task *types.DioneTask, privateKey []byte) (*types2.Message, error) {
var message types2.Message
message.Type = types2.MessageTypePrePrepare
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
message.Payload = types2.ConsensusMessage{Task: *task}
return &message, nil
}

View File

@ -1,39 +1,40 @@
package filecoin
import (
"bytes"
"github.com/Secured-Finance/dione/types"
"github.com/Secured-Finance/dione/consensus/validation"
rtypes "github.com/Secured-Finance/dione/rpc/types"
ftypes "github.com/Secured-Finance/dione/rpc/filecoin/types"
"github.com/Secured-Finance/dione/sigs"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
func ValidateGetTransaction(payload []byte) error {
var msg ftypes.SignedMessage
if err := msg.UnmarshalCBOR(bytes.NewReader(payload)); err != nil {
if err := msg.Message.UnmarshalCBOR(bytes.NewReader(payload)); err != nil {
return xerrors.Errorf("cannot unmarshal payload: %s", err.Error())
}
func ValidateGetTransaction(task *types.DioneTask) error {
//var msg ftypes.SignedMessage
//if err := msg.UnmarshalCBOR(bytes.NewReader(payload)); err != nil {
// if err := msg.Message.UnmarshalCBOR(bytes.NewReader(payload)); err != nil {
// return xerrors.Errorf("cannot unmarshal payload: %s", err.Error())
// }
//}
//
//if msg.Type == ftypes.MessageTypeSecp256k1 {
// if err := sigs.Verify(&msg.Signature, msg.Message.From.Bytes(), msg.Message.Cid().Bytes()); err != nil {
// logrus.Errorf("Couldn't verify transaction %v", err)
// return xerrors.Errorf("Couldn't verify transaction: %v")
// }
// return nil
//} else {
// // TODO: BLS Signature verification
// return nil
//}
return validation.VerifyExactMatching(task)
}
if msg.Type == ftypes.MessageTypeSecp256k1 {
if err := sigs.Verify(&msg.Signature, msg.Message.From.Bytes(), msg.Message.Cid().Bytes()); err != nil {
logrus.Errorf("Couldn't verify transaction %v", err)
return xerrors.Errorf("Couldn't verify transaction: %v")
}
return nil
} else {
// TODO: BLS Signature verification
return nil
}
func ValidateGetBlock(task *types.DioneTask) error {
return validation.VerifyExactMatching(task)
}
func init() {
validation.RegisterValidation(rtypes.RPCTypeFilecoin, map[string]func([]byte) error{
validation.RegisterValidation(rtypes.RPCTypeFilecoin, map[string]func(*types.DioneTask) error{
"getTransaction": ValidateGetTransaction,
"getBlock": ValidateGetBlock,
})
}

View File

@ -1,12 +1,14 @@
package validation
var validations = map[uint8]map[string]func([]byte) error{} // rpcType -> {rpcMethodName -> actual func var}
import "github.com/Secured-Finance/dione/types"
func RegisterValidation(typ uint8, methods map[string]func([]byte) error) {
var validations = map[uint8]map[string]func(*types.DioneTask) error{} // rpcType -> {rpcMethodName -> actual func var}
func RegisterValidation(typ uint8, methods map[string]func(*types.DioneTask) error) {
validations[typ] = methods
}
func GetValidationMethod(typ uint8, methodName string) func([]byte) error {
func GetValidationMethod(typ uint8, methodName string) func(*types.DioneTask) error {
rpcMethods, ok := validations[typ]
if !ok {
return nil

View File

@ -0,0 +1,17 @@
package solana
import (
"github.com/Secured-Finance/dione/consensus/validation"
rtypes "github.com/Secured-Finance/dione/rpc/types"
"github.com/Secured-Finance/dione/types"
)
func ValidateGetTransaction(task *types.DioneTask) error {
return validation.VerifyExactMatching(task)
}
func init() {
validation.RegisterValidation(rtypes.RPCTypeSolana, map[string]func(*types.DioneTask) error{
"getTransaction": ValidateGetTransaction,
})
}

View File

@ -0,0 +1,24 @@
package validation
import (
"bytes"
"fmt"
"github.com/Secured-Finance/dione/rpc"
"github.com/Secured-Finance/dione/types"
)
func VerifyExactMatching(task *types.DioneTask) error {
rpcMethod := rpc.GetRPCMethod(task.OriginChain, task.RequestType)
if rpcMethod == nil {
return fmt.Errorf("invalid RPC method")
}
res, err := rpcMethod(task.RequestParams)
if err != nil {
return fmt.Errorf("failed to invoke RPC method: %w", err)
}
if bytes.Compare(res, task.Payload) != 0 {
return fmt.Errorf("actual rpc response doesn't match with task's payload")
}
return nil
}

View File

@ -1,23 +0,0 @@
package drand
import (
"context"
"encoding/hex"
"testing"
"github.com/Secured-Finance/dione/config"
drandClient "github.com/drand/drand/client/http"
"github.com/stretchr/testify/assert"
)
func TestPrintGroupInfo(t *testing.T) {
cfg := config.NewDrandConfig()
ctx := context.Background()
drandServer := cfg.Servers[0]
client, err := drandClient.New(drandServer, nil, nil)
assert.NoError(t, err)
drandResult, err := client.Get(ctx, 266966)
assert.NoError(t, err)
stringSha256 := hex.EncodeToString(drandResult.Randomness())
assert.Equal(t, stringSha256, "cb67e13477cad0e54540980a3b621dfd9c5fcd7c92ed42626289a1de6f25c3d1")
}

View File

@ -4,3 +4,4 @@ cache
dist
coverage
coverage.json
scripts/private

View File

@ -85,7 +85,7 @@ contract DioneOracle {
function submitOracleRequest(uint256 _reqID, bytes memory _data) public onlyPendingRequest(_reqID) returns (bool) {
require(pendingRequests[_reqID].deadline - int256(block.timestamp) >= 0, "submission has exceeded the deadline");
delete pendingRequests[_reqID];
dioneStaking.mine(msg.sender);
dioneStaking.mineAndStake(msg.sender);
pendingRequests[_reqID].callbackAddress.call(abi.encodeWithSelector(pendingRequests[_reqID].callbackMethodID, _reqID, _data));
emit SubmittedOracleRequest(_reqID, _data);
return true;

View File

@ -1,30 +1,28 @@
import { task } from "hardhat/config";
import "@nomiclabs/hardhat-ethers";
import "hardhat-tracer";
import "@nomiclabs/hardhat-waffle";
import "solidity-coverage";
import "hardhat-gas-reporter";
task("accounts", "Prints the list of accounts", async (args, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(await account.address);
}
});
import * as secrets from "./secrets.json";
import "./hardhat.tasks";
import "@nomiclabs/hardhat-etherscan";
export default {
solidity: "0.8.3",
networks: {
geth: {
url: `http://localhost:8545`,
accounts: {
mnemonic: "test test test test test test test test test test test junk"
solidity: {
version: "0.8.3",
settings: {
optimizer: {
enabled: false,
runs: 200
}
}
},
networks: secrets.networks,
gasReporter: {
currency: 'USD',
enabled: (process.env.REPORT_GAS) ? true : false
enabled: process.env.REPORT_GAS
},
etherscan: {
apiKey: secrets.etherscanApiKey
}
};

View File

@ -0,0 +1,34 @@
import { task } from "hardhat/config";
import "@nomiclabs/hardhat-ethers";
task("stake", "Prints an account's stake amount in DioneStaking contract")
.addParam("contract", "DioneStaking contract address")
.addParam("account", "The account's address")
.setAction(async (args, hre) => {
const DioneStaking = await hre.ethers.getContractFactory("DioneStaking");
const contract = DioneStaking.attach(args.contract);
const a = await contract.minerStake(args.account);
console.log("Stake amount:", a.div(hre.ethers.constants.WeiPerEther).toString());
});
task("accounts", "Prints the list of accounts", async (args, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(await account.address);
}
});
task("oracleRequest", "Makes oracle request to Mediator contract")
.addParam("contract", "Mediator contract address")
.addParam("chainid", "Chain ID from which need to request info")
.addParam("method", "Method name of requesting chain RPC")
.addParam("param", "Value of parameter for specified method")
.setAction(async (args, hre) => {
const Mediator = await hre.ethers.getContractFactory("Mediator");
const contract = Mediator.attach(args.contract);
const res = await contract.request(parseInt(args.chainid), args.method, args.param)
console.log("Request has successfully been sent.")
console.log("Transaction info:")
console.log(res)
})

View File

@ -13,6 +13,7 @@
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.4",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/chai": "^4.2.16",
"@types/mocha": "^8.2.2",
@ -1278,6 +1279,421 @@
"hardhat": "^2.0.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.4.tgz",
"integrity": "sha512-KgFNTQv9gpioiTpQ9UlTysCAFfkcBonmEn9rVPTT22A7DRENFM1VTsVeGWF3AzRhd0mrASBF+o0gvbH30pSe0Q==",
"dev": true,
"dependencies": {
"@ethersproject/abi": "^5.1.2",
"@ethersproject/address": "^5.0.2",
"cbor": "^5.0.2",
"debug": "^4.1.1",
"fs-extra": "^7.0.1",
"node-fetch": "^2.6.0",
"semver": "^6.3.0"
},
"peerDependencies": {
"hardhat": "^2.0.4"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/abi": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.0.tgz",
"integrity": "sha512-9gU2H+/yK1j2eVMdzm6xvHSnMxk8waIHQGYCZg5uvAyH0rsAzxkModzBSpbAkAuhKFEovC2S9hM4nPuLym8IZw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/address": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/constants": "^5.4.0",
"@ethersproject/hash": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/strings": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/abstract-provider": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.0.tgz",
"integrity": "sha512-vPBR7HKUBY0lpdllIn7tLIzNN7DrVnhCLKSzY0l8WAwxz686m/aL7ASDzrVxV93GJtIub6N2t4dfZ29CkPOxgA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/networks": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/transactions": "^5.4.0",
"@ethersproject/web": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/abstract-signer": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.4.0.tgz",
"integrity": "sha512-AieQAzt05HJZS2bMofpuxMEp81AHufA5D6M4ScKwtolj041nrfIbIi8ciNW7+F59VYxXq+V4c3d568Q6l2m8ew==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/abstract-provider": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/address": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.4.0.tgz",
"integrity": "sha512-SD0VgOEkcACEG/C6xavlU1Hy3m5DGSXW3CUHkaaEHbAPPsgi0coP5oNPsxau8eTlZOk/bpa/hKeCNoK5IzVI2Q==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/rlp": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/base64": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.4.0.tgz",
"integrity": "sha512-CjQw6E17QDSSC5jiM9YpF7N1aSCHmYGMt9bWD8PWv6YPMxjsys2/Q8xLrROKI3IWJ7sFfZ8B3flKDTM5wlWuZQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bytes": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/bignumber": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.4.1.tgz",
"integrity": "sha512-fJhdxqoQNuDOk6epfM7yD6J8Pol4NUCy1vkaGAkuujZm0+lNow//MKu1hLhRiYV4BsOHyBv5/lsTjF+7hWwhJg==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"bn.js": "^4.11.9"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/bytes": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.4.0.tgz",
"integrity": "sha512-H60ceqgTHbhzOj4uRc/83SCN9d+BSUnOkrr2intevqdtEMO1JFVZ1XL84OEZV+QjV36OaZYxtnt4lGmxcGsPfA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/logger": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/constants": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.4.0.tgz",
"integrity": "sha512-tzjn6S7sj9+DIIeKTJLjK9WGN2Tj0P++Z8ONEIlZjyoTkBuODN+0VfhAyYksKi43l1Sx9tX2VlFfzjfmr5Wl3Q==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bignumber": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/hash": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.4.0.tgz",
"integrity": "sha512-xymAM9tmikKgbktOCjW60Z5sdouiIIurkZUr9oW5NOex5uwxrbsYG09kb5bMcNjlVeJD3yPivTNzViIs1GCbqA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/abstract-signer": "^5.4.0",
"@ethersproject/address": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/strings": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/keccak256": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.4.0.tgz",
"integrity": "sha512-FBI1plWet+dPUvAzPAeHzRKiPpETQzqSUWR1wXJGHVWi4i8bOSrpC3NwpkPjgeXG7MnugVc1B42VbfnQikyC/A==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bytes": "^5.4.0",
"js-sha3": "0.5.7"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/logger": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.0.tgz",
"integrity": "sha512-xYdWGGQ9P2cxBayt64d8LC8aPFJk6yWCawQi/4eJ4+oJdMMjEBMrIcIMZ9AxhwpPVmnBPrsB10PcXGmGAqgUEQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
]
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/networks": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.1.tgz",
"integrity": "sha512-8SvowCKz9Uf4xC5DTKI8+il8lWqOr78kmiqAVLYT9lzB8aSmJHQMD1GSuJI0CW4hMAnzocpGpZLgiMdzsNSPig==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/logger": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/properties": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.0.tgz",
"integrity": "sha512-7jczalGVRAJ+XSRvNA6D5sAwT4gavLq3OXPuV/74o3Rd2wuzSL035IMpIMgei4CYyBdialJMrTqkOnzccLHn4A==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/logger": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/rlp": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.4.0.tgz",
"integrity": "sha512-0I7MZKfi+T5+G8atId9QaQKHRvvasM/kqLyAH4XxBCBchAooH2EX5rL9kYZWwcm3awYV+XC7VF6nLhfeQFKVPg==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/signing-key": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.4.0.tgz",
"integrity": "sha512-q8POUeywx6AKg2/jX9qBYZIAmKSB4ubGXdQ88l40hmATj29JnG5pp331nAWwwxPn2Qao4JpWHNZsQN+bPiSW9A==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"bn.js": "^4.11.9",
"elliptic": "6.5.4",
"hash.js": "1.1.7"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/strings": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.4.0.tgz",
"integrity": "sha512-k/9DkH5UGDhv7aReXLluFG5ExurwtIpUfnDNhQA29w896Dw3i4uDTz01Quaptbks1Uj9kI8wo9tmW73wcIEaWA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/constants": "^5.4.0",
"@ethersproject/logger": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/transactions": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.4.0.tgz",
"integrity": "sha512-s3EjZZt7xa4BkLknJZ98QGoIza94rVjaEed0rzZ/jB9WrIuu/1+tjvYCWzVrystXtDswy7TPBeIepyXwSYa4WQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/address": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/constants": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/rlp": "^5.4.0",
"@ethersproject/signing-key": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/@ethersproject/web": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.4.0.tgz",
"integrity": "sha512-1bUusGmcoRLYgMn6c1BLk1tOKUIFuTg8j+6N8lYlbMpDesnle+i3pGSagGNvwjaiLo4Y5gBibwctpPRmjrh4Og==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"dependencies": {
"@ethersproject/base64": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/strings": "^5.4.0"
}
},
"node_modules/@nomiclabs/hardhat-etherscan/node_modules/js-sha3": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz",
"integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=",
"dev": true
},
"node_modules/@nomiclabs/hardhat-waffle": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.1.tgz",
@ -2622,6 +3038,19 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"node_modules/cbor": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz",
"integrity": "sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==",
"dev": true,
"dependencies": {
"bignumber.js": "^9.0.1",
"nofilter": "^1.0.4"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/chai": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz",
@ -17393,6 +17822,15 @@
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/nofilter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz",
"integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@ -21876,6 +22314,240 @@
"dev": true,
"requires": {}
},
"@nomiclabs/hardhat-etherscan": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.4.tgz",
"integrity": "sha512-KgFNTQv9gpioiTpQ9UlTysCAFfkcBonmEn9rVPTT22A7DRENFM1VTsVeGWF3AzRhd0mrASBF+o0gvbH30pSe0Q==",
"dev": true,
"requires": {
"@ethersproject/abi": "^5.1.2",
"@ethersproject/address": "^5.0.2",
"cbor": "^5.0.2",
"debug": "^4.1.1",
"fs-extra": "^7.0.1",
"node-fetch": "^2.6.0",
"semver": "^6.3.0"
},
"dependencies": {
"@ethersproject/abi": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.0.tgz",
"integrity": "sha512-9gU2H+/yK1j2eVMdzm6xvHSnMxk8waIHQGYCZg5uvAyH0rsAzxkModzBSpbAkAuhKFEovC2S9hM4nPuLym8IZw==",
"dev": true,
"requires": {
"@ethersproject/address": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/constants": "^5.4.0",
"@ethersproject/hash": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/strings": "^5.4.0"
}
},
"@ethersproject/abstract-provider": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.0.tgz",
"integrity": "sha512-vPBR7HKUBY0lpdllIn7tLIzNN7DrVnhCLKSzY0l8WAwxz686m/aL7ASDzrVxV93GJtIub6N2t4dfZ29CkPOxgA==",
"dev": true,
"requires": {
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/networks": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/transactions": "^5.4.0",
"@ethersproject/web": "^5.4.0"
}
},
"@ethersproject/abstract-signer": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.4.0.tgz",
"integrity": "sha512-AieQAzt05HJZS2bMofpuxMEp81AHufA5D6M4ScKwtolj041nrfIbIi8ciNW7+F59VYxXq+V4c3d568Q6l2m8ew==",
"dev": true,
"requires": {
"@ethersproject/abstract-provider": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0"
}
},
"@ethersproject/address": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.4.0.tgz",
"integrity": "sha512-SD0VgOEkcACEG/C6xavlU1Hy3m5DGSXW3CUHkaaEHbAPPsgi0coP5oNPsxau8eTlZOk/bpa/hKeCNoK5IzVI2Q==",
"dev": true,
"requires": {
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/rlp": "^5.4.0"
}
},
"@ethersproject/base64": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.4.0.tgz",
"integrity": "sha512-CjQw6E17QDSSC5jiM9YpF7N1aSCHmYGMt9bWD8PWv6YPMxjsys2/Q8xLrROKI3IWJ7sFfZ8B3flKDTM5wlWuZQ==",
"dev": true,
"requires": {
"@ethersproject/bytes": "^5.4.0"
}
},
"@ethersproject/bignumber": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.4.1.tgz",
"integrity": "sha512-fJhdxqoQNuDOk6epfM7yD6J8Pol4NUCy1vkaGAkuujZm0+lNow//MKu1hLhRiYV4BsOHyBv5/lsTjF+7hWwhJg==",
"dev": true,
"requires": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"bn.js": "^4.11.9"
}
},
"@ethersproject/bytes": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.4.0.tgz",
"integrity": "sha512-H60ceqgTHbhzOj4uRc/83SCN9d+BSUnOkrr2intevqdtEMO1JFVZ1XL84OEZV+QjV36OaZYxtnt4lGmxcGsPfA==",
"dev": true,
"requires": {
"@ethersproject/logger": "^5.4.0"
}
},
"@ethersproject/constants": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.4.0.tgz",
"integrity": "sha512-tzjn6S7sj9+DIIeKTJLjK9WGN2Tj0P++Z8ONEIlZjyoTkBuODN+0VfhAyYksKi43l1Sx9tX2VlFfzjfmr5Wl3Q==",
"dev": true,
"requires": {
"@ethersproject/bignumber": "^5.4.0"
}
},
"@ethersproject/hash": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.4.0.tgz",
"integrity": "sha512-xymAM9tmikKgbktOCjW60Z5sdouiIIurkZUr9oW5NOex5uwxrbsYG09kb5bMcNjlVeJD3yPivTNzViIs1GCbqA==",
"dev": true,
"requires": {
"@ethersproject/abstract-signer": "^5.4.0",
"@ethersproject/address": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/strings": "^5.4.0"
}
},
"@ethersproject/keccak256": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.4.0.tgz",
"integrity": "sha512-FBI1plWet+dPUvAzPAeHzRKiPpETQzqSUWR1wXJGHVWi4i8bOSrpC3NwpkPjgeXG7MnugVc1B42VbfnQikyC/A==",
"dev": true,
"requires": {
"@ethersproject/bytes": "^5.4.0",
"js-sha3": "0.5.7"
}
},
"@ethersproject/logger": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.0.tgz",
"integrity": "sha512-xYdWGGQ9P2cxBayt64d8LC8aPFJk6yWCawQi/4eJ4+oJdMMjEBMrIcIMZ9AxhwpPVmnBPrsB10PcXGmGAqgUEQ==",
"dev": true
},
"@ethersproject/networks": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.1.tgz",
"integrity": "sha512-8SvowCKz9Uf4xC5DTKI8+il8lWqOr78kmiqAVLYT9lzB8aSmJHQMD1GSuJI0CW4hMAnzocpGpZLgiMdzsNSPig==",
"dev": true,
"requires": {
"@ethersproject/logger": "^5.4.0"
}
},
"@ethersproject/properties": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.0.tgz",
"integrity": "sha512-7jczalGVRAJ+XSRvNA6D5sAwT4gavLq3OXPuV/74o3Rd2wuzSL035IMpIMgei4CYyBdialJMrTqkOnzccLHn4A==",
"dev": true,
"requires": {
"@ethersproject/logger": "^5.4.0"
}
},
"@ethersproject/rlp": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.4.0.tgz",
"integrity": "sha512-0I7MZKfi+T5+G8atId9QaQKHRvvasM/kqLyAH4XxBCBchAooH2EX5rL9kYZWwcm3awYV+XC7VF6nLhfeQFKVPg==",
"dev": true,
"requires": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0"
}
},
"@ethersproject/signing-key": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.4.0.tgz",
"integrity": "sha512-q8POUeywx6AKg2/jX9qBYZIAmKSB4ubGXdQ88l40hmATj29JnG5pp331nAWwwxPn2Qao4JpWHNZsQN+bPiSW9A==",
"dev": true,
"requires": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"bn.js": "^4.11.9",
"elliptic": "6.5.4",
"hash.js": "1.1.7"
}
},
"@ethersproject/strings": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.4.0.tgz",
"integrity": "sha512-k/9DkH5UGDhv7aReXLluFG5ExurwtIpUfnDNhQA29w896Dw3i4uDTz01Quaptbks1Uj9kI8wo9tmW73wcIEaWA==",
"dev": true,
"requires": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/constants": "^5.4.0",
"@ethersproject/logger": "^5.4.0"
}
},
"@ethersproject/transactions": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.4.0.tgz",
"integrity": "sha512-s3EjZZt7xa4BkLknJZ98QGoIza94rVjaEed0rzZ/jB9WrIuu/1+tjvYCWzVrystXtDswy7TPBeIepyXwSYa4WQ==",
"dev": true,
"requires": {
"@ethersproject/address": "^5.4.0",
"@ethersproject/bignumber": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/constants": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/rlp": "^5.4.0",
"@ethersproject/signing-key": "^5.4.0"
}
},
"@ethersproject/web": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.4.0.tgz",
"integrity": "sha512-1bUusGmcoRLYgMn6c1BLk1tOKUIFuTg8j+6N8lYlbMpDesnle+i3pGSagGNvwjaiLo4Y5gBibwctpPRmjrh4Og==",
"dev": true,
"requires": {
"@ethersproject/base64": "^5.4.0",
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/logger": "^5.4.0",
"@ethersproject/properties": "^5.4.0",
"@ethersproject/strings": "^5.4.0"
}
},
"js-sha3": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz",
"integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=",
"dev": true
}
}
},
"@nomiclabs/hardhat-waffle": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.1.tgz",
@ -22223,7 +22895,9 @@
"resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-2.0.0.tgz",
"integrity": "sha512-0xdCkyGOzdqh4h5JSf+zoWx85IusEjDcPIwNEHP8mrWSnCae4rvrqB+/gtpdNfX7zjlFlZiMeePn2r63EI3Lrw==",
"dev": true,
"requires": {}
"requires": {
"ethers": "^5.0.2"
}
},
"@types/bn.js": {
"version": "4.11.6",
@ -23064,6 +23738,16 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"cbor": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz",
"integrity": "sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==",
"dev": true,
"requires": {
"bignumber.js": "^9.0.1",
"nofilter": "^1.0.4"
}
},
"chai": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz",
@ -33148,7 +33832,9 @@
"resolved": "https://registry.npmjs.org/hardhat-tracer/-/hardhat-tracer-1.0.0-alpha.5.tgz",
"integrity": "sha512-25TZhzIrQgFyYs2oIZrdK6rU0XbK82nr759KYStyCzWklE6bxGlDO5niQugScAIo9aGAT/4vec/mxcJo1Ladpw==",
"dev": true,
"requires": {}
"requires": {
"ethers": "^5.0.24"
}
},
"has": {
"version": "1.0.3",
@ -34565,6 +35251,12 @@
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
"dev": true
},
"nofilter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz",
"integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==",
"dev": true
},
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",

View File

@ -7,12 +7,13 @@
"test": "TS_NODE_FILES=true npx hardhat test",
"test:reportGas": "REPORT_GAS=1 TS_NODE_FILES=true npx hardhat test",
"coverage": "TS_NODE_FILES=true npx hardhat coverage",
"deploy:local": "TS_NODE_FILES=true npx hardhat --network geth run scripts/deploy.ts"
"deploy:local": "TS_NODE_FILES=true npx hardhat --network local run scripts/deploy.ts"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.4",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/chai": "^4.2.16",
"@types/mocha": "^8.2.2",

View File

@ -5,7 +5,7 @@ import deploy from "../common/deployment";
describe("DioneOracle", function () {
let dioneOracle: Contract;
let dioneToken: Contract;
let dioneStaking: Contract;
beforeEach(async function () {
const contracts = await deploy({
@ -20,11 +20,11 @@ describe("DioneOracle", function () {
minStakeForDisputeVotes: 100
});
dioneOracle = contracts.dioneOracle;
dioneToken = contracts.dioneToken;
dioneStaking = contracts.dioneStaking;
});
it("should create request and cancel it", async function () {
const timestamp = 1625097600;
const timestamp = new Date().getTime();
await ethers.provider.send("evm_setNextBlockTimestamp", [timestamp]);
const requestDeadline = timestamp + 300;
await expect(dioneOracle.requestOracles(1, "getTransaction", "bafy2bzaceaaab3kkoaocal2dzh3okzy4gscqpdt42hzrov3df6vjumalngc3g", "0x0000000000000000000000000000000000000000", "0x00000000"))
@ -50,8 +50,8 @@ describe("DioneOracle", function () {
.withArgs(1, BigNumber.from(0x8da5cb5b));
// check if miner has received the reward
expect(await dioneToken.balanceOf(addr0.address))
.to.be.equal(ethers.constants.WeiPerEther.mul(100));
expect(await dioneStaking.minerStake(addr0.address))
.to.be.equal(ethers.constants.WeiPerEther.mul(9100));
});
it("should fail submission after request deadline", async function () {

View File

@ -4,7 +4,8 @@
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "dist"
"outDir": "dist",
"resolveJsonModule": true
},
"include": ["./scripts", "./test", "./common"],
"files": ["./hardhat.config.ts"]

View File

@ -1,7 +1,8 @@
package ethclient
import (
"github.com/Secured-Finance/dione/types"
"math/big"
"github.com/ethereum/go-ethereum/common"
)
@ -9,32 +10,22 @@ 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
func (c *EthereumClient) GetTotalStake() (*types.BigInt, error) {
var b types.BigInt
// GetTotalStake for getting total stake in DioneStaking contract
func (c *EthereumClient) GetTotalStake() (*big.Int, error) {
totalStake, err := c.dioneStaking.TotalStake()
if err != nil {
return nil, err
}
b.Int = totalStake
return &b, nil
return totalStake, nil
}
// Getting miner stake in DioneStaking contract, this function could
// be used for storing the miner's stake and veryfing the stake tokens
// on new tasks
func (c *EthereumClient) GetMinerStake(minerAddress common.Address) (*types.BigInt, error) {
var b types.BigInt
// GetMinerStake for getting specified miner stake in DioneStaking contract
func (c *EthereumClient) GetMinerStake(minerAddress common.Address) (*big.Int, error) {
minerStake, err := c.dioneStaking.MinerStake(minerAddress)
if err != nil {
return nil, err
}
b.Int = minerStake
return &b, nil
return minerStake, nil
}

42
go.mod
View File

@ -1,13 +1,13 @@
module github.com/Secured-Finance/dione
go 1.14
go 1.16
require (
github.com/Secured-Finance/go-libp2p-pex v1.1.0
github.com/Secured-Finance/go-libp2p-pex v1.1.2
github.com/Secured-Finance/golang-set v1.8.0
github.com/VictoriaMetrics/fastcache v1.5.7
github.com/aristanetworks/goarista v0.0.0-20210308203447-b196d8410f1d // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/cespare/cp v1.1.1 // indirect
github.com/deckarep/golang-set v1.7.1 // indirect
github.com/dgrr/fastws v1.0.0
@ -16,13 +16,11 @@ require (
github.com/ethereum/go-ethereum v1.9.25
github.com/filecoin-project/go-address v0.0.5
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
github.com/filecoin-project/go-state-types v0.1.0
github.com/filecoin-project/lotus v1.6.0
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
github.com/fxamacker/cbor/v2 v2.2.0
github.com/fxamacker/cbor/v2 v2.3.0
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/go-kit/kit v0.10.0
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
github.com/go-redis/redis/v8 v8.7.0
github.com/gobwas/ws v1.0.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@ -30,20 +28,21 @@ require (
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/huin/goupnp v1.0.1-0.20200620063722-49508fba0031 // indirect
github.com/ipfs/go-log v1.0.4
github.com/jmoiron/sqlx v1.2.0
github.com/ipfs/go-log/v2 v2.1.3 // indirect
github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/libp2p/go-libp2p v0.12.0
github.com/libp2p/go-libp2p-core v0.7.0
github.com/klauspost/cpuid/v2 v2.0.8 // indirect
github.com/ledgerwatch/lmdb-go v1.17.8
github.com/libp2p/go-libp2p v0.13.0
github.com/libp2p/go-libp2p-core v0.8.5
github.com/libp2p/go-libp2p-gorpc v0.1.3
github.com/libp2p/go-libp2p-pubsub v0.4.2-0.20210212194758-6c1addf493eb
github.com/mattn/go-sqlite3 v1.11.0
github.com/miguelmota/go-ethereum-hdwallet v0.0.0-20210314074952-8dd49aa599b9
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/multiformats/go-multiaddr v0.3.3
github.com/multiformats/go-multihash v0.0.15 // indirect
github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/raulk/clock v1.1.0
github.com/rjeczalik/notify v0.9.2 // indirect
github.com/rs/cors v1.7.0 // indirect
@ -52,17 +51,18 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect
github.com/stretchr/testify v1.7.0
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/ugorji/go v1.2.6 // indirect
github.com/valyala/fasthttp v1.17.0
github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a
github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2
github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542
go.uber.org/zap v1.16.0
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
go.uber.org/fx v1.13.1
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.17.0
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 // indirect
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
nhooyr.io/websocket v1.8.6 // indirect
)

126
go.sum
View File

@ -44,6 +44,7 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CurtisLusmore/ghp v0.0.0-20190131093722-04a23b486a62/go.mod h1:+iVlyn4r8pVe5rgooNlrYjuzDayoXL5H/JBMhemDs/I=
github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
@ -56,8 +57,8 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Secured-Finance/go-libp2p-pex v1.1.0 h1:u+VQmzKmECZJTCkmUwQw0NGU1xHQIZjY7gYElLOxl0o=
github.com/Secured-Finance/go-libp2p-pex v1.1.0/go.mod h1:pMq1xsaluhIIdY8dALk/U7NH5N+naUCo/1jRgh8YNsI=
github.com/Secured-Finance/go-libp2p-pex v1.1.2 h1:0T/Qd77exlim+5XBpWNj30anqIFzbdAMPHajyBWvcnM=
github.com/Secured-Finance/go-libp2p-pex v1.1.2/go.mod h1:pMq1xsaluhIIdY8dALk/U7NH5N+naUCo/1jRgh8YNsI=
github.com/Secured-Finance/golang-set v1.8.0 h1:2z3Aymw/LtvPsfRIJbX2p6xiSIQFxByzmGd2xYU0H9E=
github.com/Secured-Finance/golang-set v1.8.0/go.mod h1:NLdE4DjG2Aw84FTUFyspBP33PcmrRMxfNrrd+Eo9vJI=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@ -106,8 +107,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@ -132,8 +133,9 @@ github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcug
github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo=
github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
@ -178,7 +180,6 @@ github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u9
github.com/cockroachdb/pebble v0.0.0-20200916222308-4e219a90ba5b/go.mod h1:hU7vhtrqonEphNF+xt8/lHdaBprxmV1h8BOGrd9XwmQ=
github.com/cockroachdb/pebble v0.0.0-20201001221639-879f3bfeef07/go.mod h1:hU7vhtrqonEphNF+xt8/lHdaBprxmV1h8BOGrd9XwmQ=
github.com/cockroachdb/redact v0.0.0-20200622112456-cd282804bbd3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@ -381,8 +382,9 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
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/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ=
github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik=
github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays=
@ -392,9 +394,7 @@ github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1/go.mod h1:0eHX/BVySxPc6SE2mZRoppGq7qcE
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
@ -413,21 +413,14 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-redis/redis/v8 v8.7.0 h1:LJ8sFG5eNH1u3SxlptEZ3mEgm/5J9Qx6QhiTG3HhpCo=
github.com/go-redis/redis/v8 v8.7.0/go.mod h1:BRxHBWn3pO3CfjyX6vAoyeRmCquvxr6QG+2onGV2gYs=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@ -757,8 +750,9 @@ github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBW
github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 h1:3bijxqzQ1O9yg7gd7Aqk80oaEvsJ+uXw0zSvi2qR3Jw=
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk=
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA=
github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto=
github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk=
@ -836,8 +830,6 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.1.1-0.20190114141812-62fb9bc030d1 h1:qBCV/RLV02TSfQa7tFmxTihnG+u+7JXByOkhlkR5rmQ=
@ -852,7 +844,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
@ -862,7 +853,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3 h1:Iy7Ifq2ysilWU4QlCx/97OoI4xT1IV7i8byT/EyIT/M=
github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4ufZiIGv2nXn4gMxnfKV306n3mWXgNu/d2TqdTU=
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
@ -888,8 +878,8 @@ github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.2/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.8 h1:bhR2mgIlno/Sfk4oUbH4sPlc83z1yGrN9bvqiq3C33I=
github.com/klauspost/cpuid/v2 v2.0.8/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
github.com/klauspost/reedsolomon v1.9.11/go.mod h1:nLvuzNvy1ZDNQW30IuMc2ZWCbiqrJgdLoUS2X8HAUVg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -908,10 +898,9 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/ledgerwatch/lmdb-go v1.17.8 h1:IQUVJ2MYIfSsrhz+o+QbugUqRQuqTXodt8pE6cuvQVg=
github.com/ledgerwatch/lmdb-go v1.17.8/go.mod h1:NKRpCxksoTQPyxsUcBiVOe0135uqnJsnf6cElxmOL0o=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU=
@ -949,8 +938,9 @@ github.com/libp2p/go-libp2p v0.8.3/go.mod h1:EsH1A+8yoWK+L4iKcbPYu6MPluZ+CHWI9El
github.com/libp2p/go-libp2p v0.9.2/go.mod h1:cunHNLDVus66Ct9iXXcjKRLdmHdFdHVe1TAnbubJQqQ=
github.com/libp2p/go-libp2p v0.10.0/go.mod h1:yBJNpb+mGJdgrwbKAKrhPU0u3ogyNFTfjJ6bdM+Q/G8=
github.com/libp2p/go-libp2p v0.11.0/go.mod h1:3/ogJDXsbbepEfqtZKBR/DedzxJXCeK17t2Z9RE9bEE=
github.com/libp2p/go-libp2p v0.12.0 h1:+xai9RQnQ9l5elFOKvp5wRyjyWisSwEx+6nU2+onpUA=
github.com/libp2p/go-libp2p v0.12.0/go.mod h1:FpHZrfC1q7nA8jitvdjKBDF31hguaC676g/nT9PgQM0=
github.com/libp2p/go-libp2p v0.13.0 h1:tDdrXARSghmusdm0nf1U/4M8aj8Rr0V2IzQOXmbzQ3s=
github.com/libp2p/go-libp2p v0.13.0/go.mod h1:pM0beYdACRfHO1WcJlp65WXyG2A6NqYM+t2DTVAJxMo=
github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo=
github.com/libp2p/go-libp2p-autonat v0.0.2/go.mod h1:fs71q5Xk+pdnKU014o2iq1RhMs9/PMaG5zXRFNnIIT4=
github.com/libp2p/go-libp2p-autonat v0.0.6/go.mod h1:uZneLdOkZHro35xIhpbtTzLlgYturpu4J5+0cZK3MqE=
@ -1010,8 +1000,8 @@ github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX
github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
github.com/libp2p/go-libp2p-core v0.7.0 h1:4a0TMjrWNTZlNvcqxZmrMRDi/NQWrhwO2pkTuLSQ/IQ=
github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw=
github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE=
@ -1027,6 +1017,8 @@ github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQO
github.com/libp2p/go-libp2p-discovery v0.4.0/go.mod h1:bZ0aJSrFc/eX2llP0ryhb1kpgkPyTo23SJ5b7UQCMh4=
github.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+WgN9mN0iWviQ=
github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug=
github.com/libp2p/go-libp2p-gorpc v0.1.3 h1:b0bRXD4PEfqIvXbivDhNnaSQ8ERoaYd0vM7mDIDLQCQ=
github.com/libp2p/go-libp2p-gorpc v0.1.3/go.mod h1:ulZShaJCp3JHlBMHiA20efUmiqDECza+JvGFNXJyKdI=
github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go=
github.com/libp2p/go-libp2p-host v0.0.3/go.mod h1:Y/qPyA6C8j2coYyos1dfRm0I8+nvd4TGrDGt4tA7JR8=
github.com/libp2p/go-libp2p-interface-connmgr v0.0.1/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k=
@ -1047,8 +1039,10 @@ github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiY
github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo=
github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek=
github.com/libp2p/go-libp2p-mplex v0.2.4/go.mod h1:mI7iOezdWFOisvUwaYd3IDrJ4oVmgoXK8H331ui39CE=
github.com/libp2p/go-libp2p-mplex v0.3.0 h1:CZyqqKP0BSGQyPLvpRQougbfXaaaJZdGgzhCpJNuNSk=
github.com/libp2p/go-libp2p-mplex v0.3.0/go.mod h1:l9QWxRbbb5/hQMECEb908GbS9Sm2UAR2KFZKUJEynEs=
github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw=
github.com/libp2p/go-libp2p-mplex v0.4.1 h1:/pyhkP1nLwjG3OM+VuaNJkQT/Pqq73WzB3aDN3Fx1sc=
github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g=
github.com/libp2p/go-libp2p-nat v0.0.2/go.mod h1:QrjXQSD5Dj4IJOdEcjHRkWTSomyxRo6HnUkf/TfQpLQ=
github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY=
github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE=
@ -1114,8 +1108,9 @@ github.com/libp2p/go-libp2p-swarm v0.2.4/go.mod h1:/xIpHFPPh3wmSthtxdGbkHZ0OET1h
github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYRNYp1xs4S2FBWKA=
github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM=
github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=
github.com/libp2p/go-libp2p-swarm v0.3.1 h1:UTobu+oQHGdXTOGpZ4RefuVqYoJXcT0EBtSR74m2LkI=
github.com/libp2p/go-libp2p-swarm v0.3.1/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=
github.com/libp2p/go-libp2p-swarm v0.4.0 h1:hahq/ijRoeH6dgROOM8x7SeaKK5VgjjIr96vdrT+NUA=
github.com/libp2p/go-libp2p-swarm v0.4.0/go.mod h1:XVFcO52VoLoo0eitSxNQWYq4D6sydGOweTOAjJNraCw=
github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
@ -1123,8 +1118,9 @@ github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MB
github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc=
github.com/libp2p/go-libp2p-testing v0.3.0 h1:ZiBYstPamsi7y6NJZebRudUzsYmVkt998hltyLqf8+g=
github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g=
github.com/libp2p/go-libp2p-testing v0.4.0 h1:PrwHRi0IGqOwVQWR3xzgigSlhlLfxgfXgkHxr77EghQ=
github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0=
github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM=
github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M=
github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk=
@ -1134,8 +1130,9 @@ github.com/libp2p/go-libp2p-transport-upgrader v0.0.1/go.mod h1:NJpUAgQab/8K6K0m
github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2kn/m1w6YXxcIAYJYeI90h6BGgUc=
github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA=
github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns=
github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 h1:q3ULhsknEQ34eVDhv4YwKS8iet69ffs9+Fir6a7weN4=
github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o=
github.com/libp2p/go-libp2p-transport-upgrader v0.4.0 h1:xwj4h3hJdBrxqMOyMUjwscjoVst0AASTsKtZiTChoHI=
github.com/libp2p/go-libp2p-transport-upgrader v0.4.0/go.mod h1:J4ko0ObtZSmgn5BX5AmegP+dK3CSnU2lMCKsSq/EY0s=
github.com/libp2p/go-libp2p-yamux v0.1.2/go.mod h1:xUoV/RmYkg6BW/qGxA9XJyg+HzXFYkeXbnhjmnYzKp8=
github.com/libp2p/go-libp2p-yamux v0.1.3/go.mod h1:VGSQVrqkh6y4nm0189qqxMtvyBft44MOYYPpYKXiVt4=
github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8=
@ -1145,8 +1142,10 @@ github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ
github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU=
github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4=
github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30=
github.com/libp2p/go-libp2p-yamux v0.4.1 h1:TJxRVPY9SjH7TNrNC80l1OJMBiWhs1qpKmeB+1Ug3xU=
github.com/libp2p/go-libp2p-yamux v0.4.1/go.mod h1:FA/NjRYRVNjqOzpGuGqcruH7jAU2mYIjtKBicVOL3dc=
github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po=
github.com/libp2p/go-libp2p-yamux v0.5.1 h1:sX4WQPHMhRxJE5UZTfjEuBvlQWXB5Bo3A2JK9ZJ9EM0=
github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4=
github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=
@ -1157,8 +1156,9 @@ github.com/libp2p/go-mplex v0.0.4/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTW
github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU=
github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
github.com/libp2p/go-mplex v0.2.0 h1:Ov/D+8oBlbRkjBs1R1Iua8hJ8cUfbdiW8EOdZuxcgaI=
github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ=
github.com/libp2p/go-mplex v0.3.0 h1:U1T+vmCYJaEoDJPV1aq31N56hS+lJgb397GsylNSgrU=
github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ=
github.com/libp2p/go-msgio v0.0.1/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
@ -1210,8 +1210,9 @@ github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw
github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y=
github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM=
github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
github.com/libp2p/go-ws-transport v0.3.1 h1:ZX5rWB8nhRRJVaPO6tmkGI/Xx8XNboYX20PW5hXIscw=
github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
github.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k=
github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA=
github.com/libp2p/go-yamux v1.2.1/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
@ -1223,6 +1224,8 @@ github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/h
github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI=
github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
github.com/libp2p/go-yamux/v2 v2.0.0 h1:vSGhAy5u6iHBq11ZDcyHH4Blcf9xlBhT4WQDoOE90LU=
github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
@ -1264,9 +1267,6 @@ github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@ -1308,10 +1308,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
@ -1332,8 +1330,9 @@ github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y9
github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE=
github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y=
github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI=
github.com/multiformats/go-multiaddr v0.3.1 h1:1bxa+W7j9wZKTZREySx1vPMs2TqrYWjVZ7zE6/XLG1I=
github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc=
github.com/multiformats/go-multiaddr v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs=
github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0=
github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
@ -1453,6 +1452,8 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@ -1645,7 +1646,6 @@ github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@ -1682,11 +1682,15 @@ github.com/uber/jaeger-client-go v2.23.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMW
github.com/uber/jaeger-lib v1.5.1-0.20181102163054-1fc5c315e03c/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
@ -1708,6 +1712,8 @@ github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvS
github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w=
github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a h1:MwXxGlHLoTCM3/5nlvGJqSWhcmWtGuc6RBtUsTKJwvU=
github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a/go.mod h1:Q/vZYhXjtE/oLDRqlGLWwRrNKZJAwidHlVfwIkKEH2w=
github.com/weaveworks/common v0.0.0-20200512154658-384f10054ec5 h1:EYxr08r8x6r/5fLEAMMkida1BVgxVXE4LfZv/XV+znU=
github.com/weaveworks/common v0.0.0-20200512154658-384f10054ec5/go.mod h1:c98fKi5B9u8OsKGiWHLRKus6ToQ1Tubeow44ECO1uxY=
github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M=
@ -1778,7 +1784,6 @@ github.com/zondax/ledger-go v0.12.1/go.mod h1:KatxXrVDzgWwbssUWsF5+cOJHXPvzQ09YS
go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs=
go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw=
go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ=
go.dedis.ch/kyber/v3 v3.0.9 h1:i0ZbOQocHUjfFasBiUql5zVeC7u/vahFd96DFA8UOWk=
go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg=
go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
@ -1815,25 +1820,30 @@ go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/dig v1.10.0 h1:yLmDDj9/zuDjv3gz8GQGviXMs9TfysIUMUilCpgzUJY=
go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw=
go.uber.org/fx v1.9.0/go.mod h1:mFdUyAUuJ3w4jAckiKSKbldsxy1ojpAMJ+dVZg5Y0Aw=
go.uber.org/fx v1.13.1 h1:CFNTr1oin5OJ0VCZ8EycL3wzF29Jz2g0xe55RFsf2a4=
go.uber.org/fx v1.13.1/go.mod h1:bREWhavnedxpJeTq9pQT53BbvwhUv7TcpsOqcH4a+3w=
go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=
go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -1842,6 +1852,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -1876,8 +1887,8 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1969,8 +1980,8 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -2005,6 +2016,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -2073,8 +2085,9 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -2120,10 +2133,10 @@ golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@ -2273,7 +2286,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -2282,8 +2294,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
modernc.org/cc v1.0.0 h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=

6
node/flags.go Normal file
View File

@ -0,0 +1,6 @@
package node
type AppFlags struct {
ConfigPath string
Verbose bool
}

95
node/network_service.go Normal file
View File

@ -0,0 +1,95 @@
package node
import (
"context"
"errors"
"fmt"
"github.com/Secured-Finance/dione/blockchain"
gorpc "github.com/libp2p/go-libp2p-gorpc"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/consensus/policy"
"github.com/Secured-Finance/dione/node/wire"
"github.com/Secured-Finance/dione/blockchain/pool"
)
type NetworkService struct {
blockchain *blockchain.BlockChain
mempool *pool.Mempool
rpcClient *gorpc.Client
}
func NewNetworkService(bc *blockchain.BlockChain, mp *pool.Mempool) *NetworkService {
return &NetworkService{
blockchain: bc,
mempool: mp,
}
}
func (s *NetworkService) LastBlockHeight(ctx context.Context, arg struct{}, reply *wire.LastBlockHeightReply) error {
height, err := s.blockchain.GetLatestBlockHeight()
if err != nil {
return err
}
reply.Height = height
return nil
}
func (s *NetworkService) GetRangeOfBlocks(ctx context.Context, arg wire.GetRangeOfBlocksArg, reply *wire.GetRangeOfBlocksReply) error {
if arg.From > arg.To {
return fmt.Errorf("incorrect arguments: from > to")
}
if arg.To-arg.From > policy.MaxBlockCountForRetrieving {
return fmt.Errorf("incorrect arguments: count of block for retrieving is exceeded the limit")
}
for i := arg.From; i <= arg.To; i++ {
block, err := s.blockchain.FetchBlockByHeight(i)
if err != nil {
logrus.Warnf("failed to retrieve block from blockpool with height %d", i)
reply.FailedBlockHeights = append(reply.FailedBlockHeights, i)
continue
}
reply.Blocks = append(reply.Blocks, *block)
}
return nil
}
func (s *NetworkService) Mempool(ctx context.Context, arg struct{}, reply *wire.InvMessage) error {
txs := s.mempool.GetAllTransactions()
// extract hashes of txs
for _, v := range txs {
reply.Inventory = append(reply.Inventory, wire.InvItem{
Type: wire.TxInvType,
Hash: v.Hash,
})
}
return nil
}
func (s *NetworkService) GetMempoolTxs(ctx context.Context, arg wire.GetMempoolTxsArg, reply *wire.GetMempoolTxsReply) error {
if len(arg.Items) > policy.MaxTransactionCountForRetrieving {
pid, _ := gorpc.GetRequestSender(ctx)
logrus.Warnf("Max tx count limit exceeded for GetMempoolTxs request of node %s", pid)
return fmt.Errorf("max tx count limit exceeded")
}
for _, v := range arg.Items {
tx, err := s.mempool.GetTransaction(v)
if err != nil {
if errors.Is(err, pool.ErrTxNotFound) {
reply.NotFoundTxs = append(reply.NotFoundTxs, v)
} else {
return err
}
}
reply.Transactions = append(reply.Transactions, *tx)
}
return nil
}

View File

@ -2,49 +2,37 @@ package node
import (
"context"
"crypto/rand"
"flag"
"fmt"
"io/ioutil"
"os"
"time"
pex "github.com/Secured-Finance/go-libp2p-pex"
"github.com/Secured-Finance/dione/blockchain"
drand2 "github.com/Secured-Finance/dione/beacon/drand"
"github.com/Secured-Finance/dione/pubsub"
"github.com/Secured-Finance/dione/cache"
"github.com/Secured-Finance/dione/consensus"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/Secured-Finance/dione/blockchain/sync"
"github.com/Secured-Finance/dione/drand"
"go.uber.org/fx"
"github.com/ethereum/go-ethereum/common"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/fxamacker/cbor/v2"
"github.com/Secured-Finance/dione/types"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/blockchain/pool"
"github.com/libp2p/go-libp2p-core/discovery"
"github.com/Secured-Finance/dione/rpc"
rtypes "github.com/Secured-Finance/dione/rpc/types"
solana2 "github.com/Secured-Finance/dione/rpc/solana"
"github.com/Secured-Finance/dione/rpc/filecoin"
"github.com/Secured-Finance/dione/types"
"github.com/Secured-Finance/dione/wallet"
"golang.org/x/xerrors"
"github.com/Secured-Finance/dione/beacon"
"github.com/Secured-Finance/dione/config"
"github.com/Secured-Finance/dione/ethclient"
pubsub2 "github.com/Secured-Finance/dione/pubsub"
"github.com/libp2p/go-libp2p"
crypto "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/host"
"github.com/multiformats/go-multiaddr"
"github.com/sirupsen/logrus"
)
@ -52,129 +40,58 @@ const (
DefaultPEXUpdateTime = 6 * time.Second
)
type Node struct {
Host host.Host
PeerDiscovery discovery.Discovery
PubSubRouter *pubsub2.PubSubRouter
GlobalCtx context.Context
GlobalCtxCancel context.CancelFunc
Config *config.Config
Ethereum *ethclient.EthereumClient
ConsensusManager *consensus.PBFTConsensusManager
Miner *consensus.Miner
Beacon beacon.BeaconNetworks
Wallet *wallet.LocalWallet
EventCache cache.EventCache
DisputeManager *consensus.DisputeManager
}
func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTime time.Duration) (*Node, error) {
n := &Node{
Config: config,
}
// initialize libp2p host
lhost, err := provideLibp2pHost(n.Config, prvKey, pexDiscoveryUpdateTime)
func runNode(
lc fx.Lifecycle,
cfg *config.Config,
disco discovery.Discovery,
ethClient *ethclient.EthereumClient,
h host.Host,
mp *pool.Mempool,
syncManager sync.SyncManager,
consensusManager *consensus.PBFTConsensusManager,
pubSubRouter *pubsub.PubSubRouter,
disputeManager *consensus.DisputeManager,
db *drand2.DrandBeacon,
) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
err := runLibp2pAsync(context.TODO(), h, cfg, disco)
if err != nil {
logrus.Fatal(err)
return err
}
n.Host = lhost
logrus.Info("Started up Libp2p host!")
// initialize ethereum client
ethClient, err := provideEthereumClient(n.Config)
err = db.Run(context.TODO())
if err != nil {
logrus.Fatal(err)
return err
}
n.Ethereum = ethClient
logrus.Info("Started up Ethereum client!")
// initialize blockchain rpc clients
err = n.setupRPCClients()
// Run pubsub router
pubSubRouter.Run()
// Subscribe on new requests event channel from Ethereum
err = subscribeOnEthContractsAsync(context.TODO(), ethClient, mp)
if err != nil {
logrus.Fatal(err)
}
logrus.Info("RPC clients has successfully configured!")
// initialize pubsub subsystem
psb := providePubsubRouter(lhost, n.Config)
n.PubSubRouter = psb
logrus.Info("PubSub subsystem has initialized!")
// initialize peer discovery
peerDiscovery, err := providePeerDiscovery(n.Config, lhost, pexDiscoveryUpdateTime)
if err != nil {
logrus.Fatal(err)
}
n.PeerDiscovery = peerDiscovery
logrus.Info("Peer discovery subsystem has initialized!")
// get private key of libp2p host
rawPrivKey, err := prvKey.Raw()
if err != nil {
logrus.Fatal(err)
return 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 initialized!")
// Run blockchain sync manager
syncManager.Run()
// initialize mining subsystem
miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Beacon, n.Ethereum, rawPrivKey)
n.Miner = miner
logrus.Info("Mining subsystem has initialized!")
// Run dispute manager
disputeManager.Run(context.TODO())
// initialize event log cache subsystem
eventCache := provideEventCache(config)
n.EventCache = eventCache
logrus.Info("Event cache subsystem has initialized!")
// initialize consensus subsystem
cManager := provideConsensusManager(psb, miner, ethClient, rawPrivKey, n.Config.ConsensusMinApprovals, eventCache)
n.ConsensusManager = cManager
logrus.Info("Consensus subsystem has initialized!")
// initialize dispute subsystem
disputeManager, err := provideDisputeManager(context.TODO(), ethClient, cManager, config)
if err != nil {
logrus.Fatal(err)
}
n.DisputeManager = disputeManager
logrus.Info("Dispute subsystem has initialized!")
// initialize internal eth wallet
wallet, err := provideWallet(n.Host.ID(), rawPrivKey)
if err != nil {
logrus.Fatal(err)
}
n.Wallet = wallet
return n, nil
}
func (n *Node) Run(ctx context.Context) error {
n.runLibp2pAsync(ctx)
n.subscribeOnEthContractsAsync(ctx)
for {
select {
case <-ctx.Done():
return nil
}
},
OnStop: func(ctx context.Context) error {
// TODO
return nil
},
})
}
// return nil
}
func (n *Node) runLibp2pAsync(ctx context.Context) error {
logrus.Info(fmt.Sprintf("[*] Your Multiaddress Is: /ip4/%s/tcp/%d/p2p/%s", n.Config.ListenAddr, n.Config.ListenPort, n.Host.ID().Pretty()))
func runLibp2pAsync(ctx context.Context, h host.Host, cfg *config.Config, disco discovery.Discovery) error {
logrus.Info("Announcing ourselves...")
_, err := n.PeerDiscovery.Advertise(context.TODO(), n.Config.Rendezvous)
_, err := disco.Advertise(context.TODO(), cfg.Rendezvous)
if err != nil {
return xerrors.Errorf("failed to announce this node to the network: %v", err)
}
@ -182,7 +99,7 @@ func (n *Node) runLibp2pAsync(ctx context.Context) error {
// Discover unbounded count of peers
logrus.Info("Searching for other peers...")
peerChan, err := n.PeerDiscovery.FindPeers(context.TODO(), n.Config.Rendezvous)
peerChan, err := disco.FindPeers(context.TODO(), cfg.Rendezvous)
if err != nil {
return xerrors.Errorf("failed to find new peers: %v", err)
}
@ -197,15 +114,18 @@ func (n *Node) runLibp2pAsync(ctx context.Context) error {
if len(newPeer.Addrs) == 0 {
continue
}
if newPeer.ID.String() == n.Host.ID().String() {
if newPeer.ID.String() == h.ID().String() {
continue
}
logrus.Infof("Found peer: %s", newPeer)
logrus.WithField("peer", newPeer.ID).Info("Discovered new peer, connecting...")
// Connect to the peer
if err := n.Host.Connect(ctx, newPeer); err != nil {
logrus.Warn("Connection failed: ", err)
if err := h.Connect(ctx, newPeer); err != nil {
logrus.WithFields(logrus.Fields{
"peer": newPeer.ID,
"err": err.Error(),
}).Warn("Connection with newly discovered peer has been failed")
}
logrus.Info("Connected to newly discovered peer: ", newPeer)
logrus.WithField("peer", newPeer.ID).Info("Connected to newly discovered peer")
}
}
}
@ -213,10 +133,10 @@ func (n *Node) runLibp2pAsync(ctx context.Context) error {
return nil
}
func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) {
eventChan, subscription, err := n.Ethereum.SubscribeOnOracleEvents(ctx)
func subscribeOnEthContractsAsync(ctx context.Context, ethClient *ethclient.EthereumClient, mp *pool.Mempool) error {
eventChan, subscription, err := ethClient.SubscribeOnOracleEvents(ctx)
if err != nil {
logrus.Fatal("Couldn't subscribe on ethereum contracts, exiting... ", err)
return err
}
go func() {
@ -225,222 +145,78 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) {
select {
case event := <-eventChan:
{
err := n.EventCache.Store("request_"+event.ReqID.String(), event)
if err != nil {
logrus.Errorf("Failed to store new request event to event log cache: %v", err)
}
logrus.Info("Let's wait a little so that all nodes have time to receive the request and cache it")
time.Sleep(5 * time.Second)
task, err := n.Miner.MineTask(context.TODO(), event)
if err != nil {
logrus.Errorf("Failed to mine task: %v", err)
}
if task == nil {
rpcMethod := rpc.GetRPCMethod(event.OriginChain, event.RequestType)
if rpcMethod == nil {
logrus.Errorf("Invalid RPC method name/type %d/%s for oracle request %s", event.OriginChain, event.RequestType, event.ReqID.String())
continue
}
logrus.Infof("Proposed new Dione task with ID: %s", event.ReqID.String())
err = n.ConsensusManager.Propose(*task)
res, err := rpcMethod(event.RequestParams)
if err != nil {
logrus.Errorf("Failed to propose task: %w", err)
logrus.Errorf("Failed to invoke RPC method for oracle request %s: %s", event.ReqID.String(), err.Error())
continue
}
task := &types.DioneTask{
OriginChain: event.OriginChain,
RequestType: event.RequestType,
RequestParams: event.RequestParams,
Payload: res,
RequestID: event.ReqID.String(),
}
data, err := cbor.Marshal(task)
if err != nil {
logrus.Errorf("Failed to marshal RPC response for oracle request %s: %s", event.ReqID.String(), err.Error())
continue
}
tx := types2.CreateTransaction(data)
err = mp.StoreTx(tx)
if err != nil {
logrus.Errorf("Failed to store tx in mempool: %s", err.Error())
continue
}
}
case <-ctx.Done():
break EventLoop
case <-subscription.Err():
logrus.Fatal("Error with ethereum subscription, exiting... ", err)
case err := <-subscription.Err():
logrus.Fatalf("Error has occurred in subscription to Ethereum event channel: %s", err.Error())
}
}
}()
}
func provideEventCache(config *config.Config) cache.EventCache {
var backend cache.EventCache
switch config.CacheType {
case "in-memory":
backend = cache.NewEventLogCache()
case "redis":
backend = cache.NewEventRedisCache(config)
default:
backend = cache.NewEventLogCache()
}
return backend
}
func provideDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, pcm *consensus.PBFTConsensusManager, cfg *config.Config) (*consensus.DisputeManager, error) {
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 {
return consensus.NewMiner(peerID, ethAddress, beacon, ethClient, privateKey)
}
func provideBeacon(ps *pubsub.PubSub) (beacon.BeaconNetworks, error) {
networks := beacon.BeaconNetworks{}
bc, err := drand.NewDrandBeacon(config.ChainGenesis, config.TaskEpochInterval, ps)
if err != nil {
return nil, fmt.Errorf("failed to setup drand beacon: %w", err)
}
networks = append(networks, beacon.BeaconNetwork{Start: types.DrandRound(config.ChainGenesis), Beacon: bc})
// NOTE: currently we use only one network
return networks, nil
}
// FIXME: do we really need this?
func provideWallet(peerID peer.ID, privKey []byte) (*wallet.LocalWallet, error) {
// TODO make persistent keystore
kstore := wallet.NewMemKeyStore()
keyInfo := types.KeyInfo{
Type: types.KTEd25519,
PrivateKey: privKey,
}
kstore.Put(wallet.KNamePrefix+peerID.String(), keyInfo)
w, err := wallet.NewWallet(kstore)
if err != nil {
return nil, xerrors.Errorf("failed to setup wallet: %w", err)
}
return w, nil
}
func provideEthereumClient(config *config.Config) (*ethclient.EthereumClient, error) {
ethereum := ethclient.NewEthereumClient()
err := ethereum.Initialize(&config.Ethereum)
if err != nil {
return nil, xerrors.Errorf("failed to initialize ethereum client: %v", err)
}
return ethereum, nil
}
func (n *Node) setupRPCClients() error {
fc := filecoin.NewLotusClient()
rpc.RegisterRPC(rtypes.RPCTypeFilecoin, map[string]func(string) ([]byte, error){
"getTransaction": fc.GetTransaction,
"getBlock": fc.GetBlock,
})
sl := solana2.NewSolanaClient()
rpc.RegisterRPC(rtypes.RPCTypeSolana, map[string]func(string) ([]byte, error){
"getTransaction": sl.GetTransaction,
})
return nil
}
func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub2.PubSubRouter {
return pubsub2.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap)
}
func provideConsensusManager(psb *pubsub2.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey []byte, minApprovals int, evc cache.EventCache) *consensus.PBFTConsensusManager {
return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner, evc)
}
func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey, pexDiscoveryUpdateTime time.Duration) (host.Host, error) {
listenMultiAddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", config.ListenAddr, config.ListenPort))
if err != nil {
return nil, xerrors.Errorf("failed to parse multiaddress: %v", err)
}
host, err := libp2p.New(
context.TODO(),
libp2p.ListenAddrs(listenMultiAddr),
libp2p.Identity(privateKey),
)
if err != nil {
return nil, xerrors.Errorf("failed to setup libp2p host: %v", err)
}
return host, nil
}
func providePeerDiscovery(config *config.Config, h host.Host, pexDiscoveryUpdateTime time.Duration) (discovery.Discovery, error) {
var bootstrapMaddrs []multiaddr.Multiaddr
for _, a := range config.BootstrapNodes {
maddr, err := multiaddr.NewMultiaddr(a)
if err != nil {
return nil, xerrors.Errorf("invalid multiaddress of bootstrap node: %v", err)
}
bootstrapMaddrs = append(bootstrapMaddrs, maddr)
}
if config.IsBootstrap {
bootstrapMaddrs = nil
}
pexDiscovery, err := pex.NewPEXDiscovery(h, bootstrapMaddrs, pexDiscoveryUpdateTime)
if err != nil {
return nil, xerrors.Errorf("failed to setup pex pexDiscovery: %v", err)
}
return pexDiscovery, nil
}
func Start() {
configPath := flag.String("config", "", "Path to config")
verbose := flag.Bool("verbose", false, "Verbose logging")
flag.Parse()
if *configPath == "" {
logrus.Fatal("no config path provided")
}
cfg, err := config.NewConfig(*configPath)
if err != nil {
logrus.Fatalf("failed to load config: %v", err)
}
var privateKey crypto.PrivKey
if cfg.IsBootstrap {
if _, err := os.Stat(".bootstrap_privkey"); os.IsNotExist(err) {
privateKey, err = generatePrivateKey()
if err != nil {
logrus.Fatal(err)
}
f, _ := os.Create(".bootstrap_privkey")
r, _ := privateKey.Raw()
f.Write(r)
} else {
pkey, _ := ioutil.ReadFile(".bootstrap_privkey")
privateKey, _ = crypto.UnmarshalEd25519PrivateKey(pkey)
}
} else {
privateKey, err = generatePrivateKey()
if err != nil {
logrus.Fatal(err)
}
}
node, err := NewNode(cfg, privateKey, DefaultPEXUpdateTime)
if err != nil {
logrus.Fatal(err)
}
// log
if *verbose {
logrus.SetLevel(logrus.DebugLevel)
} else {
logrus.SetLevel(logrus.DebugLevel)
}
//log.SetDebugLogging()
//ctx, ctxCancel := context.WithCancel(context.Background())
//node.GlobalCtx = ctx
//node.GlobalCtxCancel = ctxCancel
err = node.Run(context.TODO())
if err != nil {
logrus.Fatal(err)
}
}
func generatePrivateKey() (crypto.PrivKey, error) {
r := rand.Reader
// Creates a new RSA key pair for this host.
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, r)
if err != nil {
return nil, err
}
return prvKey, nil
fx.New(
fx.Provide(
provideEventBus,
provideAppFlags,
provideConfig,
providePrivateKey,
provideLibp2pHost,
provideEthereumClient,
providePubsubRouter,
provideBootstrapAddrs,
providePeerDiscovery,
provideDrandBeacon,
provideMempool,
blockchain.NewMiner,
provideBlockChain,
provideBlockPool,
provideSyncManager,
provideNetworkRPCHost,
provideNetworkService,
provideDirectRPCClient,
provideConsensusManager,
provideDisputeManager,
),
fx.Invoke(
configureLogger,
configureDirectRPC,
configureForeignBlockchainRPC,
initializeBlockchain,
configureMiner,
runNode,
),
fx.NopLogger,
).Run()
}

413
node/node_dep_providers.go Normal file
View File

@ -0,0 +1,413 @@
package node
import (
"context"
"crypto/rand"
"flag"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/rpc"
"github.com/Secured-Finance/dione/rpc/filecoin"
solana2 "github.com/Secured-Finance/dione/rpc/solana"
rtypes "github.com/Secured-Finance/dione/rpc/types"
"github.com/sirupsen/logrus"
"github.com/asaskevich/EventBus"
"github.com/Secured-Finance/dione/blockchain"
drand2 "github.com/Secured-Finance/dione/beacon/drand"
"github.com/libp2p/go-libp2p-core/protocol"
gorpc "github.com/libp2p/go-libp2p-gorpc"
"github.com/Secured-Finance/dione/blockchain/sync"
"github.com/Secured-Finance/dione/blockchain/pool"
"github.com/Secured-Finance/dione/cache"
"github.com/Secured-Finance/dione/config"
"github.com/Secured-Finance/dione/consensus"
"github.com/Secured-Finance/dione/ethclient"
"github.com/Secured-Finance/dione/pubsub"
"github.com/Secured-Finance/dione/types"
"github.com/Secured-Finance/dione/wallet"
pex "github.com/Secured-Finance/go-libp2p-pex"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/discovery"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/multiformats/go-multiaddr"
"golang.org/x/xerrors"
)
const (
DioneProtocolID = protocol.ID("/dione/1.0")
)
func provideCache(config *config.Config) cache.Cache {
var backend cache.Cache
switch config.CacheType {
case "in-memory":
backend = cache.NewInMemoryCache()
case "redis":
backend = cache.NewRedisCache(config)
default:
backend = cache.NewInMemoryCache()
}
return backend
}
func provideDisputeManager(ethClient *ethclient.EthereumClient, pcm *consensus.PBFTConsensusManager, cfg *config.Config, bc *blockchain.BlockChain) *consensus.DisputeManager {
dm, err := consensus.NewDisputeManager(context.TODO(), ethClient, pcm, cfg.Ethereum.DisputeVoteWindow, bc)
if err != nil {
logrus.Fatal(err)
}
logrus.Info("Dispute subsystem has been initialized!")
return dm
}
func provideDrandBeacon(ps *pubsub.PubSubRouter, bus EventBus.Bus) *drand2.DrandBeacon {
db, err := drand2.NewDrandBeacon(ps.Pubsub, bus)
if err != nil {
logrus.Fatalf("Failed to setup drand beacon: %s", err)
}
logrus.Info("DRAND beacon subsystem has been initialized!")
return db
}
// FIXME: do we really need this?
func provideWallet(peerID peer.ID, privKey []byte) (*wallet.LocalWallet, error) {
// TODO make persistent keystore
kstore := wallet.NewMemKeyStore()
keyInfo := types.KeyInfo{
Type: types.KTEd25519,
PrivateKey: privKey,
}
kstore.Put(wallet.KNamePrefix+peerID.String(), keyInfo)
w, err := wallet.NewWallet(kstore)
if err != nil {
return nil, xerrors.Errorf("failed to setup wallet: %w", err)
}
return w, nil
}
func provideEthereumClient(config *config.Config) *ethclient.EthereumClient {
ethereum := ethclient.NewEthereumClient()
err := ethereum.Initialize(&config.Ethereum)
if err != nil {
logrus.Fatal(err)
}
logrus.WithField("ethAddress", ethereum.GetEthAddress().Hex()).Info("Ethereum client has been initialized!")
return ethereum
}
func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubRouter {
psb := pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap)
logrus.Info("PubSub subsystem has been initialized!")
return psb
}
func provideConsensusManager(
h host.Host,
bus EventBus.Bus,
psb *pubsub.PubSubRouter,
miner *blockchain.Miner,
bc *blockchain.BlockChain,
ethClient *ethclient.EthereumClient,
privateKey crypto.PrivKey,
bp *consensus.ConsensusStatePool,
db *drand2.DrandBeacon,
mp *pool.Mempool,
) *consensus.PBFTConsensusManager {
c := consensus.NewPBFTConsensusManager(
bus,
psb,
privateKey,
ethClient,
miner,
bc,
bp,
db,
mp,
h.ID(),
)
logrus.Info("Consensus subsystem has been initialized!")
return c
}
func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) host.Host {
listenMultiAddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", config.ListenAddr, config.ListenPort))
if err != nil {
logrus.Fatalf("Failed to parse multiaddress: %s", err.Error())
}
libp2pHost, err := libp2p.New(
context.TODO(),
libp2p.ListenAddrs(listenMultiAddr),
libp2p.Identity(privateKey),
)
if err != nil {
logrus.Fatal(err)
}
logrus.WithField(
"multiaddress",
fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s",
config.ListenAddr,
config.ListenPort,
libp2pHost.ID().Pretty(),
)).Info("Libp2p host has been initialized!")
return libp2pHost
}
func provideNetworkRPCHost(h host.Host) *gorpc.Server {
return gorpc.NewServer(h, DioneProtocolID)
}
func provideBootstrapAddrs(c *config.Config) []multiaddr.Multiaddr {
if c.IsBootstrap {
return nil
}
var bootstrapMaddrs []multiaddr.Multiaddr
for _, a := range c.BootstrapNodes {
maddr, err := multiaddr.NewMultiaddr(a)
if err != nil {
logrus.Fatalf("Invalid multiaddress of bootstrap node: %v", err)
}
bootstrapMaddrs = append(bootstrapMaddrs, maddr)
}
return bootstrapMaddrs
}
func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host) discovery.Discovery {
pexDiscovery, err := pex.NewPEXDiscovery(h, baddrs, DefaultPEXUpdateTime)
if err != nil {
logrus.Fatalf("Failed to setup libp2p PEX discovery: %s", err.Error())
}
logrus.Info("Peer discovery subsystem has been initialized!")
return pexDiscovery
}
func provideBlockChain(config *config.Config, bus EventBus.Bus, miner *blockchain.Miner, db *drand2.DrandBeacon) *blockchain.BlockChain {
bc, err := blockchain.NewBlockChain(config.Blockchain.DatabasePath, bus, miner, db)
if err != nil {
logrus.Fatalf("Failed to initialize blockchain storage: %s", err.Error())
}
logrus.Info("Blockchain storage has been successfully initialized!")
return bc
}
func provideMempool(bus EventBus.Bus) *pool.Mempool {
mp, err := pool.NewMempool(bus)
if err != nil {
logrus.Fatalf("Failed to initialize mempool: %s", err.Error())
}
logrus.Info("Mempool has been successfully initialized!")
return mp
}
func provideSyncManager(
bus EventBus.Bus,
bp *blockchain.BlockChain,
mp *pool.Mempool,
c *gorpc.Client,
bootstrapAddresses []multiaddr.Multiaddr,
psb *pubsub.PubSubRouter,
) sync.SyncManager {
bootstrapPeerID := peer.ID("")
if bootstrapAddresses != nil {
addr, err := peer.AddrInfoFromP2pAddr(bootstrapAddresses[0]) // FIXME
if err != nil {
logrus.Fatal(err)
}
bootstrapPeerID = addr.ID
}
sm := sync.NewSyncManager(bus, bp, mp, c, bootstrapPeerID, psb)
logrus.Info("Blockchain sync subsystem has been successfully initialized!")
return sm
}
func provideDirectRPCClient(h host.Host) *gorpc.Client {
return gorpc.NewClient(h, DioneProtocolID)
}
func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *NetworkService {
ns := NewNetworkService(bp, mp)
logrus.Info("Direct RPC has been successfully initialized!")
return ns
}
func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus, config *config.Config) *consensus.ConsensusStatePool {
bp, err := consensus.NewConsensusRoundPool(mp, bus, config.ConsensusMinApprovals)
if err != nil {
logrus.Fatalf("Failed to initialize blockpool: %s", err.Error())
}
logrus.Info("Consensus state pool has been successfully initialized!")
return bp
}
func provideEventBus() EventBus.Bus {
return EventBus.New()
}
func provideAppFlags() *AppFlags {
var flags AppFlags
flag.StringVar(&flags.ConfigPath, "config", "", "Path to config")
flag.BoolVar(&flags.Verbose, "verbose", false, "Verbose logging")
flag.Parse()
return &flags
}
func provideConfig(flags *AppFlags) *config.Config {
if flags.ConfigPath == "" {
logrus.Fatal("no config path provided")
}
cfg, err := config.NewConfig(flags.ConfigPath)
if err != nil {
logrus.Fatalf("failed to load config: %v", err)
}
return cfg
}
func providePrivateKey(cfg *config.Config) crypto.PrivKey {
var privateKey crypto.PrivKey
if _, err := os.Stat(cfg.PrivateKeyPath); os.IsNotExist(err) {
privateKey, err = generatePrivateKey()
if err != nil {
logrus.Fatal(err)
}
dirName := filepath.Dir(cfg.PrivateKeyPath)
if _, err := os.Stat(dirName); os.IsNotExist(err) {
err := os.MkdirAll(dirName, 0755)
if err != nil {
logrus.Fatalf("Cannot create private key file: %s", err.Error())
}
}
f, err := os.Create(cfg.PrivateKeyPath)
if err != nil {
logrus.Fatalf("Cannot create private key file: %s, ", err)
}
r, err := privateKey.Raw()
if err != nil {
logrus.Fatal(err)
}
_, err = f.Write(r)
if err != nil {
logrus.Fatal(err)
}
} else {
pkey, err := ioutil.ReadFile(cfg.PrivateKeyPath)
if err != nil {
logrus.Fatal(err)
}
privateKey, err = crypto.UnmarshalEd25519PrivateKey(pkey)
if err != nil {
logrus.Fatal(err)
}
}
return privateKey
}
func generatePrivateKey() (crypto.PrivKey, error) {
r := rand.Reader
// Creates a new RSA key pair for this host.
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, r)
if err != nil {
return nil, err
}
return prvKey, nil
}
func configureDirectRPC(rpcServer *gorpc.Server, ns *NetworkService) {
err := rpcServer.Register(ns)
if err != nil {
logrus.Fatal(err)
}
}
func configureLogger(flags *AppFlags) {
logrus.SetReportCaller(true)
logrus.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
CallerPrettyfier: func(f *runtime.Frame) (string, string) {
filename := path.Base(f.File)
return "", fmt.Sprintf("%s:%d:", filename, f.Line)
},
})
if flags.Verbose {
logrus.SetLevel(logrus.DebugLevel)
} else {
logrus.SetLevel(logrus.InfoLevel)
}
}
func configureForeignBlockchainRPC() {
fc := filecoin.NewLotusClient()
rpc.RegisterRPC(rtypes.RPCTypeFilecoin, map[string]func(string) ([]byte, error){
"getTransaction": fc.GetTransaction,
"getBlock": fc.GetBlock,
})
sl := solana2.NewSolanaClient()
rpc.RegisterRPC(rtypes.RPCTypeSolana, map[string]func(string) ([]byte, error){
"getTransaction": sl.GetTransaction,
})
logrus.Info("Foreign Blockchain RPC clients has been successfully configured!")
}
func configureMiner(m *blockchain.Miner, b *blockchain.BlockChain) {
m.SetBlockchainInstance(b)
}
func initializeBlockchain(bc *blockchain.BlockChain) {
_, err := bc.GetLatestBlockHeight()
if err == blockchain.ErrLatestHeightNil {
gBlock := types2.GenesisBlock()
err = bc.StoreBlock(gBlock) // commit genesis block
if err != nil {
logrus.Fatal(err)
}
logrus.Info("Committed genesis block")
}
}

View File

@ -0,0 +1,12 @@
package wire
import "github.com/Secured-Finance/dione/blockchain/types"
type GetMempoolTxsArg struct {
Items [][]byte
}
type GetMempoolTxsReply struct {
Transactions []types.Transaction
NotFoundTxs [][]byte
}

View File

@ -0,0 +1,13 @@
package wire
import "github.com/Secured-Finance/dione/blockchain/types"
type GetRangeOfBlocksArg struct {
From uint64
To uint64
}
type GetRangeOfBlocksReply struct {
Blocks []types.Block
FailedBlockHeights []uint64 // list of block heights the node was unable to retrieve
}

17
node/wire/inv.go Normal file
View File

@ -0,0 +1,17 @@
package wire
type InvType int
const (
InvalidInvType = iota
TxInvType
)
type InvMessage struct {
Inventory []InvItem
}
type InvItem struct {
Type InvType
Hash []byte
}

View File

@ -0,0 +1,5 @@
package wire
type LastBlockHeightReply struct {
Height uint64
}

View File

@ -1,7 +0,0 @@
package pubsub
import (
"github.com/Secured-Finance/dione/consensus/types"
)
type Handler func(message *types.Message)

20
pubsub/message.go Normal file
View File

@ -0,0 +1,20 @@
package pubsub
import "github.com/libp2p/go-libp2p-core/peer"
type PubSubMessageType int
const (
UnknownMessageType = iota
PrePrepareMessageType
PrepareMessageType
CommitMessageType
NewTxMessageType
NewBlockMessageType
)
type PubSubMessage struct {
Type PubSubMessageType
From peer.ID `cbor:"-"`
Payload []byte
}

View File

@ -2,14 +2,11 @@ package pubsub
import (
"context"
"time"
"github.com/fxamacker/cbor/v2"
"github.com/Secured-Finance/dione/consensus/types"
host "github.com/libp2p/go-libp2p-core/host"
peer "github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/sirupsen/logrus"
)
@ -20,11 +17,13 @@ type PubSubRouter struct {
context context.Context
contextCancel context.CancelFunc
serviceSubscription *pubsub.Subscription
handlers map[types.MessageType][]Handler
handlers map[PubSubMessageType][]Handler
oracleTopicName string
oracleTopic *pubsub.Topic
}
type Handler func(message *PubSubMessage)
func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubRouter {
ctx, ctxCancel := context.WithCancel(context.Background())
@ -32,26 +31,27 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR
node: h,
context: ctx,
contextCancel: ctxCancel,
handlers: make(map[types.MessageType][]Handler),
handlers: make(map[PubSubMessageType][]Handler),
oracleTopicName: oracleTopic,
}
var pbOptions []pubsub.Option
if isBootstrap {
// turn off the mesh in bootstrappers -- only do gossip and PX
pubsub.GossipSubD = 0
pubsub.GossipSubDscore = 0
pubsub.GossipSubDlo = 0
pubsub.GossipSubDhi = 0
pubsub.GossipSubDout = 0
pubsub.GossipSubDlazy = 64
pubsub.GossipSubGossipFactor = 0.25
pubsub.GossipSubPruneBackoff = 5 * time.Minute
//pubsub.GossipSubD = 0
//pubsub.GossipSubDscore = 0
//pubsub.GossipSubDlo = 0
//pubsub.GossipSubDhi = 0
//pubsub.GossipSubDout = 0
//pubsub.GossipSubDlazy = 64
//pubsub.GossipSubGossipFactor = 0.25
//pubsub.GossipSubPruneBackoff = 5 * time.Minute
// turn on PX
pbOptions = append(pbOptions, pubsub.WithPeerExchange(true))
//pbOptions = append(pbOptions, pubsub.WithPeerExchange(true))
}
pb, err := pubsub.NewGossipSub(
pb, err := pubsub.NewFloodSub(
context.TODO(),
psr.node,
pbOptions...,
@ -61,7 +61,6 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR
logrus.Fatalf("Error occurred when initializing PubSub subsystem: %v", err)
}
psr.oracleTopicName = oracleTopic
topic, err := pb.Join(oracleTopic)
if err != nil {
logrus.Fatalf("Error occurred when subscribing to service topic: %v", err)
@ -72,6 +71,10 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR
psr.Pubsub = pb
psr.oracleTopic = topic
return psr
}
func (psr *PubSubRouter) Run() {
go func() {
for {
select {
@ -79,7 +82,7 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR
return
default:
{
msg, err := subscription.Next(psr.context)
msg, err := psr.serviceSubscription.Next(psr.context)
if err != nil {
logrus.Warnf("Failed to receive pubsub message: %v", err)
}
@ -88,8 +91,6 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR
}
}
}()
return psr
}
func (psr *PubSubRouter) handleMessage(p *pubsub.Message) {
@ -102,16 +103,16 @@ func (psr *PubSubRouter) handleMessage(p *pubsub.Message) {
if senderPeerID == psr.node.ID() {
return
}
var message types.Message
var message PubSubMessage
err = cbor.Unmarshal(p.Data, &message)
if err != nil {
logrus.Warn("Unable to decode message data! " + err.Error())
logrus.Warn("Unable to decode pubsub message data! " + err.Error())
return
}
message.From = senderPeerID
handlers, ok := psr.handlers[message.Type]
if !ok {
logrus.Warn("Dropping message " + string(message.Type) + " because we don't have any handlers!")
logrus.Warnf("Dropping pubsub message with type %d because we don't have any handlers!", message.Type)
return
}
for _, v := range handlers {
@ -119,7 +120,7 @@ func (psr *PubSubRouter) handleMessage(p *pubsub.Message) {
}
}
func (psr *PubSubRouter) Hook(messageType types.MessageType, handler Handler) {
func (psr *PubSubRouter) Hook(messageType PubSubMessageType, handler Handler) {
_, ok := psr.handlers[messageType]
if !ok {
psr.handlers[messageType] = []Handler{}
@ -127,7 +128,7 @@ func (psr *PubSubRouter) Hook(messageType types.MessageType, handler Handler) {
psr.handlers[messageType] = append(psr.handlers[messageType], handler)
}
func (psr *PubSubRouter) BroadcastToServiceTopic(msg *types.Message) error {
func (psr *PubSubRouter) BroadcastToServiceTopic(msg *PubSubMessage) error {
data, err := cbor.Marshal(msg)
if err != nil {
return err

View File

@ -114,6 +114,6 @@ func (c *LotusClient) HandleRequest(method string, params []interface{}) ([]byte
return nil, err
}
bodyBytes := resp.Body()
logrus.Debugf("Filecoin RPC reply: %v", string(bodyBytes))
logrus.Tracef("Filecoin RPC reply: %v", string(bodyBytes))
return bodyBytes, nil
}

View File

@ -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),
)
}

View File

@ -1,46 +1,44 @@
package store
import (
"github.com/Secured-Finance/dione/node"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
type Store struct {
db *sqlx.DB
node *node.Node
genesisTs uint64
StakeStorage DioneStakeInfo
// genesisTask *types.DioneTask
}
func NewStore(node *node.Node, genesisTs uint64) (*Store, error) {
db, err := newDB(node.Config.Store.DatabaseURL)
if err != nil {
return nil, err
}
defer db.Close()
return &Store{
db: db,
node: node,
genesisTs: genesisTs,
}, nil
}
func newDB(databaseURL string) (*sqlx.DB, error) {
db, err := sqlx.Connect("sqlite3", databaseURL)
if err != nil {
return nil, err
}
if err := db.Ping(); err != nil {
return nil, err
}
return db, nil
}
//import (
// "github.com/Secured-Finance/dione/node"
// "github.com/jmoiron/sqlx"
// _ "github.com/mattn/go-sqlite3"
//)
//
//type Store struct {
// db *sqlx.DB
// node *node.Node
// genesisTs uint64
//}
//
//func NewStore(node *node.Node, genesisTs uint64) (*Store, error) {
// db, err := newDB(node.Config.Store.DatabaseURL)
// if err != nil {
// return nil, err
// }
//
// defer db.Close()
//
// return &Store{
// db: db,
// node: node,
// genesisTs: genesisTs,
// }, nil
//}
//
//func newDB(databaseURL string) (*sqlx.DB, error) {
// db, err := sqlx.Connect("sqlite3", databaseURL)
// if err != nil {
// return nil, err
// }
//
// if err := db.Ping(); err != nil {
// return nil, err
// }
//
// return db, nil
//}
//
// TODO: Discuss with ChronosX88 about using custom database to decrease I/O bound
// specify the migrations for stake storage;

View File

@ -3,13 +3,13 @@ package types
type BeaconEntry struct {
Round uint64
Data []byte
Metadata map[string]interface{}
}
type Randomness []byte
func NewBeaconEntry(round uint64, data []byte) BeaconEntry {
func NewBeaconEntry(round uint64, data []byte, metadata map[string]interface{}) BeaconEntry {
return BeaconEntry{
Round: round,
Data: data,
Metadata: metadata,
}
}

View File

@ -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
}
}

View File

@ -10,6 +10,7 @@ import (
type ElectionProof struct {
WinCount int64
VRFProof []byte
RandomnessRound uint64
}
const precision = 256
@ -99,13 +100,13 @@ func polyval(p []*big.Int, x *big.Int) *big.Int {
// computes lambda in Q.256
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.Div(lam /* Q.256 */, totalPower /* Q.0 */) // Q.256
return lam
}
var MaxWinCount = 3 * int64(config.TasksPerEpoch)
var MaxWinCount = 10 * config.ExpectedLeadersPerEpoch.Int64()
type poiss struct {
lam *big.Int
@ -175,10 +176,10 @@ func (p *poiss) next() *big.Int {
// ComputeWinCount uses VRFProof to compute number of wins
// The algorithm is based on Algorand's Sortition with Binomial 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)
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
// rate λ=power*E/totalPower
@ -191,7 +192,7 @@ func (ep *ElectionProof) ComputeWinCount(power BigInt, totalPower BigInt) int64
// rhs = 1 - 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)

View File

@ -1,145 +0,0 @@
package types
import (
"bytes"
"fmt"
"math/big"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/xorcare/golden"
)
func TestPoissonFunction(t *testing.T) {
tests := []struct {
lambdaBase uint64
lambdaShift uint
}{
{10, 10}, // 0.0097
{209714, 20}, // 0.19999885
{1036915, 20}, // 0.9888792038
{1706, 10}, // 1.6660
{2, 0}, // 2
{5242879, 20}, //4.9999990
{5, 0}, // 5
}
for _, test := range tests {
test := test
t.Run(fmt.Sprintf("lam-%d-%d", test.lambdaBase, test.lambdaShift), func(t *testing.T) {
b := &bytes.Buffer{}
b.WriteString("icdf\n")
lam := new(big.Int).SetUint64(test.lambdaBase)
lam = lam.Lsh(lam, precision-test.lambdaShift)
p, icdf := newPoiss(lam)
b.WriteString(icdf.String())
b.WriteRune('\n')
for i := 0; i < 15; i++ {
b.WriteString(p.next().String())
b.WriteRune('\n')
}
golden.Assert(t, b.Bytes())
})
}
}
func TestLambdaFunction(t *testing.T) {
tests := []struct {
power string
totalPower string
target float64
}{
{"10", "100", .1 * 5.},
{"1024", "2048", 0.5 * 5.},
{"2000000000000000", "100000000000000000", 0.02 * 5.},
}
for _, test := range tests {
test := test
t.Run(fmt.Sprintf("%s-%s", test.power, test.totalPower), func(t *testing.T) {
pow, ok := new(big.Int).SetString(test.power, 10)
assert.True(t, ok)
total, ok := new(big.Int).SetString(test.totalPower, 10)
assert.True(t, ok)
lam := lambda(pow, total)
assert.Equal(t, test.target, q256ToF(lam))
golden.Assert(t, []byte(lam.String()))
})
}
}
func TestExpFunction(t *testing.T) {
const N = 256
step := big.NewInt(5)
step = step.Lsh(step, 256) // Q.256
step = step.Div(step, big.NewInt(N-1))
x := big.NewInt(0)
b := &bytes.Buffer{}
b.WriteString("x, y\n")
for i := 0; i < N; i++ {
y := expneg(x)
fmt.Fprintf(b, "%s,%s\n", x, y)
x = x.Add(x, step)
}
golden.Assert(t, b.Bytes())
}
func q256ToF(x *big.Int) float64 {
deno := big.NewInt(1)
deno = deno.Lsh(deno, 256)
rat := new(big.Rat).SetFrac(x, deno)
f, _ := rat.Float64()
return f
}
func TestElectionLam(t *testing.T) {
p := big.NewInt(64)
tot := big.NewInt(128)
lam := lambda(p, tot)
target := 64. * 5. / 128.
if q256ToF(lam) != target {
t.Fatalf("wrong lambda: %f, should be: %f", q256ToF(lam), target)
}
}
var Res int64
func BenchmarkWinCounts(b *testing.B) {
totalPower := NewInt(100)
power := NewInt(100)
ep := &ElectionProof{VRFProof: nil}
var res int64
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
ep.VRFProof = []byte{byte(i), byte(i >> 8), byte(i >> 16), byte(i >> 24), byte(i >> 32)}
j := ep.ComputeWinCount(power, totalPower)
res += j
}
Res += res
}
func TestWinCounts(t *testing.T) {
t.SkipNow()
totalPower := NewInt(100)
power := NewInt(30)
f, _ := os.Create("output.wins")
fmt.Fprintf(f, "wins\n")
ep := &ElectionProof{VRFProof: nil}
for i := uint64(0); i < 1000000; i++ {
i := i + 1000000
ep.VRFProof = []byte{byte(i), byte(i >> 8), byte(i >> 16), byte(i >> 24), byte(i >> 32)}
j := ep.ComputeWinCount(power, totalPower)
fmt.Fprintf(f, "%d\n", j)
}
}

View File

@ -1,63 +1,12 @@
package types
import (
"strconv"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/config"
)
// DrandRound represents the round number in DRAND
type DrandRound int64
const TicketRandomnessLookback = DrandRound(1)
func (e DrandRound) String() string {
return strconv.FormatInt(int64(e), 10)
}
const TicketRandomnessLookback = 1
// DioneTask represents the values of task computation
type DioneTask struct {
OriginChain uint8
RequestType string
RequestParams string
Miner peer.ID
MinerEth string
Ticket *Ticket
ElectionProof *ElectionProof
BeaconEntries []BeaconEntry
DrandRound DrandRound
Payload []byte
RequestID string
ConsensusID string
Signature []byte `hash:"-"`
}
func NewDioneTask(
originChain uint8,
requestType string,
requestParams string,
miner peer.ID,
ticket *Ticket,
electionProof *ElectionProof,
beacon []BeaconEntry,
drand DrandRound,
payload []byte,
) *DioneTask {
return &DioneTask{
OriginChain: originChain,
RequestType: requestType,
RequestParams: requestParams,
Miner: miner,
Ticket: ticket,
ElectionProof: electionProof,
BeaconEntries: beacon,
DrandRound: drand,
Payload: payload,
}
}
var tasksPerEpoch = NewInt(config.TasksPerEpoch)
const sha256bits = 256

View File

@ -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
}

View File

@ -50,7 +50,7 @@ func KeyWallet(keys ...*Key) *LocalWallet {
}
}
func (w *LocalWallet) WalletSign(ctx context.Context, addr peer.ID, msg []byte) (*types.Signature, error) {
func (w *LocalWallet) Sign(addr peer.ID, msg []byte) (*types.Signature, error) {
ki, err := w.findKey(addr)
if err != nil {
return nil, err
@ -143,7 +143,7 @@ func (w *LocalWallet) GetDefault() (peer.ID, error) {
return k.Address, nil
}
func (w *LocalWallet) WalletNew(ctx context.Context, typ types.KeyType) (peer.ID, error) {
func (w *LocalWallet) NewKey(typ types.KeyType) (peer.ID, error) {
w.lk.Lock()
defer w.lk.Unlock()