diff --git a/.gitignore b/.gitignore index 8e77007..45d3320 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ eth-contracts/node_modules /.dione-config-3.toml /.dione-config-4.toml .bootstrap_privkey -eth-contracts/build \ No newline at end of file +eth-contracts/build +.db +eth-contracts/secrets.json \ No newline at end of file diff --git a/beacon/beacon.go b/beacon/beacon.go index 1589a51..fa4e57b 100644 --- a/beacon/beacon.go +++ b/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] - } -} diff --git a/beacon/domain_separation_tag.go b/beacon/domain_separation_tag.go new file mode 100644 index 0000000..d2196d6 --- /dev/null +++ b/beacon/domain_separation_tag.go @@ -0,0 +1,8 @@ +package beacon + +// RandomnessType specifies a type of randomness. +type RandomnessType int64 + +const ( + RandomnessTypeElectionProofProduction RandomnessType = 1 + iota +) diff --git a/drand/drand.go b/beacon/drand/drand.go similarity index 54% rename from drand/drand.go rename to beacon/drand/drand.go index 529f158..00e1ef6 100644 --- a/drand/drand.go +++ b/beacon/drand/drand.go @@ -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) diff --git a/beacon/utils.go b/beacon/utils.go new file mode 100644 index 0000000..3f47e4d --- /dev/null +++ b/beacon/utils.go @@ -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 +} diff --git a/beacon/vrf.go b/beacon/vrf.go new file mode 100644 index 0000000..5e1b7a0 --- /dev/null +++ b/beacon/vrf.go @@ -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 +} diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go new file mode 100644 index 0000000..86dbc3d --- /dev/null +++ b/blockchain/blockchain.go @@ -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 +} diff --git a/blockchain/miner.go b/blockchain/miner.go new file mode 100644 index 0000000..3f5c97d --- /dev/null +++ b/blockchain/miner.go @@ -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 +} diff --git a/blockchain/pool/mempool.go b/blockchain/pool/mempool.go new file mode 100644 index 0000000..0a64d55 --- /dev/null +++ b/blockchain/pool/mempool.go @@ -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 +} diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go new file mode 100644 index 0000000..ce9f51e --- /dev/null +++ b/blockchain/sync/sync_mgr.go @@ -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 + } +} diff --git a/blockchain/types/block.go b/blockchain/types/block.go new file mode 100644 index 0000000..ce99ec2 --- /dev/null +++ b/blockchain/types/block.go @@ -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 +} diff --git a/blockchain/types/transaction.go b/blockchain/types/transaction.go new file mode 100644 index 0000000..96481bf --- /dev/null +++ b/blockchain/types/transaction.go @@ -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) +} diff --git a/blockchain/utils/index.go b/blockchain/utils/index.go new file mode 100644 index 0000000..24a7896 --- /dev/null +++ b/blockchain/utils/index.go @@ -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)) +} diff --git a/blockchain/utils/verification.go b/blockchain/utils/verification.go new file mode 100644 index 0000000..21e9742 --- /dev/null +++ b/blockchain/utils/verification.go @@ -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 +} diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..96f37fe --- /dev/null +++ b/cache/cache.go @@ -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 +} diff --git a/cache/event_cache_interface.go b/cache/event_cache_interface.go deleted file mode 100644 index e9f5f39..0000000 --- a/cache/event_cache_interface.go +++ /dev/null @@ -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) -} diff --git a/cache/event_log_cache.go b/cache/event_log_cache.go deleted file mode 100644 index 4a5ec61..0000000 --- a/cache/event_log_cache.go +++ /dev/null @@ -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)) -} diff --git a/cache/event_redis_cache.go b/cache/event_redis_cache.go deleted file mode 100644 index f259ddf..0000000 --- a/cache/event_redis_cache.go +++ /dev/null @@ -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) -} diff --git a/cache/inmemory_cache.go b/cache/inmemory_cache.go new file mode 100644 index 0000000..0acef75 --- /dev/null +++ b/cache/inmemory_cache.go @@ -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 +} diff --git a/cache/redis_cache.go b/cache/redis_cache.go new file mode 100644 index 0000000..9eacdab --- /dev/null +++ b/cache/redis_cache.go @@ -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 +} diff --git a/config/config.go b/config/config.go index 2c25fa9..337a6b7 100644 --- a/config/config.go +++ b/config/config.go @@ -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, diff --git a/config/drand.go b/config/drand.go index dc73536..7db057f 100644 --- a/config/drand.go +++ b/config/drand.go @@ -28,3 +28,5 @@ func NewDrandConfig() *DrandConfig { } return cfg } + +var DrandChainGenesisTime = uint64(1603603302) diff --git a/config/tasks.go b/config/tasks.go deleted file mode 100644 index 29e87ae..0000000 --- a/config/tasks.go +++ /dev/null @@ -1,9 +0,0 @@ -package config - -var ExpectedLeadersPerEpoch = int64(5) - -var TasksPerEpoch = uint64(ExpectedLeadersPerEpoch) - -var ChainGenesis = uint64(1603603302) - -var TaskEpochInterval = uint64(15) diff --git a/config/win_config.go b/config/win_config.go new file mode 100644 index 0000000..10a76c0 --- /dev/null +++ b/config/win_config.go @@ -0,0 +1,5 @@ +package config + +import "math/big" + +var ExpectedLeadersPerEpoch = big.NewInt(2) diff --git a/consensus/consensus.go b/consensus/consensus.go index 4d94982..1490604 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -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) } diff --git a/consensus/consensus_round_pool.go b/consensus/consensus_round_pool.go new file mode 100644 index 0000000..54e172c --- /dev/null +++ b/consensus/consensus_round_pool.go @@ -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 +//} diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 6ca65cd..24e84fc 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -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 +} diff --git a/consensus/dispute_manager.go b/consensus/dispute_manager.go index f967589..28e3ba6 100644 --- a/consensus/dispute_manager.go +++ b/consensus/dispute_manager.go @@ -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 diff --git a/consensus/miner.go b/consensus/miner.go deleted file mode 100644 index c7247bf..0000000 --- a/consensus/miner.go +++ /dev/null @@ -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 -} diff --git a/consensus/msg_log.go b/consensus/msg_log.go index 0bd8011..3276a0f 100644 --- a/consensus/msg_log.go +++ b/consensus/msg_log.go @@ -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) + } } } diff --git a/consensus/policy/policy.go b/consensus/policy/policy.go new file mode 100644 index 0000000..f5b5cf2 --- /dev/null +++ b/consensus/policy/policy.go @@ -0,0 +1,7 @@ +package policy + +const ( + BlockMaxTransactionCount = 100 + MaxBlockCountForRetrieving = 500 // we do it just like in Bitcoin + MaxTransactionCountForRetrieving = 50000 +) diff --git a/consensus/types/consensus_message.go b/consensus/types/consensus_message.go new file mode 100644 index 0000000..7365378 --- /dev/null +++ b/consensus/types/consensus_message.go @@ -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 +} diff --git a/consensus/types/message.go b/consensus/types/message.go index b2995e9..33e8918 100644 --- a/consensus/types/message.go +++ b/consensus/types/message.go @@ -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 diff --git a/consensus/utils.go b/consensus/utils.go index d1813a0..88a1ded 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -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 } diff --git a/consensus/validation/filecoin/filecoin.go b/consensus/validation/filecoin/filecoin.go index 076f43d..06393cf 100644 --- a/consensus/validation/filecoin/filecoin.go +++ b/consensus/validation/filecoin/filecoin.go @@ -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, }) } diff --git a/consensus/validation/payload_validator.go b/consensus/validation/payload_validator.go index 0eed568..145d35c 100644 --- a/consensus/validation/payload_validator.go +++ b/consensus/validation/payload_validator.go @@ -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 diff --git a/consensus/validation/solana/solana.go b/consensus/validation/solana/solana.go new file mode 100644 index 0000000..96592a0 --- /dev/null +++ b/consensus/validation/solana/solana.go @@ -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, + }) +} diff --git a/consensus/validation/utils.go b/consensus/validation/utils.go new file mode 100644 index 0000000..44181fc --- /dev/null +++ b/consensus/validation/utils.go @@ -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 +} diff --git a/drand/drand_test.go b/drand/drand_test.go deleted file mode 100644 index 1db4fb6..0000000 --- a/drand/drand_test.go +++ /dev/null @@ -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") -} diff --git a/eth-contracts/.gitignore b/eth-contracts/.gitignore index 4a31fd5..e6f4188 100644 --- a/eth-contracts/.gitignore +++ b/eth-contracts/.gitignore @@ -3,4 +3,5 @@ artifacts cache dist coverage -coverage.json \ No newline at end of file +coverage.json +scripts/private diff --git a/eth-contracts/contracts/DioneOracle.sol b/eth-contracts/contracts/DioneOracle.sol index 067d73d..3a1ab79 100644 --- a/eth-contracts/contracts/DioneOracle.sol +++ b/eth-contracts/contracts/DioneOracle.sol @@ -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; diff --git a/eth-contracts/hardhat.config.ts b/eth-contracts/hardhat.config.ts index 66a5258..49d745b 100644 --- a/eth-contracts/hardhat.config.ts +++ b/eth-contracts/hardhat.config.ts @@ -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 } }; \ No newline at end of file diff --git a/eth-contracts/hardhat.tasks.ts b/eth-contracts/hardhat.tasks.ts new file mode 100644 index 0000000..7b4b0ff --- /dev/null +++ b/eth-contracts/hardhat.tasks.ts @@ -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) + }) \ No newline at end of file diff --git a/eth-contracts/package-lock.json b/eth-contracts/package-lock.json index 751e22d..1069a40 100644 --- a/eth-contracts/package-lock.json +++ b/eth-contracts/package-lock.json @@ -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", diff --git a/eth-contracts/package.json b/eth-contracts/package.json index 312ea78..8055274 100644 --- a/eth-contracts/package.json +++ b/eth-contracts/package.json @@ -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", diff --git a/eth-contracts/test/DioneOracle.ts b/eth-contracts/test/DioneOracle.ts index 483ab4b..e370383 100644 --- a/eth-contracts/test/DioneOracle.ts +++ b/eth-contracts/test/DioneOracle.ts @@ -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 () { diff --git a/eth-contracts/tsconfig.json b/eth-contracts/tsconfig.json index d8b1055..421550b 100644 --- a/eth-contracts/tsconfig.json +++ b/eth-contracts/tsconfig.json @@ -4,7 +4,8 @@ "module": "commonjs", "strict": true, "esModuleInterop": true, - "outDir": "dist" + "outDir": "dist", + "resolveJsonModule": true }, "include": ["./scripts", "./test", "./common"], "files": ["./hardhat.config.ts"] diff --git a/ethclient/stake.go b/ethclient/stake.go index c7d36d8..a3ac145 100644 --- a/ethclient/stake.go +++ b/ethclient/stake.go @@ -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 } diff --git a/go.mod b/go.mod index a809fa9..4b954af 100644 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index 5bf985f..bf32893 100644 --- a/go.sum +++ b/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= diff --git a/node/flags.go b/node/flags.go new file mode 100644 index 0000000..dd4a760 --- /dev/null +++ b/node/flags.go @@ -0,0 +1,6 @@ +package node + +type AppFlags struct { + ConfigPath string + Verbose bool +} diff --git a/node/network_service.go b/node/network_service.go new file mode 100644 index 0000000..ff3fc04 --- /dev/null +++ b/node/network_service.go @@ -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 +} diff --git a/node/node.go b/node/node.go index f429497..8526b2a 100644 --- a/node/node.go +++ b/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() } diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go new file mode 100644 index 0000000..73ce43c --- /dev/null +++ b/node/node_dep_providers.go @@ -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") + } +} diff --git a/node/wire/get_mempool_tx.go b/node/wire/get_mempool_tx.go new file mode 100644 index 0000000..b9881fb --- /dev/null +++ b/node/wire/get_mempool_tx.go @@ -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 +} diff --git a/node/wire/get_range_of_blocks.go b/node/wire/get_range_of_blocks.go new file mode 100644 index 0000000..2c8321d --- /dev/null +++ b/node/wire/get_range_of_blocks.go @@ -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 +} diff --git a/node/wire/inv.go b/node/wire/inv.go new file mode 100644 index 0000000..b903ec5 --- /dev/null +++ b/node/wire/inv.go @@ -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 +} diff --git a/node/wire/last_block_height.go b/node/wire/last_block_height.go new file mode 100644 index 0000000..eb3f2e3 --- /dev/null +++ b/node/wire/last_block_height.go @@ -0,0 +1,5 @@ +package wire + +type LastBlockHeightReply struct { + Height uint64 +} diff --git a/pubsub/handler.go b/pubsub/handler.go deleted file mode 100644 index d53ef51..0000000 --- a/pubsub/handler.go +++ /dev/null @@ -1,7 +0,0 @@ -package pubsub - -import ( - "github.com/Secured-Finance/dione/consensus/types" -) - -type Handler func(message *types.Message) diff --git a/pubsub/message.go b/pubsub/message.go new file mode 100644 index 0000000..5537d51 --- /dev/null +++ b/pubsub/message.go @@ -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 +} diff --git a/pubsub/pubsub_router.go b/pubsub/pubsub_router.go index 3d1e7f3..2ff7fda 100644 --- a/pubsub/pubsub_router.go +++ b/pubsub/pubsub_router.go @@ -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 diff --git a/rpc/filecoin/filecoin.go b/rpc/filecoin/filecoin.go index ace13b1..a9ec9e5 100644 --- a/rpc/filecoin/filecoin.go +++ b/rpc/filecoin/filecoin.go @@ -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 } diff --git a/store/stake.go b/store/stake.go deleted file mode 100644 index f4addd6..0000000 --- a/store/stake.go +++ /dev/null @@ -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), - ) -} diff --git a/store/store.go b/store/store.go index 84d7ac0..3f6cf44 100644 --- a/store/store.go +++ b/store/store.go @@ -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; diff --git a/types/beacon.go b/types/beacon.go index 567b1b9..d3cc45f 100644 --- a/types/beacon.go +++ b/types/beacon.go @@ -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, } } diff --git a/types/bigint.go b/types/bigint.go deleted file mode 100644 index 642ecd7..0000000 --- a/types/bigint.go +++ /dev/null @@ -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 - } -} diff --git a/types/electionproof.go b/types/electionproof.go index 0627b4b..e95b2eb 100644 --- a/types/electionproof.go +++ b/types/electionproof.go @@ -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) diff --git a/types/electionproof_test.go b/types/electionproof_test.go deleted file mode 100644 index 47f684e..0000000 --- a/types/electionproof_test.go +++ /dev/null @@ -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) - } -} diff --git a/types/task.go b/types/task.go index cfd1a82..87ec8b8 100644 --- a/types/task.go +++ b/types/task.go @@ -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 diff --git a/types/ticket.go b/types/ticket.go deleted file mode 100644 index 68f7425..0000000 --- a/types/ticket.go +++ /dev/null @@ -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 -} diff --git a/wallet/wallet.go b/wallet/wallet.go index 33f6ce7..249488f 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -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()