Merge pull request #16 from Secured-Finance/feat/blockchain
Implement blockchain for Dione, fix most consensus issues
This commit is contained in:
commit
1d12828313
4
.gitignore
vendored
4
.gitignore
vendored
@ -24,4 +24,6 @@ eth-contracts/node_modules
|
||||
/.dione-config-3.toml
|
||||
/.dione-config-4.toml
|
||||
.bootstrap_privkey
|
||||
eth-contracts/build
|
||||
eth-contracts/build
|
||||
.db
|
||||
eth-contracts/secrets.json
|
112
beacon/beacon.go
112
beacon/beacon.go
@ -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]
|
||||
}
|
||||
}
|
||||
|
8
beacon/domain_separation_tag.go
Normal file
8
beacon/domain_separation_tag.go
Normal file
@ -0,0 +1,8 @@
|
||||
package beacon
|
||||
|
||||
// RandomnessType specifies a type of randomness.
|
||||
type RandomnessType int64
|
||||
|
||||
const (
|
||||
RandomnessTypeElectionProofProduction RandomnessType = 1 + iota
|
||||
)
|
@ -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
|
||||
cacheLock sync.Mutex
|
||||
localCache map[uint64]types.BeaconEntry
|
||||
DrandClient client.Client
|
||||
PublicKey kyber.Point
|
||||
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()
|
||||
}
|
||||
log.Infof("done fetching randomness: round %v, took %v", round, lib.Clock.Since(start))
|
||||
out <- br
|
||||
close(out)
|
||||
}()
|
||||
|
||||
return out
|
||||
start := lib.Clock.Now()
|
||||
log.Infof("start fetching randomness: round %v", round)
|
||||
resp, err := db.DrandClient.Get(ctx, round)
|
||||
if err != nil {
|
||||
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))
|
||||
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)
|
||||
}
|
||||
return latestDround.Round()
|
||||
db.cacheLock.Lock()
|
||||
defer db.cacheLock.Unlock()
|
||||
return db.latestDrandRound
|
||||
}
|
||||
|
||||
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
29
beacon/utils.go
Normal 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
28
beacon/vrf.go
Normal 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
434
blockchain/blockchain.go
Normal 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
199
blockchain/miner.go
Normal 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
107
blockchain/pool/mempool.go
Normal 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
278
blockchain/sync/sync_mgr.go
Normal 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
109
blockchain/types/block.go
Normal 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
|
||||
}
|
36
blockchain/types/transaction.go
Normal file
36
blockchain/types/transaction.go
Normal 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
96
blockchain/utils/index.go
Normal 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))
|
||||
}
|
27
blockchain/utils/verification.go
Normal file
27
blockchain/utils/verification.go
Normal 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
17
cache/cache.go
vendored
Normal 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
|
||||
}
|
9
cache/event_cache_interface.go
vendored
9
cache/event_cache_interface.go
vendored
@ -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)
|
||||
}
|
50
cache/event_log_cache.go
vendored
50
cache/event_log_cache.go
vendored
@ -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))
|
||||
}
|
58
cache/event_redis_cache.go
vendored
58
cache/event_redis_cache.go
vendored
@ -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
69
cache/inmemory_cache.go
vendored
Normal 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
98
cache/redis_cache.go
vendored
Normal 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
|
||||
}
|
@ -7,18 +7,20 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ListenPort int `mapstructure:"listen_port"`
|
||||
ListenAddr string `mapstructure:"listen_addr"`
|
||||
IsBootstrap bool `mapstructure:"is_bootstrap"`
|
||||
BootstrapNodes []string `mapstructure:"bootstrap_node_multiaddr"`
|
||||
Rendezvous string `mapstructure:"rendezvous"`
|
||||
Ethereum EthereumConfig `mapstructure:"ethereum"`
|
||||
Filecoin FilecoinConfig `mapstructure:"filecoin"`
|
||||
PubSub PubSubConfig `mapstructure:"pubSub"`
|
||||
Store StoreConfig `mapstructure:"store"`
|
||||
ConsensusMinApprovals int `mapstructure:"consensus_min_approvals"`
|
||||
Redis RedisConfig `mapstructure:"redis"`
|
||||
CacheType string `mapstructure:"cache_type"`
|
||||
ListenPort int `mapstructure:"listen_port"`
|
||||
ListenAddr string `mapstructure:"listen_addr"`
|
||||
IsBootstrap bool `mapstructure:"is_bootstrap"`
|
||||
BootstrapNodes []string `mapstructure:"bootstrap_node_multiaddr"`
|
||||
Rendezvous string `mapstructure:"rendezvous"`
|
||||
Ethereum EthereumConfig `mapstructure:"ethereum"`
|
||||
Filecoin FilecoinConfig `mapstructure:"filecoin"`
|
||||
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,
|
||||
|
@ -28,3 +28,5 @@ func NewDrandConfig() *DrandConfig {
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
var DrandChainGenesisTime = uint64(1603603302)
|
||||
|
@ -1,9 +0,0 @@
|
||||
package config
|
||||
|
||||
var ExpectedLeadersPerEpoch = int64(5)
|
||||
|
||||
var TasksPerEpoch = uint64(ExpectedLeadersPerEpoch)
|
||||
|
||||
var ChainGenesis = uint64(1603603302)
|
||||
|
||||
var TaskEpochInterval = uint64(15)
|
5
config/win_config.go
Normal file
5
config/win_config.go
Normal file
@ -0,0 +1,5 @@
|
||||
package config
|
||||
|
||||
import "math/big"
|
||||
|
||||
var ExpectedLeadersPerEpoch = big.NewInt(2)
|
@ -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 {
|
||||
psb *pubsub.PubSubRouter
|
||||
minApprovals int
|
||||
privKey []byte
|
||||
msgLog *MessageLog
|
||||
validator *ConsensusValidator
|
||||
consensusMap map[string]*Consensus
|
||||
ethereumClient *ethclient.EthereumClient
|
||||
miner *Miner
|
||||
eventCache cache.EventCache
|
||||
bus EventBus.Bus
|
||||
psb *pubsub.PubSubRouter
|
||||
privKey crypto.PrivKey
|
||||
validator *ConsensusValidator
|
||||
ethereumClient *ethclient.EthereumClient
|
||||
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{},
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
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)
|
||||
logrus.Errorf("failed to convert payload to PrePrepare message: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
pcm.createConsensusInfo(&message.Payload.Task, false)
|
||||
if *prePrepare.Block.Header.Proposer == pcm.address {
|
||||
return
|
||||
}
|
||||
|
||||
pcm.psb.BroadcastToServiceTopic(&prepareMsg)
|
||||
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.WithField("err", err.Error()).Warn("Failed to add PREPARE message to log")
|
||||
return
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%x", cmsg.Block.Header.Hash),
|
||||
"from": message.From.String(),
|
||||
}).Debug("Received PREPREPARE message")
|
||||
}
|
||||
|
||||
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...")
|
||||
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
|
||||
}
|
||||
|
||||
pcm.msgLog.AddMessage(*message)
|
||||
cmsg := &types.ConsensusMessage{
|
||||
Type: types.ConsensusMessageTypePrepare,
|
||||
From: message.From,
|
||||
Blockhash: prepare.Blockhash,
|
||||
Signature: prepare.Signature,
|
||||
}
|
||||
|
||||
if len(pcm.msgLog.GetMessagesByTypeAndConsensusID(types.MessageTypePrepare, message.Payload.Task.ConsensusID)) >= pcm.minApprovals {
|
||||
commitMsg, err := NewMessage(message, types.MessageTypeCommit)
|
||||
if !pcm.validator.Valid(cmsg) {
|
||||
logrus.Warnf("received invalid prepare msg for block %x", cmsg.Blockhash)
|
||||
return
|
||||
}
|
||||
|
||||
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 create commit message: %w", err)
|
||||
logrus.Errorf("Failed to serialize block %x for broadcasting!", block.Header.Hash)
|
||||
} else {
|
||||
newBlockMessage.Payload = blockSerialized
|
||||
pcm.psb.BroadcastToServiceTopic(&newBlockMessage)
|
||||
}
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
if !pcm.validator.Valid(*message) {
|
||||
logrus.Warn("received invalid commit msg, dropping...")
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pcm.msgLog.AddMessage(*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...")
|
||||
// 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
|
||||
}
|
||||
info.mutex.Lock()
|
||||
defer info.mutex.Unlock()
|
||||
if info.Finished {
|
||||
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)
|
||||
if !ok {
|
||||
logrus.Errorf("Failed to parse request ID: %v", consensusMsg.Task.RequestID)
|
||||
}
|
||||
|
||||
err := pcm.ethereumClient.SubmitRequestAnswer(reqID, consensusMsg.Task.Payload)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to submit on-chain result: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
info.Finished = true
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
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.WithFields(logrus.Fields{
|
||||
"txHash": hex.EncodeToString(tx.Hash),
|
||||
}).Error("Failed to parse request id number in Dione task")
|
||||
continue // FIXME
|
||||
}
|
||||
|
||||
err = pcm.ethereumClient.SubmitRequestAnswer(reqIDNumber, task.Payload)
|
||||
if err != nil {
|
||||
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)")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
jStake, err := pcm.ethereumClient.GetMinerStake(blocks[j].Block.Header.ProposerEth)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pcm *PBFTConsensusManager) GetConsensusInfo(consensusID string) *Consensus {
|
||||
c, ok := pcm.consensusMap[consensusID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c
|
||||
return selectedBlock, pcm.blockchain.StoreBlock(selectedBlock)
|
||||
}
|
||||
|
126
consensus/consensus_round_pool.go
Normal file
126
consensus/consensus_round_pool.go
Normal 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
|
||||
//}
|
@ -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
|
||||
}
|
||||
|
@ -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,74 +29,83 @@ 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
|
||||
}
|
||||
|
||||
dm := &DisputeManager{
|
||||
ethClient: ethClient,
|
||||
pcm: pcm,
|
||||
ctx: ctx,
|
||||
submissionMap: map[string]*dioneOracle.DioneOracleSubmittedOracleRequest{},
|
||||
disputeMap: map[string]*dioneDispute.DioneDisputeNewDispute{},
|
||||
voteWindow: time.Duration(voteWindow) * time.Second,
|
||||
ethClient: ethClient,
|
||||
pcm: pcm,
|
||||
ctx: ctx,
|
||||
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
|
||||
|
@ -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
|
||||
}
|
@ -1,40 +1,50 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
types2 "github.com/Secured-Finance/dione/consensus/types"
|
||||
mapset "github.com/Secured-Finance/golang-set"
|
||||
)
|
||||
|
||||
type MessageLog struct {
|
||||
messages mapset.Set
|
||||
maxLogSize int
|
||||
validationFuncMap map[types2.MessageType]func(message types2.Message)
|
||||
type ConsensusMessageLog struct {
|
||||
messages mapset.Set
|
||||
}
|
||||
|
||||
func NewMessageLog() *MessageLog {
|
||||
msgLog := &MessageLog{
|
||||
messages: mapset.NewSet(),
|
||||
maxLogSize: 0, // TODO
|
||||
func NewConsensusMessageLog() *ConsensusMessageLog {
|
||||
msgLog := &ConsensusMessageLog{
|
||||
messages: mapset.NewSet(),
|
||||
}
|
||||
|
||||
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 {
|
||||
result = append(result, msg)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
7
consensus/policy/policy.go
Normal file
7
consensus/policy/policy.go
Normal file
@ -0,0 +1,7 @@
|
||||
package policy
|
||||
|
||||
const (
|
||||
BlockMaxTransactionCount = 100
|
||||
MaxBlockCountForRetrieving = 500 // we do it just like in Bitcoin
|
||||
MaxTransactionCountForRetrieving = 50000
|
||||
)
|
25
consensus/types/consensus_message.go
Normal file
25
consensus/types/consensus_message.go
Normal file
@ -0,0 +1,25 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
types3 "github.com/Secured-Finance/dione/blockchain/types"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
type ConsensusMessageType uint8
|
||||
|
||||
const (
|
||||
ConsensusMessageTypeUnknown = ConsensusMessageType(iota)
|
||||
|
||||
ConsensusMessageTypePrePrepare
|
||||
ConsensusMessageTypePrepare
|
||||
ConsensusMessageTypeCommit
|
||||
)
|
||||
|
||||
// ConsensusMessage is common struct for various consensus message types. It is stored in consensus message log.
|
||||
type ConsensusMessage struct {
|
||||
Type ConsensusMessageType
|
||||
Blockhash []byte
|
||||
Signature []byte
|
||||
Block *types3.Block // it is optional, because not all message types have block included
|
||||
From peer.ID
|
||||
}
|
@ -1,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
|
||||
|
@ -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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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, fmt.Errorf("failed to convert message to map: %s", err.Error())
|
||||
}
|
||||
message.Payload = data
|
||||
break
|
||||
}
|
||||
case types2.ConsensusMessageTypePrepare:
|
||||
{
|
||||
message.Type = pubsub.PrepareMessageType
|
||||
signature, err := privKey.Sign(cmsg.Blockhash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create signature: %v", err)
|
||||
}
|
||||
pm := types2.PrepareMessage{
|
||||
Blockhash: cmsg.Blockhash,
|
||||
Signature: signature,
|
||||
}
|
||||
data, err := cbor.Marshal(pm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert message to map: %s", err.Error())
|
||||
}
|
||||
message.Payload = data
|
||||
break
|
||||
}
|
||||
case types2.ConsensusMessageTypeCommit:
|
||||
{
|
||||
message.Type = pubsub.CommitMessageType
|
||||
signature, err := privKey.Sign(cmsg.Blockhash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create signature: %v", err)
|
||||
}
|
||||
pm := types2.CommitMessage{
|
||||
Blockhash: cmsg.Blockhash,
|
||||
Signature: signature,
|
||||
}
|
||||
data, err := cbor.Marshal(pm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert message to map: %s", err.Error())
|
||||
}
|
||||
message.Payload = data
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sig.Type != types.SigTypeEd25519 {
|
||||
return nil, fmt.Errorf("miner worker address was not a Ed25519 key")
|
||||
}
|
||||
|
||||
return sig.Data, nil
|
||||
}
|
||||
|
||||
func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error {
|
||||
err := sigs.Verify(&types.Signature{Type: types.SigTypeEd25519, Data: vrfproof}, []byte(worker), vrfBase)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("vrf was invalid: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsRoundWinner(round types.DrandRound,
|
||||
worker peer.ID, brand types.BeaconEntry, minerStake, networkStake types.BigInt, sign SignFunc) (*types.ElectionProof, error) {
|
||||
|
||||
buf, err := worker.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to marshal address: %w", err)
|
||||
}
|
||||
|
||||
electionRand, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
|
||||
}
|
||||
|
||||
vrfout, err := ComputeVRF(sign, worker, electionRand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
|
17
consensus/validation/solana/solana.go
Normal file
17
consensus/validation/solana/solana.go
Normal 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,
|
||||
})
|
||||
}
|
24
consensus/validation/utils.go
Normal file
24
consensus/validation/utils.go
Normal 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
|
||||
}
|
@ -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")
|
||||
}
|
3
eth-contracts/.gitignore
vendored
3
eth-contracts/.gitignore
vendored
@ -3,4 +3,5 @@ artifacts
|
||||
cache
|
||||
dist
|
||||
coverage
|
||||
coverage.json
|
||||
coverage.json
|
||||
scripts/private
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
};
|
34
eth-contracts/hardhat.tasks.ts
Normal file
34
eth-contracts/hardhat.tasks.ts
Normal 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)
|
||||
})
|
696
eth-contracts/package-lock.json
generated
696
eth-contracts/package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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 () {
|
||||
|
@ -4,7 +4,8 @@
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "dist"
|
||||
"outDir": "dist",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["./scripts", "./test", "./common"],
|
||||
"files": ["./hardhat.config.ts"]
|
||||
|
@ -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
42
go.mod
@ -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
126
go.sum
@ -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
6
node/flags.go
Normal file
@ -0,0 +1,6 @@
|
||||
package node
|
||||
|
||||
type AppFlags struct {
|
||||
ConfigPath string
|
||||
Verbose bool
|
||||
}
|
95
node/network_service.go
Normal file
95
node/network_service.go
Normal 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
|
||||
}
|
478
node/node.go
478
node/node.go
@ -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 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTime time.Duration) (*Node, error) {
|
||||
n := &Node{
|
||||
Config: config,
|
||||
}
|
||||
err = db.Run(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// initialize libp2p host
|
||||
lhost, err := provideLibp2pHost(n.Config, prvKey, pexDiscoveryUpdateTime)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
n.Host = lhost
|
||||
logrus.Info("Started up Libp2p host!")
|
||||
// Run pubsub router
|
||||
pubSubRouter.Run()
|
||||
|
||||
// initialize ethereum client
|
||||
ethClient, err := provideEthereumClient(n.Config)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
n.Ethereum = ethClient
|
||||
logrus.Info("Started up Ethereum client!")
|
||||
// Subscribe on new requests event channel from Ethereum
|
||||
err = subscribeOnEthContractsAsync(context.TODO(), ethClient, mp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// initialize blockchain rpc clients
|
||||
err = n.setupRPCClients()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
logrus.Info("RPC clients has successfully configured!")
|
||||
// Run blockchain sync manager
|
||||
syncManager.Run()
|
||||
|
||||
// initialize pubsub subsystem
|
||||
psb := providePubsubRouter(lhost, n.Config)
|
||||
n.PubSubRouter = psb
|
||||
logrus.Info("PubSub subsystem has initialized!")
|
||||
// Run dispute manager
|
||||
disputeManager.Run(context.TODO())
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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!")
|
||||
|
||||
// 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!")
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// return nil
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
// TODO
|
||||
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
413
node/node_dep_providers.go
Normal 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")
|
||||
}
|
||||
}
|
12
node/wire/get_mempool_tx.go
Normal file
12
node/wire/get_mempool_tx.go
Normal 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
|
||||
}
|
13
node/wire/get_range_of_blocks.go
Normal file
13
node/wire/get_range_of_blocks.go
Normal 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
17
node/wire/inv.go
Normal 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
|
||||
}
|
5
node/wire/last_block_height.go
Normal file
5
node/wire/last_block_height.go
Normal file
@ -0,0 +1,5 @@
|
||||
package wire
|
||||
|
||||
type LastBlockHeightReply struct {
|
||||
Height uint64
|
||||
}
|
@ -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
20
pubsub/message.go
Normal 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
|
||||
}
|
@ -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,38 +17,41 @@ 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())
|
||||
|
||||
psr := &PubSubRouter{
|
||||
node: h,
|
||||
context: ctx,
|
||||
contextCancel: ctxCancel,
|
||||
handlers: make(map[types.MessageType][]Handler),
|
||||
node: h,
|
||||
context: ctx,
|
||||
contextCancel: ctxCancel,
|
||||
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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,96 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Secured-Finance/dione/ethclient"
|
||||
"github.com/Secured-Finance/dione/lib"
|
||||
"github.com/Secured-Finance/dione/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
validation "github.com/go-ozzo/ozzo-validation"
|
||||
)
|
||||
|
||||
type DioneStakeInfo struct {
|
||||
ID int
|
||||
MinerStake *types.BigInt
|
||||
TotalStake *types.BigInt
|
||||
MinerAddress string
|
||||
MinerEthWallet string
|
||||
Timestamp time.Time
|
||||
Ethereum *ethclient.EthereumClient
|
||||
}
|
||||
|
||||
func NewDioneStakeInfo(minerStake, totalStake *types.BigInt, minerWallet, minerEthWallet string, ethereumClient *ethclient.EthereumClient) *DioneStakeInfo {
|
||||
return &DioneStakeInfo{
|
||||
MinerStake: minerStake,
|
||||
TotalStake: totalStake,
|
||||
MinerAddress: minerWallet,
|
||||
MinerEthWallet: minerEthWallet,
|
||||
Ethereum: ethereumClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DioneStakeInfo) UpdateMinerStake(minerEthAddress common.Address) error {
|
||||
minerStake, err := d.Ethereum.GetMinerStake(minerEthAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.MinerStake = minerStake
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DioneStakeInfo) UpdateTotalStake() error {
|
||||
totalStake, err := d.Ethereum.GetTotalStake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.TotalStake = totalStake
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Put miner's staking information into the database
|
||||
func (s *Store) CreateDioneStakeInfo(stakeStore *DioneStakeInfo) error {
|
||||
|
||||
if err := stakeStore.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := lib.Clock.Now()
|
||||
|
||||
return s.db.QueryRow(
|
||||
"INSERT INTO staking (miner_stake, total_stake, miner_address, miner_eth_wallet, timestamp) VALUES ($1, $2, $3, $4, $5) RETURNING id",
|
||||
stakeStore.MinerStake,
|
||||
stakeStore.TotalStake,
|
||||
stakeStore.MinerAddress,
|
||||
stakeStore.MinerEthWallet,
|
||||
now,
|
||||
).Scan(&stakeStore.ID)
|
||||
}
|
||||
|
||||
func (s *Store) GetLastStakeInfo(wallet, ethWallet string) (*DioneStakeInfo, error) {
|
||||
var stake *DioneStakeInfo
|
||||
if err := s.db.Select(&stake,
|
||||
`SELECT miner_stake, total_stake, miner_address, miner_eth_wallet, timestamp FROM staking ORDER BY TIMESTAMP DESC LIMIT 1 WHERE miner_address=$1, miner_eth_wallet=$2`,
|
||||
wallet,
|
||||
ethWallet,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stake, nil
|
||||
}
|
||||
|
||||
// Before puting the data into the database validating all required fields
|
||||
func (s *DioneStakeInfo) Validate() error {
|
||||
return validation.ValidateStruct(
|
||||
s,
|
||||
validation.Field(&s.MinerStake, validation.Required, validation.By(types.ValidateBigInt(s.MinerStake.Int))),
|
||||
validation.Field(&s.TotalStake, validation.Required, validation.By(types.ValidateBigInt(s.TotalStake.Int))),
|
||||
validation.Field(&s.MinerAddress, validation.Required),
|
||||
validation.Field(&s.MinerEthWallet, validation.Required),
|
||||
)
|
||||
}
|
@ -1,46 +1,44 @@
|
||||
package store
|
||||
|
||||
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;
|
||||
// specify the migrations for stake storage;
|
||||
|
@ -1,15 +1,15 @@
|
||||
package types
|
||||
|
||||
type BeaconEntry struct {
|
||||
Round uint64
|
||||
Data []byte
|
||||
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,
|
||||
Round: round,
|
||||
Data: data,
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -8,8 +8,9 @@ import (
|
||||
)
|
||||
|
||||
type ElectionProof struct {
|
||||
WinCount int64
|
||||
VRFProof []byte
|
||||
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 = lam.Lsh(lam, precision) // Q.256
|
||||
lam = lam.Div(lam /* Q.256 */, totalPower /* Q.0 */) // Q.256
|
||||
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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user