From 0bc8371f84cdf1b721e09df2fe94c097e5d1fd48 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 12 May 2021 22:34:14 +0300 Subject: [PATCH 01/59] Implement block and transaction primitives --- go.mod | 1 + go.sum | 7 ++++ types/block.go | 79 ++++++++++++++++++++++++++++++++++++++++++++ types/transaction.go | 26 +++++++++++++++ wallet/wallet.go | 4 +-- 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 types/block.go create mode 100644 types/transaction.go diff --git a/go.mod b/go.mod index a809fa9..4c33a81 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/valyala/fasthttp v1.17.0 + github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a // indirect 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 diff --git a/go.sum b/go.sum index 5bf985f..aaf874f 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= @@ -1708,6 +1709,10 @@ 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.0 h1:DsF1xMzj5rK3pSQM6mPv8jlyJyHXhFxpnA2bwEjMMBY= +github.com/wealdtech/go-merkletree v1.0.0/go.mod h1:cdil512d/8ZC7Kx3bfrDvGMQXB25NTKbsm0rFrmDax4= +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= @@ -1842,6 +1847,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= @@ -2005,6 +2011,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= diff --git a/types/block.go b/types/block.go new file mode 100644 index 0000000..60e95a1 --- /dev/null +++ b/types/block.go @@ -0,0 +1,79 @@ +package types + +import ( + "time" + + "github.com/wealdtech/go-merkletree" + "github.com/wealdtech/go-merkletree/keccak256" + + "github.com/Secured-Finance/dione/wallet" + "github.com/libp2p/go-libp2p-core/peer" +) + +type Block struct { + Header *BlockHeader + Data []*Transaction +} + +type BlockHeader struct { + Timestamp int64 + SeqNum uint64 + Hash []byte + LastHash []byte + Proposer peer.ID + Signature []byte +} + +func GenesisBlock() *Block { + return &Block{ + Header: &BlockHeader{ + Timestamp: 1620845070, + SeqNum: 0, + Hash: []byte("DIMICANDUM"), + }, + Data: []*Transaction{}, + } +} + +func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *wallet.LocalWallet) (*Block, error) { + timestamp := time.Now().Unix() + proposer, err := wallet.GetDefault() + if err != nil { + return nil, err + } + + // extract hashes from transactions + var txHashes [][]byte + for _, tx := range txs { + txHashes = append(txHashes, tx.Hash) + } + txHashes = append(txHashes, lastBlockHeader.Hash) + + tree, err := merkletree.NewUsing(txHashes, keccak256.New(), false) + if err != nil { + return nil, err + } + + // fetch merkle tree root hash (block hash) + blockHash := tree.Root() + + // sign this block hash + s, err := wallet.Sign(proposer, blockHash) + if err != nil { + return nil, err + } + + block := &Block{ + Header: &BlockHeader{ + Timestamp: timestamp, + SeqNum: lastBlockHeader.SeqNum + 1, + Proposer: proposer, + Signature: s.Data, + Hash: blockHash, + LastHash: lastBlockHeader.Hash, + }, + Data: txs, + } + + return block, nil +} diff --git a/types/transaction.go b/types/transaction.go new file mode 100644 index 0000000..efa79b7 --- /dev/null +++ b/types/transaction.go @@ -0,0 +1,26 @@ +package types + +import ( + "encoding/hex" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/crypto" +) + +type Transaction struct { + Hash []byte + Timestamp int64 + Data []byte +} + +func CreateTransaction(data []byte) *Transaction { + timestamp := time.Now().Unix() + encodedData := hex.EncodeToString(data) + hash := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", timestamp, encodedData))) + return &Transaction{ + Hash: hash, + Timestamp: timestamp, + Data: data, + } +} 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() From 7e3fca5de59884f1ee43faa37e97be35aa894a25 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 13 May 2021 14:49:38 +0300 Subject: [PATCH 02/59] Refactor cache package to be more general purpose component --- cache/cache.go | 11 ++++++ cache/event_cache_interface.go | 9 ----- cache/event_log_cache.go | 50 --------------------------- cache/event_redis_cache.go | 58 -------------------------------- cache/inmemory_cache.go | 50 +++++++++++++++++++++++++++ cache/redis_cache.go | 58 ++++++++++++++++++++++++++++++++ consensus/consensus.go | 6 ++-- consensus/consensus_validator.go | 19 ++++++----- go.mod | 2 +- go.sum | 4 --- node/node.go | 18 +++++----- 11 files changed, 143 insertions(+), 142 deletions(-) create mode 100644 cache/cache.go delete mode 100644 cache/event_cache_interface.go delete mode 100644 cache/event_log_cache.go delete mode 100644 cache/event_redis_cache.go create mode 100644 cache/inmemory_cache.go create mode 100644 cache/redis_cache.go diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..0783f34 --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,11 @@ +package cache + +import "errors" + +var ErrNilValue = errors.New("value is empty") + +type Cache interface { + Store(key string, value interface{}) error + Get(key string, value interface{}) error + Delete(key string) +} 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..80b84fd --- /dev/null +++ b/cache/inmemory_cache.go @@ -0,0 +1,50 @@ +package cache + +import ( + "github.com/VictoriaMetrics/fastcache" + "github.com/fxamacker/cbor/v2" +) + +const ( + // DefaultInMemoryCacheCapacity is maximal in-memory cache size in bytes + DefaultInMemoryCacheCapacity = 32000000 +) + +type InMemoryCache struct { + cache *fastcache.Cache +} + +func NewInMemoryCache() *InMemoryCache { + return &InMemoryCache{ + cache: fastcache.New(DefaultInMemoryCacheCapacity), + } +} + +func (imc *InMemoryCache) Store(key string, value interface{}) error { + mRes, err := cbor.Marshal(value) + if err != nil { + return err + } + + imc.cache.SetBig([]byte(key), mRes) + + return nil +} + +func (imc *InMemoryCache) Get(key string, v interface{}) error { + data := make([]byte, 0) + imc.cache.GetBig(data, []byte(key)) + if len(data) == 0 { + return ErrNilValue + } + err := cbor.Unmarshal(data, v) + if err != nil { + return err + } + + return nil +} + +func (imc *InMemoryCache) Delete(key string) { + imc.cache.Del([]byte(key)) +} diff --git a/cache/redis_cache.go b/cache/redis_cache.go new file mode 100644 index 0000000..70b9f98 --- /dev/null +++ b/cache/redis_cache.go @@ -0,0 +1,58 @@ +package cache + +import ( + "context" + + "github.com/Secured-Finance/dione/config" + "github.com/fxamacker/cbor/v2" + "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 { + mRes, err := cbor.Marshal(value) + if err != nil { + return err + } + + rc.Client.Set(rc.ctx, key, mRes, 0) + + return nil +} + +func (rc *RedisCache) Get(key string, value interface{}) error { + data, err := rc.Client.Get(rc.ctx, key).Bytes() + if err != nil { + if err == redis.Nil { + return ErrNilValue + } + return err + } + err = cbor.Unmarshal(data, &value) + if err != nil { + return err + } + + return nil +} + +func (rc *RedisCache) Delete(key string) { + rc.Client.Del(rc.ctx, key) +} diff --git a/consensus/consensus.go b/consensus/consensus.go index 4d94982..d63e0f6 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -24,7 +24,7 @@ type PBFTConsensusManager struct { consensusMap map[string]*Consensus ethereumClient *ethclient.EthereumClient miner *Miner - eventCache cache.EventCache + cache cache.Cache } type Consensus struct { @@ -34,7 +34,7 @@ type Consensus struct { Task *types2.DioneTask } -func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey []byte, ethereumClient *ethclient.EthereumClient, miner *Miner, evc cache.EventCache) *PBFTConsensusManager { +func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey []byte, ethereumClient *ethclient.EthereumClient, miner *Miner, evc cache.Cache) *PBFTConsensusManager { pcm := &PBFTConsensusManager{} pcm.psb = psb pcm.miner = miner @@ -43,7 +43,7 @@ func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey pcm.minApprovals = minApprovals pcm.privKey = privKey pcm.ethereumClient = ethereumClient - pcm.eventCache = evc + pcm.cache = evc pcm.consensusMap = map[string]*Consensus{} pcm.psb.Hook(types.MessageTypePrePrepare, pcm.handlePrePrepare) pcm.psb.Hook(types.MessageTypePrepare, pcm.handlePrepare) diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 6ca65cd..4c37be8 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -4,6 +4,7 @@ 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/contracts/dioneOracle" "github.com/Secured-Finance/dione/types" "github.com/ethereum/go-ethereum/common" "github.com/filecoin-project/go-state-types/crypto" @@ -12,14 +13,14 @@ import ( type ConsensusValidator struct { validationFuncMap map[types2.MessageType]func(msg types2.Message) bool - eventCache cache.EventCache + cache cache.Cache miner *Miner } -func NewConsensusValidator(ec cache.EventCache, miner *Miner) *ConsensusValidator { +func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { cv := &ConsensusValidator{ - eventCache: ec, - miner: miner, + cache: ec, + miner: miner, } cv.validationFuncMap = map[types2.MessageType]func(msg types2.Message) bool{ @@ -35,17 +36,19 @@ func NewConsensusValidator(ec cache.EventCache, miner *Miner) *ConsensusValidato } ///////////////////////////////// - // === verify if request exists in event log cache === - requestEvent, err := cv.eventCache.GetOracleRequestEvent("request_" + consensusMsg.Task.RequestID) + // === verify if request exists in cache === + var requestEvent *dioneOracle.DioneOracleNewOracleRequest + err = cv.cache.Get("request_"+consensusMsg.Task.RequestID, &requestEvent) if err != nil { - logrus.Errorf("the incoming request task event doesn't exist in the EVC, or is broken: %v", err) + logrus.Errorf("the request doesn't exist in the cache or has been failed to decode: %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!") + logrus.Errorf("the incoming task and cached request requestEvent don't match!") return false } ///////////////////////////////// diff --git a/go.mod b/go.mod index 4c33a81..a677663 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/valyala/fasthttp v1.17.0 - github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a // indirect + 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 diff --git a/go.sum b/go.sum index aaf874f..b86294b 100644 --- a/go.sum +++ b/go.sum @@ -1013,8 +1013,6 @@ github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX 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.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= github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= @@ -1709,8 +1707,6 @@ 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.0 h1:DsF1xMzj5rK3pSQM6mPv8jlyJyHXhFxpnA2bwEjMMBY= -github.com/wealdtech/go-merkletree v1.0.0/go.mod h1:cdil512d/8ZC7Kx3bfrDvGMQXB25NTKbsm0rFrmDax4= 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= diff --git a/node/node.go b/node/node.go index f429497..94ecbe2 100644 --- a/node/node.go +++ b/node/node.go @@ -64,7 +64,7 @@ type Node struct { Miner *consensus.Miner Beacon beacon.BeaconNetworks Wallet *wallet.LocalWallet - EventCache cache.EventCache + Cache cache.Cache DisputeManager *consensus.DisputeManager } @@ -130,7 +130,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim // initialize event log cache subsystem eventCache := provideEventCache(config) - n.EventCache = eventCache + n.Cache = eventCache logrus.Info("Event cache subsystem has initialized!") // initialize consensus subsystem @@ -225,7 +225,7 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { select { case event := <-eventChan: { - err := n.EventCache.Store("request_"+event.ReqID.String(), event) + err := n.Cache.Store("request_"+event.ReqID.String(), event) if err != nil { logrus.Errorf("Failed to store new request event to event log cache: %v", err) } @@ -255,15 +255,15 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { }() } -func provideEventCache(config *config.Config) cache.EventCache { - var backend cache.EventCache +func provideEventCache(config *config.Config) cache.Cache { + var backend cache.Cache switch config.CacheType { case "in-memory": - backend = cache.NewEventLogCache() + backend = cache.NewInMemoryCache() case "redis": - backend = cache.NewEventRedisCache(config) + backend = cache.NewRedisCache(config) default: - backend = cache.NewEventLogCache() + backend = cache.NewInMemoryCache() } return backend } @@ -332,7 +332,7 @@ func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub2.PubSub 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 { +func provideConsensusManager(psb *pubsub2.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey []byte, minApprovals int, evc cache.Cache) *consensus.PBFTConsensusManager { return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner, evc) } From 59aa9f5889bf540eb43189ba96f16dcefb14654b Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 14 May 2021 21:21:13 +0300 Subject: [PATCH 03/59] Add support of deadline (TTL) for values in Cache --- cache/cache.go | 6 +++++- cache/inmemory_cache.go | 6 ++++++ cache/redis_cache.go | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cache/cache.go b/cache/cache.go index 0783f34..bf23a26 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -1,11 +1,15 @@ package cache -import "errors" +import ( + "errors" + "time" +) var ErrNilValue = errors.New("value is empty") 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) } diff --git a/cache/inmemory_cache.go b/cache/inmemory_cache.go index 80b84fd..c036275 100644 --- a/cache/inmemory_cache.go +++ b/cache/inmemory_cache.go @@ -1,6 +1,8 @@ package cache import ( + "time" + "github.com/VictoriaMetrics/fastcache" "github.com/fxamacker/cbor/v2" ) @@ -31,6 +33,10 @@ func (imc *InMemoryCache) Store(key string, value interface{}) error { return nil } +func (imc *InMemoryCache) StoreWithTTL(key string, value interface{}, ttl time.Duration) error { + return imc.Store(key, value) // fastcache doesn't support ttl for values +} + func (imc *InMemoryCache) Get(key string, v interface{}) error { data := make([]byte, 0) imc.cache.GetBig(data, []byte(key)) diff --git a/cache/redis_cache.go b/cache/redis_cache.go index 70b9f98..0788dbe 100644 --- a/cache/redis_cache.go +++ b/cache/redis_cache.go @@ -2,6 +2,7 @@ package cache import ( "context" + "time" "github.com/Secured-Finance/dione/config" "github.com/fxamacker/cbor/v2" @@ -37,6 +38,17 @@ func (rc *RedisCache) Store(key string, value interface{}) error { return nil } +func (rc *RedisCache) StoreWithTTL(key string, value interface{}, ttl time.Duration) error { + mRes, err := cbor.Marshal(value) + if err != nil { + return err + } + + rc.Client.Set(rc.ctx, key, mRes, ttl) + + return nil +} + func (rc *RedisCache) Get(key string, value interface{}) error { data, err := rc.Client.Get(rc.ctx, key).Bytes() if err != nil { From d9a0fa91941b7f9ec64f30f7a39fad9b8f74852d Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 14 May 2021 22:42:22 +0300 Subject: [PATCH 04/59] Change type of timestamp in transaction --- types/transaction.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/transaction.go b/types/transaction.go index efa79b7..463eac2 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -10,12 +10,12 @@ import ( type Transaction struct { Hash []byte - Timestamp int64 + Timestamp time.Time Data []byte } func CreateTransaction(data []byte) *Transaction { - timestamp := time.Now().Unix() + timestamp := time.Now() encodedData := hex.EncodeToString(data) hash := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", timestamp, encodedData))) return &Transaction{ From 7f85330953dc61c60200c03e8cdb177508eef548 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 14 May 2021 23:32:39 +0300 Subject: [PATCH 05/59] Implement basic mempool --- consensus/policy/policy.go | 5 ++ pool/mempool.go | 93 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 consensus/policy/policy.go create mode 100644 pool/mempool.go diff --git a/consensus/policy/policy.go b/consensus/policy/policy.go new file mode 100644 index 0000000..28e96f9 --- /dev/null +++ b/consensus/policy/policy.go @@ -0,0 +1,5 @@ +package policy + +const ( + BlockMaxTransactionCount = 100 +) diff --git a/pool/mempool.go b/pool/mempool.go new file mode 100644 index 0000000..a5c8f47 --- /dev/null +++ b/pool/mempool.go @@ -0,0 +1,93 @@ +package pool + +import ( + "encoding/hex" + "sort" + "sync" + "time" + + "github.com/Secured-Finance/dione/consensus/policy" + + "github.com/Secured-Finance/dione/types" + + "github.com/Secured-Finance/dione/cache" +) + +const ( + DefaultTxTTL = 10 * time.Minute + DefaultTxPrefix = "tx_" +) + +type Mempool struct { + m sync.RWMutex + cache cache.Cache + txDescriptors []string // list of txs in cache +} + +func NewMempool(c cache.Cache) (*Mempool, error) { + mp := &Mempool{ + cache: c, + } + + var txDesc []string + err := c.Get("tx_list", &txDesc) + if err != nil || err != cache.ErrNilValue { + return nil, err + } + mp.txDescriptors = txDesc + + return mp, nil +} + +func (mp *Mempool) StoreTx(tx *types.Transaction) error { + mp.m.Lock() + defer mp.m.Unlock() + + hashStr := hex.EncodeToString(tx.Hash) + err := mp.cache.StoreWithTTL(DefaultTxPrefix+hashStr, tx, DefaultTxTTL) + mp.txDescriptors = append(mp.txDescriptors, hashStr) + mp.cache.Store("tx_list", mp.txDescriptors) // update tx list in cache + return err +} + +func (mp *Mempool) GetTxsForNewBlock() []*types.Transaction { + mp.m.Lock() + defer mp.m.Unlock() + + var txForBlock []*types.Transaction + var allTxs []*types.Transaction + + for i, v := range mp.txDescriptors { + var tx types.Transaction + err := mp.cache.Get(DefaultTxPrefix+v, &tx) + if err != nil { + if err == cache.ErrNilValue { + // descriptor is broken + // delete it and update list + mp.txDescriptors = removeItemFromStringSlice(mp.txDescriptors, i) + mp.cache.Store("tx_list", mp.txDescriptors) // update tx list in cache + } + continue + } + allTxs = append(allTxs, &tx) + } + 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 removeItemFromStringSlice(s []string, i int) []string { + s[len(s)-1], s[i] = s[i], s[len(s)-1] + return s[:len(s)-1] +} From 2cd62d00799f0f5aa4ac20c4d04d641922329513 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 14 May 2021 23:32:49 +0300 Subject: [PATCH 06/59] Implement basic blockpool --- go.mod | 1 + go.sum | 2 + pool/blockpool.go | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 pool/blockpool.go diff --git a/go.mod b/go.mod index a677663..a5e902f 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/jmoiron/sqlx v1.2.0 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/klauspost/cpuid/v2 v2.0.6 // indirect + github.com/ledgerwatch/lmdb-go v1.17.8 // indirect github.com/libp2p/go-libp2p v0.12.0 github.com/libp2p/go-libp2p-core v0.7.0 github.com/libp2p/go-libp2p-pubsub v0.4.2-0.20210212194758-6c1addf493eb diff --git a/go.sum b/go.sum index b86294b..985b752 100644 --- a/go.sum +++ b/go.sum @@ -909,6 +909,8 @@ 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/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 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 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= diff --git a/pool/blockpool.go b/pool/blockpool.go new file mode 100644 index 0000000..b2c8ba1 --- /dev/null +++ b/pool/blockpool.go @@ -0,0 +1,137 @@ +package pool + +import ( + "encoding/hex" + "errors" + + "github.com/Secured-Finance/dione/types" + "github.com/fxamacker/cbor/v2" + "github.com/ledgerwatch/lmdb-go/lmdb" +) + +const ( + DefaultBlockPrefix = "block_" + DefaultBlockHeaderPrefix = "header_" +) + +var ( + ErrBlockNotFound = errors.New("block isn't found") +) + +type BlockPool struct { + dbEnv *lmdb.Env + db lmdb.DBI +} + +func NewBlockPool(path string) (*BlockPool, error) { + pool := &BlockPool{} + + // configure lmdb env + env, err := lmdb.NewEnv() + if err != nil { + return nil, err + } + + err = env.SetMapSize(100 * 1024 * 1024 * 1024) // 100 GB + if err != nil { + return nil, err + } + + err = env.Open(path, 0, 0664) + if err != nil { + return nil, err + } + + pool.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 + } + + pool.db = dbi + + return pool, nil +} + +func (bp *BlockPool) StoreBlock(block *types.Block) error { + return bp.dbEnv.Update(func(txn *lmdb.Txn) error { + data, err := cbor.Marshal(block) + 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(bp.db, []byte(DefaultBlockPrefix+blockHash), data, 0) + if err != nil { + return err + } + err = txn.Put(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching + return err + }) +} + +func (bp *BlockPool) HasBlock(blockHash string) (bool, error) { + var blockExists bool + err := bp.dbEnv.View(func(txn *lmdb.Txn) error { + _, err := txn.Get(bp.db, []byte(DefaultBlockPrefix+blockHash)) // 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 (bp *BlockPool) FetchBlock(blockHash string) (*types.Block, error) { + var block types.Block + err := bp.dbEnv.View(func(txn *lmdb.Txn) error { + data, err := txn.Get(bp.db, []byte(DefaultBlockPrefix+blockHash)) + if err != nil { + if lmdb.IsNotFound(err) { + return ErrBlockNotFound + } + return err + } + err = cbor.Unmarshal(data, &block) + return err + }) + if err != nil { + return nil, err + } + return &block, nil +} + +func (bp *BlockPool) FetchBlockHeader(blockHash string) (*types.BlockHeader, error) { + var blockHeader types.BlockHeader + err := bp.dbEnv.View(func(txn *lmdb.Txn) error { + data, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash)) + 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 +} From 3a53295fd20b4f84137e1d859221000c91912334 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 14 May 2021 23:53:32 +0300 Subject: [PATCH 07/59] Update .gitignore --- eth-contracts/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 5e48c0d176503d81bc14715fd1ee824366922fe0 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 19 May 2021 23:54:36 +0300 Subject: [PATCH 08/59] Move blockchain-related code into separate package --- {pool => blockchain/pool}/blockpool.go | 13 +++++++------ {pool => blockchain/pool}/mempool.go | 14 +++++++------- {types => blockchain/types}/block.go | 0 {types => blockchain/types}/transaction.go | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) rename {pool => blockchain/pool}/blockpool.go (88%) rename {pool => blockchain/pool}/mempool.go (86%) rename {types => blockchain/types}/block.go (100%) rename {types => blockchain/types}/transaction.go (81%) diff --git a/pool/blockpool.go b/blockchain/pool/blockpool.go similarity index 88% rename from pool/blockpool.go rename to blockchain/pool/blockpool.go index b2c8ba1..f8938f1 100644 --- a/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -4,8 +4,9 @@ import ( "encoding/hex" "errors" - "github.com/Secured-Finance/dione/types" + types2 "github.com/Secured-Finance/dione/blockchain/types" "github.com/fxamacker/cbor/v2" + "github.com/ledgerwatch/lmdb-go/lmdb" ) @@ -58,7 +59,7 @@ func NewBlockPool(path string) (*BlockPool, error) { return pool, nil } -func (bp *BlockPool) StoreBlock(block *types.Block) error { +func (bp *BlockPool) StoreBlock(block *types2.Block) error { return bp.dbEnv.Update(func(txn *lmdb.Txn) error { data, err := cbor.Marshal(block) if err != nil { @@ -98,8 +99,8 @@ func (bp *BlockPool) HasBlock(blockHash string) (bool, error) { return blockExists, nil } -func (bp *BlockPool) FetchBlock(blockHash string) (*types.Block, error) { - var block types.Block +func (bp *BlockPool) FetchBlock(blockHash string) (*types2.Block, error) { + var block types2.Block err := bp.dbEnv.View(func(txn *lmdb.Txn) error { data, err := txn.Get(bp.db, []byte(DefaultBlockPrefix+blockHash)) if err != nil { @@ -117,8 +118,8 @@ func (bp *BlockPool) FetchBlock(blockHash string) (*types.Block, error) { return &block, nil } -func (bp *BlockPool) FetchBlockHeader(blockHash string) (*types.BlockHeader, error) { - var blockHeader types.BlockHeader +func (bp *BlockPool) FetchBlockHeader(blockHash string) (*types2.BlockHeader, error) { + var blockHeader types2.BlockHeader err := bp.dbEnv.View(func(txn *lmdb.Txn) error { data, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash)) if err != nil { diff --git a/pool/mempool.go b/blockchain/pool/mempool.go similarity index 86% rename from pool/mempool.go rename to blockchain/pool/mempool.go index a5c8f47..0d7fe5d 100644 --- a/pool/mempool.go +++ b/blockchain/pool/mempool.go @@ -6,9 +6,9 @@ import ( "sync" "time" - "github.com/Secured-Finance/dione/consensus/policy" + types2 "github.com/Secured-Finance/dione/blockchain/types" - "github.com/Secured-Finance/dione/types" + "github.com/Secured-Finance/dione/consensus/policy" "github.com/Secured-Finance/dione/cache" ) @@ -39,7 +39,7 @@ func NewMempool(c cache.Cache) (*Mempool, error) { return mp, nil } -func (mp *Mempool) StoreTx(tx *types.Transaction) error { +func (mp *Mempool) StoreTx(tx *types2.Transaction) error { mp.m.Lock() defer mp.m.Unlock() @@ -50,15 +50,15 @@ func (mp *Mempool) StoreTx(tx *types.Transaction) error { return err } -func (mp *Mempool) GetTxsForNewBlock() []*types.Transaction { +func (mp *Mempool) GetTxsForNewBlock() []*types2.Transaction { mp.m.Lock() defer mp.m.Unlock() - var txForBlock []*types.Transaction - var allTxs []*types.Transaction + var txForBlock []*types2.Transaction + var allTxs []*types2.Transaction for i, v := range mp.txDescriptors { - var tx types.Transaction + var tx types2.Transaction err := mp.cache.Get(DefaultTxPrefix+v, &tx) if err != nil { if err == cache.ErrNilValue { diff --git a/types/block.go b/blockchain/types/block.go similarity index 100% rename from types/block.go rename to blockchain/types/block.go diff --git a/types/transaction.go b/blockchain/types/transaction.go similarity index 81% rename from types/transaction.go rename to blockchain/types/transaction.go index 463eac2..6ea276c 100644 --- a/types/transaction.go +++ b/blockchain/types/transaction.go @@ -17,7 +17,7 @@ type Transaction struct { func CreateTransaction(data []byte) *Transaction { timestamp := time.Now() encodedData := hex.EncodeToString(data) - hash := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", timestamp, encodedData))) + hash := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", timestamp.Unix(), encodedData))) return &Transaction{ Hash: hash, Timestamp: timestamp, From 6d50f37a12908efaa575c1a7727c0209c8ff61aa Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 20 May 2021 00:53:58 +0300 Subject: [PATCH 09/59] Implement storing latest block hash in blockpool storage --- blockchain/pool/blockpool.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go index f8938f1..e9060aa 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -13,6 +13,7 @@ import ( const ( DefaultBlockPrefix = "block_" DefaultBlockHeaderPrefix = "header_" + LatestBlockKey = "latest_block" ) var ( @@ -59,6 +60,28 @@ func NewBlockPool(path string) (*BlockPool, error) { return pool, nil } +func (bp *BlockPool) SetLatestBlock(hash []byte) error { + return bp.dbEnv.Update(func(txn *lmdb.Txn) error { + return txn.Put(bp.db, []byte(LatestBlockKey), hash, 0) + }) +} + +func (bp *BlockPool) GetLatestBlock() ([]byte, error) { + var hash []byte + err := bp.dbEnv.View(func(txn *lmdb.Txn) error { + data, err := txn.Get(bp.db, []byte(LatestBlockKey)) + if err != nil { + if lmdb.IsNotFound(err) { + return nil + } + return err + } + hash = data + return nil + }) + return hash, err +} + func (bp *BlockPool) StoreBlock(block *types2.Block) error { return bp.dbEnv.Update(func(txn *lmdb.Txn) error { data, err := cbor.Marshal(block) From dde32e11dc383608f35181d8ce7c8d5318eddc03 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 25 May 2021 18:32:06 +0300 Subject: [PATCH 10/59] Implement indexes for blockpool database and auto maintaining of it --- blockchain/pool/blockpool.go | 138 ++++++++++++++++++++++++++--------- blockchain/pool/index.go | 96 ++++++++++++++++++++++++ blockchain/types/block.go | 6 +- 3 files changed, 203 insertions(+), 37 deletions(-) create mode 100644 blockchain/pool/index.go diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go index e9060aa..bb3bac7 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -1,6 +1,7 @@ package pool import ( + "encoding/binary" "encoding/hex" "errors" @@ -11,18 +12,22 @@ import ( ) const ( - DefaultBlockPrefix = "block_" + DefaultBlockDataPrefix = "blockdata_" DefaultBlockHeaderPrefix = "header_" - LatestBlockKey = "latest_block" + DefaultMetadataIndexName = "metadata" + LatestBlockHeightKey = "latest_block_height" ) var ( - ErrBlockNotFound = errors.New("block isn't found") + ErrBlockNotFound = errors.New("block isn't found") + ErrLatestHeightNil = errors.New("latest block height is nil") ) type BlockPool struct { - dbEnv *lmdb.Env - db lmdb.DBI + dbEnv *lmdb.Env + db lmdb.DBI + metadataIndex *Index + heightIndex *Index } func NewBlockPool(path string) (*BlockPool, error) { @@ -57,34 +62,33 @@ func NewBlockPool(path string) (*BlockPool, error) { pool.db = dbi + // create index instances + metadataIndex := NewIndex(DefaultMetadataIndexName, env, dbi) + heightIndex := NewIndex("height", env, dbi) + pool.metadataIndex = metadataIndex + pool.heightIndex = heightIndex + return pool, nil } -func (bp *BlockPool) SetLatestBlock(hash []byte) error { - return bp.dbEnv.Update(func(txn *lmdb.Txn) error { - return txn.Put(bp.db, []byte(LatestBlockKey), hash, 0) - }) +func (bp *BlockPool) SetLatestBlockHeight(height uint64) error { + return bp.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height) } -func (bp *BlockPool) GetLatestBlock() ([]byte, error) { - var hash []byte - err := bp.dbEnv.View(func(txn *lmdb.Txn) error { - data, err := txn.Get(bp.db, []byte(LatestBlockKey)) - if err != nil { - if lmdb.IsNotFound(err) { - return nil - } - return err +func (bp *BlockPool) GetLatestBlockHeight() (uint64, error) { + height, err := bp.metadataIndex.GetUint64([]byte(LatestBlockHeightKey)) + if err != nil { + if err == ErrIndexKeyNotFound { + return 0, ErrLatestHeightNil } - hash = data - return nil - }) - return hash, err + return 0, err + } + return height, nil } func (bp *BlockPool) StoreBlock(block *types2.Block) error { - return bp.dbEnv.Update(func(txn *lmdb.Txn) error { - data, err := cbor.Marshal(block) + err := bp.dbEnv.Update(func(txn *lmdb.Txn) error { + data, err := cbor.Marshal(block.Data) if err != nil { return err } @@ -93,19 +97,50 @@ func (bp *BlockPool) StoreBlock(block *types2.Block) error { return err } blockHash := hex.EncodeToString(block.Header.Hash) - err = txn.Put(bp.db, []byte(DefaultBlockPrefix+blockHash), data, 0) + err = txn.Put(bp.db, []byte(DefaultBlockDataPrefix+blockHash), data, 0) if err != nil { return err } err = txn.Put(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching return err }) + if err != nil { + return err + } + + // update index "height -> block hash" + var heightBytes []byte + binary.LittleEndian.PutUint64(heightBytes, block.Header.Height) + err = bp.heightIndex.PutBytes(heightBytes, block.Header.Hash) + if err != nil { + return err + } + + // update latest block height + height, err := bp.GetLatestBlockHeight() + if err != nil && err != ErrLatestHeightNil { + return err + } + + if err == ErrLatestHeightNil { + if err = bp.SetLatestBlockHeight(block.Header.Height); err != nil { + return err + } + } else { + if block.Header.Height > height { + if err = bp.SetLatestBlockHeight(block.Header.Height); err != nil { + return err + } + } + } + return nil } -func (bp *BlockPool) HasBlock(blockHash string) (bool, error) { +func (bp *BlockPool) HasBlock(blockHash []byte) (bool, error) { var blockExists bool err := bp.dbEnv.View(func(txn *lmdb.Txn) error { - _, err := txn.Get(bp.db, []byte(DefaultBlockPrefix+blockHash)) // try to fetch block header + h := hex.EncodeToString(blockHash) + _, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+h)) // try to fetch block header if err != nil { if lmdb.IsNotFound(err) { blockExists = false @@ -122,29 +157,31 @@ func (bp *BlockPool) HasBlock(blockHash string) (bool, error) { return blockExists, nil } -func (bp *BlockPool) FetchBlock(blockHash string) (*types2.Block, error) { - var block types2.Block +func (bp *BlockPool) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) { + var data []*types2.Transaction err := bp.dbEnv.View(func(txn *lmdb.Txn) error { - data, err := txn.Get(bp.db, []byte(DefaultBlockPrefix+blockHash)) + h := hex.EncodeToString(blockHash) + blockData, err := txn.Get(bp.db, []byte(DefaultBlockDataPrefix+h)) if err != nil { if lmdb.IsNotFound(err) { return ErrBlockNotFound } return err } - err = cbor.Unmarshal(data, &block) + err = cbor.Unmarshal(blockData, data) return err }) if err != nil { return nil, err } - return &block, nil + return data, nil } -func (bp *BlockPool) FetchBlockHeader(blockHash string) (*types2.BlockHeader, error) { +func (bp *BlockPool) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) { var blockHeader types2.BlockHeader err := bp.dbEnv.View(func(txn *lmdb.Txn) error { - data, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash)) + h := hex.EncodeToString(blockHash) + data, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+h)) if err != nil { if lmdb.IsNotFound(err) { return ErrBlockNotFound @@ -159,3 +196,36 @@ func (bp *BlockPool) FetchBlockHeader(blockHash string) (*types2.BlockHeader, er } return &blockHeader, nil } + +func (bp *BlockPool) FetchBlock(blockHash []byte) (*types2.Block, error) { + var block types2.Block + header, err := bp.FetchBlockHeader(blockHash) + if err != nil { + return nil, err + } + block.Header = header + + data, err := bp.FetchBlockData(blockHash) + if err != nil { + return nil, err + } + block.Data = data + + return &block, nil +} + +func (bp *BlockPool) FetchBlockByHeight(height uint64) (*types2.Block, error) { + var heightBytes []byte + binary.LittleEndian.PutUint64(heightBytes, height) + blockHash, err := bp.heightIndex.GetBytes(heightBytes) + if err != nil { + if err == ErrIndexKeyNotFound { + return nil, ErrBlockNotFound + } + } + block, err := bp.FetchBlock(blockHash) + if err != nil { + return nil, err + } + return block, nil +} diff --git a/blockchain/pool/index.go b/blockchain/pool/index.go new file mode 100644 index 0000000..7d46f7f --- /dev/null +++ b/blockchain/pool/index.go @@ -0,0 +1,96 @@ +package pool + +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 { + var data []byte + 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/types/block.go b/blockchain/types/block.go index 60e95a1..99e7e0a 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -17,7 +17,7 @@ type Block struct { type BlockHeader struct { Timestamp int64 - SeqNum uint64 + Height uint64 Hash []byte LastHash []byte Proposer peer.ID @@ -28,7 +28,7 @@ func GenesisBlock() *Block { return &Block{ Header: &BlockHeader{ Timestamp: 1620845070, - SeqNum: 0, + Height: 0, Hash: []byte("DIMICANDUM"), }, Data: []*Transaction{}, @@ -66,7 +66,7 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *walle block := &Block{ Header: &BlockHeader{ Timestamp: timestamp, - SeqNum: lastBlockHeader.SeqNum + 1, + Height: lastBlockHeader.Height + 1, Proposer: proposer, Signature: s.Data, Hash: blockHash, From fb84ab8ad66b0f782f1e3a6772ce165bd7181c29 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 26 May 2021 23:57:54 +0300 Subject: [PATCH 11/59] Add merkle proof of last block hash to BlockHeader, add merkle proof of tx hash to Transaction --- blockchain/types/block.go | 31 +++++++++++++++++++------------ blockchain/types/transaction.go | 9 ++++++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/blockchain/types/block.go b/blockchain/types/block.go index 99e7e0a..c7e9495 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -16,12 +16,13 @@ type Block struct { } type BlockHeader struct { - Timestamp int64 - Height uint64 - Hash []byte - LastHash []byte - Proposer peer.ID - Signature []byte + Timestamp int64 + Height uint64 + Hash []byte + LastHash []byte + LastHashProof *merkletree.Proof + Proposer peer.ID + Signature []byte } func GenesisBlock() *Block { @@ -63,14 +64,20 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *walle return nil, err } + lastHashProof, err := tree.GenerateProof(lastBlockHeader.Hash, 0) + if err != nil { + return nil, err + } + block := &Block{ Header: &BlockHeader{ - Timestamp: timestamp, - Height: lastBlockHeader.Height + 1, - Proposer: proposer, - Signature: s.Data, - Hash: blockHash, - LastHash: lastBlockHeader.Hash, + Timestamp: timestamp, + Height: lastBlockHeader.Height + 1, + Proposer: proposer, + Signature: s.Data, + Hash: blockHash, + LastHash: lastBlockHeader.Hash, + LastHashProof: lastHashProof, }, Data: txs, } diff --git a/blockchain/types/transaction.go b/blockchain/types/transaction.go index 6ea276c..3be0e0d 100644 --- a/blockchain/types/transaction.go +++ b/blockchain/types/transaction.go @@ -5,13 +5,16 @@ import ( "fmt" "time" + "github.com/wealdtech/go-merkletree" + "github.com/ethereum/go-ethereum/crypto" ) type Transaction struct { - Hash []byte - Timestamp time.Time - Data []byte + Hash []byte + MerkleProof *merkletree.Proof // sets when transaction is added to block + Timestamp time.Time + Data []byte } func CreateTransaction(data []byte) *Transaction { From 155fe7bc7152fdd1c9620d48734eab4160c54add Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 26 May 2021 23:58:31 +0300 Subject: [PATCH 12/59] Implement fetching block header by height in blockpool --- blockchain/pool/blockpool.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go index bb3bac7..9313e7e 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -71,7 +71,7 @@ func NewBlockPool(path string) (*BlockPool, error) { return pool, nil } -func (bp *BlockPool) SetLatestBlockHeight(height uint64) error { +func (bp *BlockPool) setLatestBlockHeight(height uint64) error { return bp.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height) } @@ -123,12 +123,12 @@ func (bp *BlockPool) StoreBlock(block *types2.Block) error { } if err == ErrLatestHeightNil { - if err = bp.SetLatestBlockHeight(block.Header.Height); err != nil { + if err = bp.setLatestBlockHeight(block.Header.Height); err != nil { return err } } else { if block.Header.Height > height { - if err = bp.SetLatestBlockHeight(block.Header.Height); err != nil { + if err = bp.setLatestBlockHeight(block.Header.Height); err != nil { return err } } @@ -229,3 +229,19 @@ func (bp *BlockPool) FetchBlockByHeight(height uint64) (*types2.Block, error) { } return block, nil } + +func (bp *BlockPool) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) { + var heightBytes []byte + binary.LittleEndian.PutUint64(heightBytes, height) + blockHash, err := bp.heightIndex.GetBytes(heightBytes) + if err != nil { + if err == ErrIndexKeyNotFound { + return nil, ErrBlockNotFound + } + } + blockHeader, err := bp.FetchBlockHeader(blockHash) + if err != nil { + return nil, err + } + return blockHeader, nil +} From 1759ecbc8f3bc98f671f4a34129cb9fd99b1c030 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 26 May 2021 23:59:05 +0300 Subject: [PATCH 13/59] Add blockchain config to Config struct --- config/config.go | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/config/config.go b/config/config.go index 2c25fa9..8f51f52 100644 --- a/config/config.go +++ b/config/config.go @@ -7,18 +7,19 @@ 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"` } type EthereumConfig struct { @@ -39,8 +40,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 +53,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 +68,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, From b51a5ffe754d13d8d5408bed66e4ba7122d0ebd6 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 27 May 2021 00:06:46 +0300 Subject: [PATCH 14/59] Implement sync manager, node p2p network rpc service, initial sync algorithm --- blockchain/sync/sync_mgr.go | 173 +++++++++++++++++++++++++ go.mod | 7 +- go.sum | 39 ++++-- node/network_service.go | 56 +++++++++ node/node.go | 222 ++++++++++++--------------------- node/node_dep_providers.go | 176 ++++++++++++++++++++++++++ node/wire/get_blocks.go | 14 +++ node/wire/last_block_height.go | 6 + 8 files changed, 539 insertions(+), 154 deletions(-) create mode 100644 blockchain/sync/sync_mgr.go create mode 100644 node/network_service.go create mode 100644 node/node_dep_providers.go create mode 100644 node/wire/get_blocks.go create mode 100644 node/wire/last_block_height.go diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go new file mode 100644 index 0000000..91c2f8e --- /dev/null +++ b/blockchain/sync/sync_mgr.go @@ -0,0 +1,173 @@ +package sync + +import ( + "bytes" + "context" + "fmt" + "strings" + "sync" + + "github.com/wealdtech/go-merkletree/keccak256" + + "github.com/wealdtech/go-merkletree" + + "github.com/sirupsen/logrus" + + "github.com/Secured-Finance/dione/node" + + "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 { + Start() + + Stop() +} + +type syncManager struct { + blockPool *pool.BlockPool + wg sync.WaitGroup + ctx context.Context + ctxCancelFunc context.CancelFunc + initialSyncCompleted bool + bootstrapPeer peer.ID + rpcClient *gorpc.Client +} + +func NewSyncManager(bp *pool.BlockPool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID) SyncManager { + ctx, cancelFunc := context.WithCancel(context.Background()) + return &syncManager{ + blockPool: bp, + ctx: ctx, + ctxCancelFunc: cancelFunc, + initialSyncCompleted: false, + bootstrapPeer: bootstrapPeer, + rpcClient: p2pRPCClient, + } +} + +func (sm *syncManager) Start() { + sm.wg.Add(1) + + sm.doInitialSync() + go sm.syncLoop() +} + +func (sm *syncManager) Stop() { + sm.ctxCancelFunc() + sm.wg.Wait() +} + +func (sm *syncManager) doInitialSync() error { + if sm.initialSyncCompleted { + return nil + } + + ourLastHeight, err := sm.blockPool.GetLatestBlockHeight() + if err == pool.ErrLatestHeightNil { + gBlock := types2.GenesisBlock() + err = sm.blockPool.StoreBlock(gBlock) // commit genesis block + if err != nil { + return err + } + } + + var reply wire.LastBlockHeightReply + err = sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "LastBlockHeight", nil, &reply) + if err != nil { + return err + } + if reply.Error != nil { + return reply.Error + } + + 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 < node.MaxBlockCountForRetrieving { + addedVal = heightCount + } else { + addedVal = node.MaxBlockCountForRetrieving + } + heightCount -= addedVal + to += addedVal + var getBlocksReply wire.GetBlocksReply + arg := wire.GetBlocksArg{From: from, To: to} + err = sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "GetBlocks", 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) 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: %s", err.Error()) + } + 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 tx.MerkleProof == nil { + return fmt.Errorf("block transaction hasn't merkle proof") + } + txProofVerified, err := merkletree.VerifyProofUsing(tx.Hash, false, tx.MerkleProof, [][]byte{block.Header.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") + } + } + + 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) syncLoop() { + +} diff --git a/go.mod b/go.mod index a5e902f..6b84fa0 100644 --- a/go.mod +++ b/go.mod @@ -33,9 +33,10 @@ require ( github.com/jmoiron/sqlx v1.2.0 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/klauspost/cpuid/v2 v2.0.6 // indirect - github.com/ledgerwatch/lmdb-go v1.17.8 // indirect - github.com/libp2p/go-libp2p v0.12.0 - github.com/libp2p/go-libp2p-core v0.7.0 + github.com/ledgerwatch/lmdb-go v1.17.8 + github.com/libp2p/go-libp2p v0.13.0 + github.com/libp2p/go-libp2p-core v0.8.0 + github.com/libp2p/go-libp2p-gorpc v0.1.2 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 diff --git a/go.sum b/go.sum index 985b752..2ad6b53 100644 --- a/go.sum +++ b/go.sum @@ -952,8 +952,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= @@ -1013,8 +1014,9 @@ 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 h1:5K3mT+64qDTKbV3yTdbMCzJ7O6wbNsavAEb8iqBvBcI= +github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= @@ -1028,6 +1030,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.2 h1:jHL0F79uDVPNsflS9byf8Wk23MQ0G+r5nUnLChoUn8A= +github.com/libp2p/go-libp2p-gorpc v0.1.2/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= @@ -1048,8 +1052,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= @@ -1115,8 +1121,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= @@ -1124,8 +1131,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= @@ -1135,8 +1143,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= @@ -1146,8 +1155,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= @@ -1158,8 +1169,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= @@ -1211,8 +1223,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= @@ -1224,6 +1237,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= @@ -1683,11 +1698,13 @@ 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 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q= +github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc= 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 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4= +github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= 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= diff --git a/node/network_service.go b/node/network_service.go new file mode 100644 index 0000000..5e65b22 --- /dev/null +++ b/node/network_service.go @@ -0,0 +1,56 @@ +package node + +import ( + "context" + + "github.com/sirupsen/logrus" + + "github.com/Secured-Finance/dione/node/wire" + + "github.com/Secured-Finance/dione/blockchain/pool" +) + +const ( + MaxBlockCountForRetrieving = 500 // we do it just like in Bitcoin +) + +type NetworkService struct { + blockpool *pool.BlockPool +} + +func NewNetworkService(bp *pool.BlockPool) *NetworkService { + return &NetworkService{ + blockpool: bp, + } +} + +func (s *NetworkService) LastBlockHeight(ctx context.Context, arg interface{}, reply *wire.LastBlockHeightReply) { + height, err := s.blockpool.GetLatestBlockHeight() + if err != nil { + reply.Error = err + return + } + reply.Height = height +} + +func (s *NetworkService) GetBlocks(ctx context.Context, arg wire.GetBlocksArg, reply *wire.GetBlocksReply) { + if arg.From > arg.To { + errText := "incorrect arguments: from > to" + reply.Error = &errText + return + } + if arg.To-arg.From > MaxBlockCountForRetrieving { + errText := "incorrect arguments: count of block for retrieving is exceeded the limit" + reply.Error = &errText + return + } + for i := arg.From; i <= arg.To; i++ { + block, err := s.blockpool.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) + } +} diff --git a/node/node.go b/node/node.go index 94ecbe2..108ccd4 100644 --- a/node/node.go +++ b/node/node.go @@ -9,17 +9,15 @@ import ( "os" "time" - pex "github.com/Secured-Finance/go-libp2p-pex" + gorpc "github.com/libp2p/go-libp2p-gorpc" + + "github.com/Secured-Finance/dione/blockchain/pool" + + "github.com/Secured-Finance/dione/blockchain/sync" "github.com/Secured-Finance/dione/cache" "github.com/Secured-Finance/dione/consensus" - - pubsub "github.com/libp2p/go-libp2p-pubsub" - - "github.com/Secured-Finance/dione/drand" - - "github.com/ethereum/go-ethereum/common" - "github.com/libp2p/go-libp2p-core/peer" + pubsub2 "github.com/Secured-Finance/dione/pubsub" "github.com/libp2p/go-libp2p-core/discovery" @@ -30,8 +28,6 @@ import ( "github.com/Secured-Finance/dione/rpc/filecoin" - "github.com/Secured-Finance/dione/types" - "github.com/Secured-Finance/dione/wallet" "golang.org/x/xerrors" @@ -40,11 +36,8 @@ import ( "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/crypto" "github.com/libp2p/go-libp2p-core/host" - "github.com/multiformats/go-multiaddr" "github.com/sirupsen/logrus" ) @@ -66,6 +59,11 @@ type Node struct { Wallet *wallet.LocalWallet Cache cache.Cache DisputeManager *consensus.DisputeManager + BlockPool *pool.BlockPool + MemPool *pool.Mempool + SyncManager sync.SyncManager + NetworkService *NetworkService + NetworkRPCHost *gorpc.Server } func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTime time.Duration) (*Node, error) { @@ -74,12 +72,12 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim } // initialize libp2p host - lhost, err := provideLibp2pHost(n.Config, prvKey, pexDiscoveryUpdateTime) + lhost, err := provideLibp2pHost(n.Config, prvKey) if err != nil { logrus.Fatal(err) } n.Host = lhost - logrus.Info("Started up Libp2p host!") + logrus.Info("Libp2p host has been successfully initialized!") // initialize ethereum client ethClient, err := provideEthereumClient(n.Config) @@ -87,27 +85,33 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Fatal(err) } n.Ethereum = ethClient - logrus.Info("Started up Ethereum client!") + logrus.Info("Ethereum client has been successfully initialized!") // initialize blockchain rpc clients err = n.setupRPCClients() if err != nil { logrus.Fatal(err) } - logrus.Info("RPC clients has successfully configured!") + logrus.Info("RPC clients has been successfully configured!") // initialize pubsub subsystem psb := providePubsubRouter(lhost, n.Config) n.PubSubRouter = psb - logrus.Info("PubSub subsystem has initialized!") + logrus.Info("PubSub subsystem has been initialized!") + + // get list of bootstrap multiaddresses + baddrs, err := provideBootstrapAddrs(n.Config) + if err != nil { + logrus.Fatal(err) + } // initialize peer discovery - peerDiscovery, err := providePeerDiscovery(n.Config, lhost, pexDiscoveryUpdateTime) + peerDiscovery, err := providePeerDiscovery(baddrs, lhost, pexDiscoveryUpdateTime) if err != nil { logrus.Fatal(err) } n.PeerDiscovery = peerDiscovery - logrus.Info("Peer discovery subsystem has initialized!") + logrus.Info("Peer discovery subsystem has been initialized!") // get private key of libp2p host rawPrivKey, err := prvKey.Raw() @@ -121,20 +125,58 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Fatal(err) } n.Beacon = randomBeaconNetwork - logrus.Info("Random beacon subsystem has initialized!") + logrus.Info("Random beacon subsystem has been initialized!") + + // initialize event log cache subsystem + c := provideCache(config) + n.Cache = c + logrus.Info("Event cache subsystem has initialized!") + + // == initialize blockchain modules + + // initialize blockpool database + bp, err := provideBlockPool(n.Config) + if err != nil { + logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) + } + n.BlockPool = bp + logrus.Info("Block pool database has been successfully initialized!") + + // initialize mempool + mp, err := provideMemPool(c) + if err != nil { + logrus.Fatalf("Failed to initialize mempool: %s", err.Error()) + } + n.MemPool = mp + logrus.Info("Mempool has been successfully initialized!") + + ns := provideNetworkService(bp) + n.NetworkService = ns + rpcHost := provideNetworkRPCHost(lhost) + err = rpcHost.Register(ns) + if err != nil { + logrus.Fatal(err) + } + logrus.Info("Node p2p RPC network service has been successfully initialized!") + + // initialize libp2p-gorpc client + r := provideP2PRPCClient(lhost) + + // initialize sync manager + sm, err := provideSyncManager(bp, r, baddrs[0]) // FIXME here we just pick up first bootstrap in list + if err != nil { + logrus.Fatal(err) + } + n.SyncManager = sm + logrus.Info("Blockchain synchronization subsystem has been successfully 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.Cache = eventCache - logrus.Info("Event cache subsystem has initialized!") - // initialize consensus subsystem - cManager := provideConsensusManager(psb, miner, ethClient, rawPrivKey, n.Config.ConsensusMinApprovals, eventCache) + cManager := provideConsensusManager(psb, miner, ethClient, rawPrivKey, n.Config.ConsensusMinApprovals, c) n.ConsensusManager = cManager logrus.Info("Consensus subsystem has initialized!") @@ -147,17 +189,20 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Dispute subsystem has initialized!") // initialize internal eth wallet - wallet, err := provideWallet(n.Host.ID(), rawPrivKey) + w, err := provideWallet(n.Host.ID(), rawPrivKey) if err != nil { logrus.Fatal(err) } - n.Wallet = wallet + n.Wallet = w return n, nil } func (n *Node) Run(ctx context.Context) error { - n.runLibp2pAsync(ctx) + err := n.runLibp2pAsync(ctx) + if err != nil { + return err + } n.subscribeOnEthContractsAsync(ctx) for { @@ -166,8 +211,6 @@ func (n *Node) Run(ctx context.Context) error { return nil } } - - // return nil } func (n *Node) runLibp2pAsync(ctx context.Context) error { @@ -243,7 +286,7 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { logrus.Infof("Proposed new Dione task with ID: %s", event.ReqID.String()) err = n.ConsensusManager.Propose(*task) if err != nil { - logrus.Errorf("Failed to propose task: %w", err) + logrus.Errorf("Failed to propose task: %v", err) } } case <-ctx.Done(): @@ -255,64 +298,6 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { }() } -func provideEventCache(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(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){ @@ -328,53 +313,6 @@ func (n *Node) setupRPCClients() error { 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.Cache) *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") @@ -391,6 +329,7 @@ func Start() { var privateKey crypto.PrivKey if cfg.IsBootstrap { + // FIXME just a little hack if _, err := os.Stat(".bootstrap_privkey"); os.IsNotExist(err) { privateKey, err = generatePrivateKey() if err != nil { @@ -399,7 +338,10 @@ func Start() { f, _ := os.Create(".bootstrap_privkey") r, _ := privateKey.Raw() - f.Write(r) + _, err = f.Write(r) + if err != nil { + logrus.Fatal(err) + } } else { pkey, _ := ioutil.ReadFile(".bootstrap_privkey") privateKey, _ = crypto.UnmarshalEd25519PrivateKey(pkey) diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go new file mode 100644 index 0000000..e34b60b --- /dev/null +++ b/node/node_dep_providers.go @@ -0,0 +1,176 @@ +package node + +import ( + "context" + "fmt" + "time" + + "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/beacon" + "github.com/Secured-Finance/dione/cache" + "github.com/Secured-Finance/dione/config" + "github.com/Secured-Finance/dione/consensus" + "github.com/Secured-Finance/dione/drand" + "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/ethereum/go-ethereum/common" + "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" + pubsub2 "github.com/libp2p/go-libp2p-pubsub" + "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(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 *pubsub2.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 providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubRouter { + return pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap) +} + +func provideConsensusManager(psb *pubsub.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey []byte, minApprovals int, evc cache.Cache) *consensus.PBFTConsensusManager { + return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner, evc) +} + +func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) (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 provideNetworkRPCHost(h host.Host) *gorpc.Server { + return gorpc.NewServer(h, DioneProtocolID) +} + +func provideBootstrapAddrs(c *config.Config) ([]multiaddr.Multiaddr, error) { + if c.IsBootstrap { + return nil, nil + } + + var bootstrapMaddrs []multiaddr.Multiaddr + for _, a := range c.BootstrapNodes { + maddr, err := multiaddr.NewMultiaddr(a) + if err != nil { + return nil, xerrors.Errorf("invalid multiaddress of bootstrap node: %v", err) + } + bootstrapMaddrs = append(bootstrapMaddrs, maddr) + } + + return bootstrapMaddrs, nil +} + +func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host, pexDiscoveryUpdateTime time.Duration) (discovery.Discovery, error) { + pexDiscovery, err := pex.NewPEXDiscovery(h, baddrs, pexDiscoveryUpdateTime) + if err != nil { + return nil, xerrors.Errorf("failed to setup pex pexDiscovery: %v", err) + } + + return pexDiscovery, nil +} + +func provideBlockPool(config *config.Config) (*pool.BlockPool, error) { + return pool.NewBlockPool(config.Blockchain.DatabasePath) +} + +func provideMemPool(c cache.Cache) (*pool.Mempool, error) { + return pool.NewMempool(c) +} + +func provideSyncManager(bp *pool.BlockPool, r *gorpc.Client, bootstrap multiaddr.Multiaddr) (sync.SyncManager, error) { + addr, err := peer.AddrInfoFromP2pAddr(bootstrap) + if err != nil { + return nil, err + } + return sync.NewSyncManager(bp, r, addr.ID), nil +} + +func provideP2PRPCClient(h host.Host) *gorpc.Client { + return gorpc.NewClient(h, DioneProtocolID) +} + +func provideNetworkService(bp *pool.BlockPool) *NetworkService { + return NewNetworkService(bp) +} diff --git a/node/wire/get_blocks.go b/node/wire/get_blocks.go new file mode 100644 index 0000000..dda8cfe --- /dev/null +++ b/node/wire/get_blocks.go @@ -0,0 +1,14 @@ +package wire + +import "github.com/Secured-Finance/dione/blockchain/types" + +type GetBlocksArg struct { + From uint64 + To uint64 +} + +type GetBlocksReply struct { + Blocks []types.Block + FailedBlockHeights []uint64 // list of block heights the node was unable to retrieve + Error *string +} diff --git a/node/wire/last_block_height.go b/node/wire/last_block_height.go new file mode 100644 index 0000000..c7ceaac --- /dev/null +++ b/node/wire/last_block_height.go @@ -0,0 +1,6 @@ +package wire + +type LastBlockHeightReply struct { + Height uint64 + Error error +} From 43871c6141652ba0ee5f7f0fe5575e31c47c1806 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 27 May 2021 00:29:37 +0300 Subject: [PATCH 15/59] Add validation of tx hash when syncing --- blockchain/sync/sync_mgr.go | 3 +++ blockchain/types/transaction.go | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 91c2f8e..2a6cbef 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -158,6 +158,9 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) 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") + } } err = sm.blockPool.StoreBlock(&block) diff --git a/blockchain/types/transaction.go b/blockchain/types/transaction.go index 3be0e0d..abb4c6d 100644 --- a/blockchain/types/transaction.go +++ b/blockchain/types/transaction.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "encoding/hex" "fmt" "time" @@ -27,3 +28,11 @@ func CreateTransaction(data []byte) *Transaction { Data: data, } } + +func (tx *Transaction) ValidateHash() bool { + h := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", tx.Timestamp.Unix(), tx.Hash))) + if bytes.Compare(h, tx.Hash) != 0 { + return false + } + return true +} From 3366a71ac1b431a3c160442fac0f8f4663dcd68f Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 27 May 2021 22:30:11 +0300 Subject: [PATCH 16/59] Change backend for in-memory cache to patrickmn/go-cache --- cache/inmemory_cache.go | 35 +++++++++++++---------------------- go.mod | 2 +- go.sum | 2 ++ 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/cache/inmemory_cache.go b/cache/inmemory_cache.go index c036275..2dacd46 100644 --- a/cache/inmemory_cache.go +++ b/cache/inmemory_cache.go @@ -3,54 +3,45 @@ package cache import ( "time" - "github.com/VictoriaMetrics/fastcache" - "github.com/fxamacker/cbor/v2" + "github.com/patrickmn/go-cache" ) const ( - // DefaultInMemoryCacheCapacity is maximal in-memory cache size in bytes - DefaultInMemoryCacheCapacity = 32000000 + DefaultCacheExpiration = 5 * time.Minute + DefaultGCInterval = 10 * time.Minute ) type InMemoryCache struct { - cache *fastcache.Cache + cache *cache.Cache } func NewInMemoryCache() *InMemoryCache { return &InMemoryCache{ - cache: fastcache.New(DefaultInMemoryCacheCapacity), + cache: cache.New(DefaultCacheExpiration, DefaultGCInterval), } } func (imc *InMemoryCache) Store(key string, value interface{}) error { - mRes, err := cbor.Marshal(value) - if err != nil { - return err - } - - imc.cache.SetBig([]byte(key), mRes) + imc.cache.Set(key, value, cache.NoExpiration) return nil } func (imc *InMemoryCache) StoreWithTTL(key string, value interface{}, ttl time.Duration) error { - return imc.Store(key, value) // fastcache doesn't support ttl for values + imc.cache.Set(key, value, ttl) + return nil } -func (imc *InMemoryCache) Get(key string, v interface{}) error { - data := make([]byte, 0) - imc.cache.GetBig(data, []byte(key)) - if len(data) == 0 { +func (imc *InMemoryCache) Get(key string, value interface{}) error { + v, exists := imc.cache.Get(key) + if !exists { return ErrNilValue } - err := cbor.Unmarshal(data, v) - if err != nil { - return err - } + value = v return nil } func (imc *InMemoryCache) Delete(key string) { - imc.cache.Del([]byte(key)) + imc.cache.Delete(key) } diff --git a/go.mod b/go.mod index 6b84fa0..f84eebd 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.14 require ( github.com/Secured-Finance/go-libp2p-pex v1.1.0 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/cespare/cp v1.1.1 // indirect @@ -46,6 +45,7 @@ require ( github.com/multiformats/go-multiaddr v0.3.1 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 diff --git a/go.sum b/go.sum index 2ad6b53..1530895 100644 --- a/go.sum +++ b/go.sum @@ -1469,6 +1469,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= From 00b041fbb0247f229539134d040730bc954c6f0e Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 27 May 2021 22:30:52 +0300 Subject: [PATCH 17/59] Use gob encoding for struct marshalling in redis cache --- cache/cache.go | 2 +- cache/inmemory_cache.go | 2 +- cache/redis_cache.go | 39 ++++++++++++++++++++++++++------------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index bf23a26..1c155f8 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -5,7 +5,7 @@ import ( "time" ) -var ErrNilValue = errors.New("value is empty") +var ErrNotFound = errors.New("key doesn't exist in cache") type Cache interface { Store(key string, value interface{}) error diff --git a/cache/inmemory_cache.go b/cache/inmemory_cache.go index 2dacd46..828a9df 100644 --- a/cache/inmemory_cache.go +++ b/cache/inmemory_cache.go @@ -35,7 +35,7 @@ func (imc *InMemoryCache) StoreWithTTL(key string, value interface{}, ttl time.D func (imc *InMemoryCache) Get(key string, value interface{}) error { v, exists := imc.cache.Get(key) if !exists { - return ErrNilValue + return ErrNotFound } value = v diff --git a/cache/redis_cache.go b/cache/redis_cache.go index 0788dbe..ca06b0a 100644 --- a/cache/redis_cache.go +++ b/cache/redis_cache.go @@ -1,11 +1,13 @@ package cache import ( + "bytes" "context" + "encoding/gob" + "errors" "time" "github.com/Secured-Finance/dione/config" - "github.com/fxamacker/cbor/v2" "github.com/go-redis/redis/v8" ) @@ -28,41 +30,52 @@ func NewRedisCache(config *config.Config) *RedisCache { } func (rc *RedisCache) Store(key string, value interface{}) error { - mRes, err := cbor.Marshal(value) + data, err := gobMarshal(value) if err != nil { return err } - rc.Client.Set(rc.ctx, key, mRes, 0) + rc.Client.Set(rc.ctx, key, data, 0) return nil } func (rc *RedisCache) StoreWithTTL(key string, value interface{}, ttl time.Duration) error { - mRes, err := cbor.Marshal(value) + data, err := gobMarshal(value) if err != nil { return err } - - rc.Client.Set(rc.ctx, key, mRes, ttl) + 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 err == redis.Nil { - return ErrNilValue + if errors.Is(err, redis.Nil) { + return ErrNotFound } return err } - err = cbor.Unmarshal(data, &value) - if err != nil { - return err - } - return nil + return gobUnmarshal(data, &value) } func (rc *RedisCache) Delete(key string) { From c0e69ff1d0355aa16d46a16a8f465e37cbfbbb40 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Mon, 31 May 2021 22:24:07 +0300 Subject: [PATCH 18/59] Implement GetAllTxs function in mempool --- blockchain/pool/mempool.go | 51 +++++++++++--------------------------- cache/cache.go | 1 + cache/inmemory_cache.go | 10 +++++++- cache/redis_cache.go | 4 +++ 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/blockchain/pool/mempool.go b/blockchain/pool/mempool.go index 0d7fe5d..4d7697e 100644 --- a/blockchain/pool/mempool.go +++ b/blockchain/pool/mempool.go @@ -3,7 +3,6 @@ package pool import ( "encoding/hex" "sort" - "sync" "time" types2 "github.com/Secured-Finance/dione/blockchain/types" @@ -19,58 +18,26 @@ const ( ) type Mempool struct { - m sync.RWMutex - cache cache.Cache - txDescriptors []string // list of txs in cache + cache cache.Cache } -func NewMempool(c cache.Cache) (*Mempool, error) { +func NewMempool() (*Mempool, error) { mp := &Mempool{ - cache: c, + cache: cache.NewInMemoryCache(), // here we need to use separate cache } - var txDesc []string - err := c.Get("tx_list", &txDesc) - if err != nil || err != cache.ErrNilValue { - return nil, err - } - mp.txDescriptors = txDesc - return mp, nil } func (mp *Mempool) StoreTx(tx *types2.Transaction) error { - mp.m.Lock() - defer mp.m.Unlock() - hashStr := hex.EncodeToString(tx.Hash) err := mp.cache.StoreWithTTL(DefaultTxPrefix+hashStr, tx, DefaultTxTTL) - mp.txDescriptors = append(mp.txDescriptors, hashStr) - mp.cache.Store("tx_list", mp.txDescriptors) // update tx list in cache return err } func (mp *Mempool) GetTxsForNewBlock() []*types2.Transaction { - mp.m.Lock() - defer mp.m.Unlock() - var txForBlock []*types2.Transaction - var allTxs []*types2.Transaction - - for i, v := range mp.txDescriptors { - var tx types2.Transaction - err := mp.cache.Get(DefaultTxPrefix+v, &tx) - if err != nil { - if err == cache.ErrNilValue { - // descriptor is broken - // delete it and update list - mp.txDescriptors = removeItemFromStringSlice(mp.txDescriptors, i) - mp.cache.Store("tx_list", mp.txDescriptors) // update tx list in cache - } - continue - } - allTxs = append(allTxs, &tx) - } + allTxs := mp.GetAllTxs() sort.Slice(allTxs, func(i, j int) bool { return allTxs[i].Timestamp.Before(allTxs[j].Timestamp) }) @@ -87,6 +54,16 @@ func (mp *Mempool) GetTxsForNewBlock() []*types2.Transaction { return txForBlock } +func (mp *Mempool) GetAllTxs() []*types2.Transaction { + var allTxs []*types2.Transaction + + for _, v := range mp.cache.Items() { + tx := v.(types2.Transaction) + allTxs = append(allTxs, &tx) + } + return allTxs +} + func removeItemFromStringSlice(s []string, i int) []string { s[len(s)-1], s[i] = s[i], s[len(s)-1] return s[:len(s)-1] diff --git a/cache/cache.go b/cache/cache.go index 1c155f8..5af78e3 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -12,4 +12,5 @@ type Cache interface { StoreWithTTL(key string, value interface{}, ttl time.Duration) error Get(key string, value interface{}) error Delete(key string) + Items() map[string]interface{} } diff --git a/cache/inmemory_cache.go b/cache/inmemory_cache.go index 828a9df..fd4333f 100644 --- a/cache/inmemory_cache.go +++ b/cache/inmemory_cache.go @@ -15,7 +15,7 @@ type InMemoryCache struct { cache *cache.Cache } -func NewInMemoryCache() *InMemoryCache { +func NewInMemoryCache() Cache { return &InMemoryCache{ cache: cache.New(DefaultCacheExpiration, DefaultGCInterval), } @@ -45,3 +45,11 @@ func (imc *InMemoryCache) Get(key string, value interface{}) error { 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 +} diff --git a/cache/redis_cache.go b/cache/redis_cache.go index ca06b0a..3f7ace3 100644 --- a/cache/redis_cache.go +++ b/cache/redis_cache.go @@ -81,3 +81,7 @@ func (rc *RedisCache) Get(key string, value interface{}) error { func (rc *RedisCache) Delete(key string) { rc.Client.Del(rc.ctx, key) } + +func (rc *RedisCache) Items() map[string]interface{} { + return nil // TODO +} From 2afe85853ff0e2043c3c7ef9509ac93e8f03099b Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 2 Jun 2021 22:45:55 +0300 Subject: [PATCH 19/59] Implement mempool synchronization --- blockchain/pool/mempool.go | 28 ++++-- blockchain/sync/sync_mgr.go | 92 +++++++++++++++---- consensus/policy/policy.go | 4 +- go.mod | 15 +-- go.sum | 20 ++++ node/network_service.go | 51 ++++++++-- node/wire/get_mempool_tx.go | 13 +++ .../{get_blocks.go => get_range_of_blocks.go} | 4 +- node/wire/inv.go | 17 ++++ 9 files changed, 206 insertions(+), 38 deletions(-) create mode 100644 node/wire/get_mempool_tx.go rename node/wire/{get_blocks.go => get_range_of_blocks.go} (78%) create mode 100644 node/wire/inv.go diff --git a/blockchain/pool/mempool.go b/blockchain/pool/mempool.go index 4d7697e..22fec10 100644 --- a/blockchain/pool/mempool.go +++ b/blockchain/pool/mempool.go @@ -2,6 +2,7 @@ package pool import ( "encoding/hex" + "errors" "sort" "time" @@ -17,6 +18,10 @@ const ( DefaultTxPrefix = "tx_" ) +var ( + ErrTxNotFound = errors.New("tx isn't found in mempool") +) + type Mempool struct { cache cache.Cache } @@ -35,9 +40,9 @@ func (mp *Mempool) StoreTx(tx *types2.Transaction) error { return err } -func (mp *Mempool) GetTxsForNewBlock() []*types2.Transaction { +func (mp *Mempool) GetTransactionsForNewBlock() []*types2.Transaction { var txForBlock []*types2.Transaction - allTxs := mp.GetAllTxs() + allTxs := mp.GetAllTransactions() sort.Slice(allTxs, func(i, j int) bool { return allTxs[i].Timestamp.Before(allTxs[j].Timestamp) }) @@ -54,7 +59,7 @@ func (mp *Mempool) GetTxsForNewBlock() []*types2.Transaction { return txForBlock } -func (mp *Mempool) GetAllTxs() []*types2.Transaction { +func (mp *Mempool) GetAllTransactions() []*types2.Transaction { var allTxs []*types2.Transaction for _, v := range mp.cache.Items() { @@ -64,7 +69,18 @@ func (mp *Mempool) GetAllTxs() []*types2.Transaction { return allTxs } -func removeItemFromStringSlice(s []string, i int) []string { - s[len(s)-1], s[i] = s[i], s[len(s)-1] - return s[:len(s)-1] +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 index 2a6cbef..7063415 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -3,18 +3,19 @@ package sync import ( "bytes" "context" + "errors" "fmt" "strings" "sync" + "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" - "github.com/Secured-Finance/dione/node/wire" "github.com/libp2p/go-libp2p-core/peer" @@ -32,7 +33,8 @@ type SyncManager interface { } type syncManager struct { - blockPool *pool.BlockPool + blockpool *pool.BlockPool + mempool *pool.Mempool wg sync.WaitGroup ctx context.Context ctxCancelFunc context.CancelFunc @@ -44,7 +46,7 @@ type syncManager struct { func NewSyncManager(bp *pool.BlockPool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID) SyncManager { ctx, cancelFunc := context.WithCancel(context.Background()) return &syncManager{ - blockPool: bp, + blockpool: bp, ctx: ctx, ctxCancelFunc: cancelFunc, initialSyncCompleted: false, @@ -56,7 +58,16 @@ func NewSyncManager(bp *pool.BlockPool, p2pRPCClient *gorpc.Client, bootstrapPee func (sm *syncManager) Start() { sm.wg.Add(1) - sm.doInitialSync() + err := sm.doInitialBlockPoolSync() + if err != nil { + logrus.Error(err) + } + + err = sm.doInitialMempoolSync() + if err != nil { + logrus.Error(err) + } + go sm.syncLoop() } @@ -65,15 +76,15 @@ func (sm *syncManager) Stop() { sm.wg.Wait() } -func (sm *syncManager) doInitialSync() error { +func (sm *syncManager) doInitialBlockPoolSync() error { if sm.initialSyncCompleted { return nil } - ourLastHeight, err := sm.blockPool.GetLatestBlockHeight() + ourLastHeight, err := sm.blockpool.GetLatestBlockHeight() if err == pool.ErrLatestHeightNil { gBlock := types2.GenesisBlock() - err = sm.blockPool.StoreBlock(gBlock) // commit genesis block + err = sm.blockpool.StoreBlock(gBlock) // commit genesis block if err != nil { return err } @@ -96,16 +107,16 @@ func (sm *syncManager) doInitialSync() error { for heightCount > 0 { from = to + 1 var addedVal uint64 - if heightCount < node.MaxBlockCountForRetrieving { + if heightCount < policy.MaxBlockCountForRetrieving { addedVal = heightCount } else { - addedVal = node.MaxBlockCountForRetrieving + addedVal = policy.MaxBlockCountForRetrieving } heightCount -= addedVal to += addedVal - var getBlocksReply wire.GetBlocksReply - arg := wire.GetBlocksArg{From: from, To: to} - err = sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "GetBlocks", arg, &getBlocksReply) + 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 } @@ -129,9 +140,59 @@ func (sm *syncManager) doInitialSync() error { return nil } +func (sm *syncManager) doInitialMempoolSync() error { + 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 + } + + 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()) + } + } + // FIXME 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) + 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) } @@ -163,7 +224,7 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) error { } } - err = sm.blockPool.StoreBlock(&block) + err = sm.blockpool.StoreBlock(&block) if err != nil { return fmt.Errorf("failed to store block in blockpool: %s", err.Error()) } @@ -172,5 +233,4 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) error { } func (sm *syncManager) syncLoop() { - } diff --git a/consensus/policy/policy.go b/consensus/policy/policy.go index 28e96f9..f5b5cf2 100644 --- a/consensus/policy/policy.go +++ b/consensus/policy/policy.go @@ -1,5 +1,7 @@ package policy const ( - BlockMaxTransactionCount = 100 + BlockMaxTransactionCount = 100 + MaxBlockCountForRetrieving = 500 // we do it just like in Bitcoin + MaxTransactionCountForRetrieving = 50000 ) diff --git a/go.mod b/go.mod index f84eebd..025a0d6 100644 --- a/go.mod +++ b/go.mod @@ -29,20 +29,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/ipfs/go-log/v2 v2.1.3 // indirect github.com/jmoiron/sqlx v1.2.0 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/klauspost/cpuid/v2 v2.0.6 // 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.0 - github.com/libp2p/go-libp2p-gorpc v0.1.2 + 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.2 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 @@ -56,15 +57,17 @@ require ( 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/multierr v1.7.0 // indirect + go.uber.org/zap v1.17.0 + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a 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/sys v0.0.0-20210601080250-7ecdf8ef093b // 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 1530895..57efd7a 100644 --- a/go.sum +++ b/go.sum @@ -760,6 +760,8 @@ github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscw 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= @@ -1017,6 +1019,8 @@ github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJB 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 h1:5K3mT+64qDTKbV3yTdbMCzJ7O6wbNsavAEb8iqBvBcI= 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= github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= @@ -1032,6 +1036,8 @@ github.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+W github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= github.com/libp2p/go-libp2p-gorpc v0.1.2 h1:jHL0F79uDVPNsflS9byf8Wk23MQ0G+r5nUnLChoUn8A= github.com/libp2p/go-libp2p-gorpc v0.1.2/go.mod h1:ulZShaJCp3JHlBMHiA20efUmiqDECza+JvGFNXJyKdI= +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= @@ -1350,6 +1356,8 @@ github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u 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.2 h1:vapLUGlFpvvkaemMvKGGxjruOzaIzQbn41J6R9vxeyU= +github.com/multiformats/go-multiaddr v0.3.2/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= @@ -1703,10 +1711,14 @@ github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q= 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/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4= 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= @@ -1847,6 +1859,8 @@ go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+ 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= @@ -1854,6 +1868,8 @@ 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= @@ -1901,6 +1917,8 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP 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-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 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= @@ -2099,6 +2117,8 @@ golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk= +golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/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= diff --git a/node/network_service.go b/node/network_service.go index 5e65b22..deb93d8 100644 --- a/node/network_service.go +++ b/node/network_service.go @@ -2,20 +2,23 @@ package node import ( "context" + "errors" + "fmt" + + 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" ) -const ( - MaxBlockCountForRetrieving = 500 // we do it just like in Bitcoin -) - type NetworkService struct { blockpool *pool.BlockPool + mempool *pool.Mempool + rpcClient *gorpc.Client } func NewNetworkService(bp *pool.BlockPool) *NetworkService { @@ -24,7 +27,7 @@ func NewNetworkService(bp *pool.BlockPool) *NetworkService { } } -func (s *NetworkService) LastBlockHeight(ctx context.Context, arg interface{}, reply *wire.LastBlockHeightReply) { +func (s *NetworkService) LastBlockHeight(ctx context.Context, arg struct{}, reply *wire.LastBlockHeightReply) { height, err := s.blockpool.GetLatestBlockHeight() if err != nil { reply.Error = err @@ -33,13 +36,13 @@ func (s *NetworkService) LastBlockHeight(ctx context.Context, arg interface{}, r reply.Height = height } -func (s *NetworkService) GetBlocks(ctx context.Context, arg wire.GetBlocksArg, reply *wire.GetBlocksReply) { +func (s *NetworkService) GetRangeOfBlocks(ctx context.Context, arg wire.GetRangeOfBlocksArg, reply *wire.GetRangeOfBlocksReply) { if arg.From > arg.To { errText := "incorrect arguments: from > to" reply.Error = &errText return } - if arg.To-arg.From > MaxBlockCountForRetrieving { + if arg.To-arg.From > policy.MaxBlockCountForRetrieving { errText := "incorrect arguments: count of block for retrieving is exceeded the limit" reply.Error = &errText return @@ -54,3 +57,37 @@ func (s *NetworkService) GetBlocks(ctx context.Context, arg wire.GetBlocksArg, r reply.Blocks = append(reply.Blocks, *block) } } + +func (s *NetworkService) Mempool(ctx context.Context, arg struct{}, reply *wire.InvMessage) { + 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, + }) + } +} + +func (s *NetworkService) GetMempoolTxs(ctx context.Context, arg wire.GetMempoolTxsArg, reply *wire.GetMempoolTxsReply) { + if len(arg.Items) > MaxTransactionCountForRetrieving { + pid, _ := gorpc.GetRequestSender(ctx) + logrus.Warnf("Max tx count limit exceeded for GetMempoolTxs request of node %s", pid) + reply.Error = fmt.Errorf("max tx count limit exceeded") + return + } + + 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 { + reply.Error = err + return + } + } + reply.Transactions = append(reply.Transactions, *tx) + } +} diff --git a/node/wire/get_mempool_tx.go b/node/wire/get_mempool_tx.go new file mode 100644 index 0000000..da364e5 --- /dev/null +++ b/node/wire/get_mempool_tx.go @@ -0,0 +1,13 @@ +package wire + +import "github.com/Secured-Finance/dione/blockchain/types" + +type GetMempoolTxsArg struct { + Items [][]byte +} + +type GetMempoolTxsReply struct { + Transactions []types.Transaction + NotFoundTxs [][]byte + Error error +} diff --git a/node/wire/get_blocks.go b/node/wire/get_range_of_blocks.go similarity index 78% rename from node/wire/get_blocks.go rename to node/wire/get_range_of_blocks.go index dda8cfe..5ecc770 100644 --- a/node/wire/get_blocks.go +++ b/node/wire/get_range_of_blocks.go @@ -2,12 +2,12 @@ package wire import "github.com/Secured-Finance/dione/blockchain/types" -type GetBlocksArg struct { +type GetRangeOfBlocksArg struct { From uint64 To uint64 } -type GetBlocksReply struct { +type GetRangeOfBlocksReply struct { Blocks []types.Block FailedBlockHeights []uint64 // list of block heights the node was unable to retrieve Error *string 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 +} From 809c5f2a23cd36dd54dcf050a58095384704a4bc Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 2 Jun 2021 22:48:56 +0300 Subject: [PATCH 20/59] Fix mempool init in SyncManager --- blockchain/sync/sync_mgr.go | 3 ++- node/node.go | 33 ++++++++++++++++++++++----------- node/node_dep_providers.go | 8 ++++---- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 7063415..3b6773d 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -43,10 +43,11 @@ type syncManager struct { rpcClient *gorpc.Client } -func NewSyncManager(bp *pool.BlockPool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID) SyncManager { +func NewSyncManager(bp *pool.BlockPool, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID) SyncManager { ctx, cancelFunc := context.WithCancel(context.Background()) return &syncManager{ blockpool: bp, + mempool: mp, ctx: ctx, ctxCancelFunc: cancelFunc, initialSyncCompleted: false, diff --git a/node/node.go b/node/node.go index 108ccd4..8f1ae21 100644 --- a/node/node.go +++ b/node/node.go @@ -9,6 +9,10 @@ import ( "os" "time" + types2 "github.com/Secured-Finance/dione/blockchain/types" + + "github.com/fxamacker/cbor/v2" + gorpc "github.com/libp2p/go-libp2p-gorpc" "github.com/Secured-Finance/dione/blockchain/pool" @@ -143,7 +147,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Block pool database has been successfully initialized!") // initialize mempool - mp, err := provideMemPool(c) + mp, err := provideMemPool() if err != nil { logrus.Fatalf("Failed to initialize mempool: %s", err.Error()) } @@ -163,7 +167,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim r := provideP2PRPCClient(lhost) // initialize sync manager - sm, err := provideSyncManager(bp, r, baddrs[0]) // FIXME here we just pick up first bootstrap in list + sm, err := provideSyncManager(bp, mp, r, baddrs[0]) // FIXME here we just pick up first bootstrap in list if err != nil { logrus.Fatal(err) } @@ -268,12 +272,7 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { select { case event := <-eventChan: { - err := n.Cache.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") + logrus.Info("Let's wait a little so that all nodes have time to receive the request") time.Sleep(5 * time.Second) task, err := n.Miner.MineTask(context.TODO(), event) @@ -281,13 +280,25 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { logrus.Errorf("Failed to mine task: %v", err) } if task == nil { + logrus.Warnf("Task is nil!") continue } - logrus.Infof("Proposed new Dione task with ID: %s", event.ReqID.String()) - err = n.ConsensusManager.Propose(*task) + payload, err := cbor.Marshal(task) if err != nil { - logrus.Errorf("Failed to propose task: %v", err) + logrus.Errorf("Failed to marshal request event") + continue } + tx := types2.CreateTransaction(payload) + err = n.MemPool.StoreTx(tx) + if err != nil { + logrus.Errorf("Failed to store tx in mempool: %s", err.Error()) + continue + } + //logrus.Infof("Proposed new Dione task with ID: %s", event.ReqID.String()) + //err = n.ConsensusManager.Propose(*task) + //if err != nil { + // logrus.Errorf("Failed to propose task: %v", err) + //} } case <-ctx.Done(): break EventLoop diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index e34b60b..f52b55c 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -155,16 +155,16 @@ func provideBlockPool(config *config.Config) (*pool.BlockPool, error) { return pool.NewBlockPool(config.Blockchain.DatabasePath) } -func provideMemPool(c cache.Cache) (*pool.Mempool, error) { - return pool.NewMempool(c) +func provideMemPool() (*pool.Mempool, error) { + return pool.NewMempool() } -func provideSyncManager(bp *pool.BlockPool, r *gorpc.Client, bootstrap multiaddr.Multiaddr) (sync.SyncManager, error) { +func provideSyncManager(bp *pool.BlockPool, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr) (sync.SyncManager, error) { addr, err := peer.AddrInfoFromP2pAddr(bootstrap) if err != nil { return nil, err } - return sync.NewSyncManager(bp, r, addr.ID), nil + return sync.NewSyncManager(bp, mp, r, addr.ID), nil } func provideP2PRPCClient(h host.Host) *gorpc.Client { From 0695d2386645f5710547ef5a06c33fc202d1d479 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 3 Jun 2021 00:19:52 +0300 Subject: [PATCH 21/59] Refactor pubsub package --- consensus/consensus.go | 101 +++++++++++++++++++++++-------- consensus/consensus_validator.go | 57 +++++++++-------- consensus/msg_log.go | 16 ++--- consensus/types/message.go | 8 +-- node/node.go | 2 +- node/node_dep_providers.go | 4 +- pubsub/handler.go | 7 --- pubsub/message.go | 20 ++++++ pubsub/pubsub_router.go | 18 +++--- 9 files changed, 145 insertions(+), 88 deletions(-) delete mode 100644 pubsub/handler.go create mode 100644 pubsub/message.go diff --git a/consensus/consensus.go b/consensus/consensus.go index d63e0f6..8079a5f 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -4,6 +4,8 @@ import ( "math/big" "sync" + "github.com/fxamacker/cbor/v2" + "github.com/Secured-Finance/dione/cache" "github.com/Secured-Finance/dione/consensus/types" @@ -45,9 +47,9 @@ func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey pcm.ethereumClient = ethereumClient pcm.cache = evc pcm.consensusMap = map[string]*Consensus{} - pcm.psb.Hook(types.MessageTypePrePrepare, pcm.handlePrePrepare) - pcm.psb.Hook(types.MessageTypePrepare, pcm.handlePrepare) - pcm.psb.Hook(types.MessageTypeCommit, pcm.handleCommit) + pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare) + pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare) + pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit) return pcm } @@ -62,44 +64,54 @@ func (pcm *PBFTConsensusManager) Propose(task types2.DioneTask) error { return nil } -func (pcm *PBFTConsensusManager) handlePrePrepare(message *types.Message) { - if message.Payload.Task.Miner == pcm.miner.address { +func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) { + cmsg, err := unmarshalPayload(message) + if err != nil { return } - if pcm.msgLog.Exists(*message) { + + if cmsg.Task.Miner == pcm.miner.address { + return + } + if pcm.msgLog.Exists(cmsg) { logrus.Debugf("received existing pre_prepare msg, dropping...") return } - if !pcm.validator.Valid(*message) { + if !pcm.validator.Valid(cmsg) { logrus.Warn("received invalid pre_prepare msg, dropping...") return } - pcm.msgLog.AddMessage(*message) + pcm.msgLog.AddMessage(cmsg) - prepareMsg, err := NewMessage(message, types.MessageTypePrepare) + prepareMsg, err := NewMessage(message, pubsub.PrepareMessageType) if err != nil { logrus.Errorf("failed to create prepare message: %v", err) } - pcm.createConsensusInfo(&message.Payload.Task, false) + pcm.createConsensusInfo(&cmsg.Task, false) pcm.psb.BroadcastToServiceTopic(&prepareMsg) } -func (pcm *PBFTConsensusManager) handlePrepare(message *types.Message) { - if pcm.msgLog.Exists(*message) { +func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { + cmsg, err := unmarshalPayload(message) + if err != nil { + return + } + + if pcm.msgLog.Exists(cmsg) { logrus.Debugf("received existing prepare msg, dropping...") return } - if !pcm.validator.Valid(*message) { + if !pcm.validator.Valid(cmsg) { logrus.Warn("received invalid prepare msg, dropping...") return } - pcm.msgLog.AddMessage(*message) + pcm.msgLog.AddMessage(cmsg) - if len(pcm.msgLog.GetMessagesByTypeAndConsensusID(types.MessageTypePrepare, message.Payload.Task.ConsensusID)) >= pcm.minApprovals { + if len(pcm.msgLog.Get(types.MessageTypePrepare, cmsg.Task.ConsensusID)) >= pcm.minApprovals { commitMsg, err := NewMessage(message, types.MessageTypeCommit) if err != nil { logrus.Errorf("failed to create commit message: %w", err) @@ -108,21 +120,25 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *types.Message) { } } -func (pcm *PBFTConsensusManager) handleCommit(message *types.Message) { - if pcm.msgLog.Exists(*message) { +func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { + cmsg, err := unmarshalPayload(message) + if err != nil { + return + } + + if pcm.msgLog.Exists(cmsg) { logrus.Debugf("received existing commit msg, dropping...") return } - if !pcm.validator.Valid(*message) { + if !pcm.validator.Valid(cmsg) { logrus.Warn("received invalid commit msg, dropping...") return } - pcm.msgLog.AddMessage(*message) + pcm.msgLog.AddMessage(cmsg) - consensusMsg := message.Payload - if len(pcm.msgLog.GetMessagesByTypeAndConsensusID(types.MessageTypeCommit, message.Payload.Task.ConsensusID)) >= pcm.minApprovals { - info := pcm.GetConsensusInfo(consensusMsg.Task.ConsensusID) + if len(pcm.msgLog.Get(types.MessageTypeCommit, cmsg.Task.ConsensusID)) >= pcm.minApprovals { + info := pcm.GetConsensusInfo(cmsg.Task.ConsensusID) if info == nil { logrus.Debugf("consensus doesn't exist in our consensus map - skipping...") return @@ -133,13 +149,13 @@ func (pcm *PBFTConsensusManager) handleCommit(message *types.Message) { 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) + logrus.Infof("Submitting on-chain result for consensus ID: %s", cmsg.Task.ConsensusID) + reqID, ok := new(big.Int).SetString(cmsg.Task.RequestID, 10) if !ok { - logrus.Errorf("Failed to parse request ID: %v", consensusMsg.Task.RequestID) + logrus.Errorf("Failed to parse request ID: %v", cmsg.Task.RequestID) } - err := pcm.ethereumClient.SubmitRequestAnswer(reqID, consensusMsg.Task.Payload) + err := pcm.ethereumClient.SubmitRequestAnswer(reqID, cmsg.Task.Payload) if err != nil { logrus.Errorf("Failed to submit on-chain result: %v", err) } @@ -167,3 +183,36 @@ func (pcm *PBFTConsensusManager) GetConsensusInfo(consensusID string) *Consensus return c } + +func unmarshalPayload(msg *pubsub.PubSubMessage) (types.ConsensusMessage, error) { + var task types2.DioneTask + err := cbor.Unmarshal(msg.Payload, &task) + if err != nil { + logrus.Debug(err) + return types.ConsensusMessage{}, err + } + var consensusMessageType types.MessageType + switch msg.Type { + case pubsub.PrePrepareMessageType: + { + consensusMessageType = types.MessageTypePrePrepare + break + } + case pubsub.PrepareMessageType: + { + consensusMessageType = types.MessageTypePrepare + break + } + case pubsub.CommitMessageType: + { + consensusMessageType = types.MessageTypeCommit + break + } + } + cmsg := types.ConsensusMessage{ + Type: consensusMessageType, + From: msg.From, + Task: task, + } + return cmsg, nil +} diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 4c37be8..b6d16d5 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -12,7 +12,7 @@ import ( ) type ConsensusValidator struct { - validationFuncMap map[types2.MessageType]func(msg types2.Message) bool + validationFuncMap map[types2.MessageType]func(msg types2.ConsensusMessage) bool cache cache.Cache miner *Miner } @@ -23,13 +23,12 @@ func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { miner: miner, } - cv.validationFuncMap = map[types2.MessageType]func(msg types2.Message) bool{ - types2.MessageTypePrePrepare: func(msg types2.Message) bool { + cv.validationFuncMap = map[types2.MessageType]func(msg types2.ConsensusMessage) bool{ + types2.MessageTypePrePrepare: func(msg types2.ConsensusMessage) bool { // TODO here we need to do validation of tx itself - consensusMsg := msg.Payload // === verify task signature === - err := VerifyTaskSignature(consensusMsg.Task) + err := VerifyTaskSignature(msg.Task) if err != nil { logrus.Errorf("unable to verify signature: %v", err) return false @@ -38,15 +37,15 @@ func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { // === verify if request exists in cache === var requestEvent *dioneOracle.DioneOracleNewOracleRequest - err = cv.cache.Get("request_"+consensusMsg.Task.RequestID, &requestEvent) + err = cv.cache.Get("request_"+msg.Task.RequestID, &requestEvent) if err != nil { logrus.Errorf("the request doesn't exist in the cache or has been failed to decode: %v", err) return false } - if requestEvent.OriginChain != consensusMsg.Task.OriginChain || - requestEvent.RequestType != consensusMsg.Task.RequestType || - requestEvent.RequestParams != consensusMsg.Task.RequestParams { + if requestEvent.OriginChain != msg.Task.OriginChain || + requestEvent.RequestType != msg.Task.RequestType || + requestEvent.RequestParams != msg.Task.RequestParams { logrus.Errorf("the incoming task and cached request requestEvent don't match!") return false @@ -54,14 +53,14 @@ func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { ///////////////////////////////// // === verify election proof wincount preliminarily === - if consensusMsg.Task.ElectionProof.WinCount < 1 { + if msg.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)) + err = cv.miner.IsMinerEligibleToProposeTask(common.HexToAddress(msg.Task.MinerEth)) if err != nil { logrus.Errorf("miner is not eligible to propose task: %v", err) return false @@ -69,22 +68,22 @@ func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { ///////////////////////////////// // === verify election proof vrf === - minerAddressMarshalled, err := consensusMsg.Task.Miner.MarshalBinary() + minerAddressMarshalled, err := msg.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, + msg.Task.BeaconEntries[1].Data, crypto.DomainSeparationTag_ElectionProofProduction, - consensusMsg.Task.DrandRound, + msg.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) + err = VerifyVRF(msg.Task.Miner, electionProofRandomness, msg.Task.ElectionProof.VRFProof) if err != nil { logrus.Errorf("failed to verify election proof vrf: %v", err) } @@ -92,9 +91,9 @@ func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { // === verify ticket vrf === ticketRandomness, err := DrawRandomness( - consensusMsg.Task.BeaconEntries[1].Data, + msg.Task.BeaconEntries[1].Data, crypto.DomainSeparationTag_TicketProduction, - consensusMsg.Task.DrandRound-types.TicketRandomnessLookback, + msg.Task.DrandRound-types.TicketRandomnessLookback, minerAddressMarshalled, ) if err != nil { @@ -102,48 +101,48 @@ func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { return false } - err = VerifyVRF(consensusMsg.Task.Miner, ticketRandomness, consensusMsg.Task.Ticket.VRFProof) + err = VerifyVRF(msg.Task.Miner, ticketRandomness, msg.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)) + mStake, nStake, err := cv.miner.GetStakeInfo(common.HexToAddress(msg.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 { + actualWinCount := msg.Task.ElectionProof.ComputeWinCount(*mStake, *nStake) + if msg.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 validationFunc := validation.GetValidationMethod(msg.Task.OriginChain, msg.Task.RequestType); validationFunc != nil { + err := validationFunc(msg.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) + logrus.Debugf("Origin chain [%v]/request type[%v] doesn't have any payload validation!", msg.Task.OriginChain, msg.Task.RequestType) } ///////////////////////////////// return true }, - types2.MessageTypePrepare: func(msg types2.Message) bool { - err := VerifyTaskSignature(msg.Payload.Task) + types2.MessageTypePrepare: func(msg types2.ConsensusMessage) bool { + err := VerifyTaskSignature(msg.Task) if err != nil { return false } return true }, - types2.MessageTypeCommit: func(msg types2.Message) bool { - err := VerifyTaskSignature(msg.Payload.Task) + types2.MessageTypeCommit: func(msg types2.ConsensusMessage) bool { + err := VerifyTaskSignature(msg.Task) if err != nil { return false } @@ -154,6 +153,6 @@ func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { return cv } -func (cv *ConsensusValidator) Valid(msg types2.Message) bool { +func (cv *ConsensusValidator) Valid(msg types2.ConsensusMessage) bool { return cv.validationFuncMap[msg.Type](msg) } diff --git a/consensus/msg_log.go b/consensus/msg_log.go index 0bd8011..3f0bac7 100644 --- a/consensus/msg_log.go +++ b/consensus/msg_log.go @@ -8,7 +8,7 @@ import ( type MessageLog struct { messages mapset.Set maxLogSize int - validationFuncMap map[types2.MessageType]func(message types2.Message) + validationFuncMap map[types2.MessageType]func(message types2.ConsensusMessage) } func NewMessageLog() *MessageLog { @@ -20,21 +20,21 @@ func NewMessageLog() *MessageLog { return msgLog } -func (ml *MessageLog) AddMessage(msg types2.Message) { +func (ml *MessageLog) AddMessage(msg types2.ConsensusMessage) { ml.messages.Add(msg) } -func (ml *MessageLog) Exists(msg types2.Message) bool { +func (ml *MessageLog) 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 *MessageLog) Get(typ types2.MessageType, consensusID string) []*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.Type == typ && msg.Task.ConsensusID == consensusID { + result = append(result, &msg) } } diff --git a/consensus/types/message.go b/consensus/types/message.go index b2995e9..f603839 100644 --- a/consensus/types/message.go +++ b/consensus/types/message.go @@ -17,10 +17,6 @@ const ( type ConsensusMessage struct { Task types.DioneTask -} - -type Message struct { - Type MessageType - Payload ConsensusMessage - From peer.ID `cbor:"-"` + From peer.ID + Type MessageType } diff --git a/node/node.go b/node/node.go index 8f1ae21..9675405 100644 --- a/node/node.go +++ b/node/node.go @@ -167,7 +167,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim r := provideP2PRPCClient(lhost) // initialize sync manager - sm, err := provideSyncManager(bp, mp, r, baddrs[0]) // FIXME here we just pick up first bootstrap in list + sm, err := provideSyncManager(bp, mp, r, baddrs[0], psb) // FIXME here we just pick up first bootstrap in list if err != nil { logrus.Fatal(err) } diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index f52b55c..6107361 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -159,12 +159,12 @@ func provideMemPool() (*pool.Mempool, error) { return pool.NewMempool() } -func provideSyncManager(bp *pool.BlockPool, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr) (sync.SyncManager, error) { +func provideSyncManager(bp *pool.BlockPool, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { addr, err := peer.AddrInfoFromP2pAddr(bootstrap) if err != nil { return nil, err } - return sync.NewSyncManager(bp, mp, r, addr.ID), nil + return sync.NewSyncManager(bp, mp, r, addr.ID, psb), nil } func provideP2PRPCClient(h host.Host) *gorpc.Client { 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..bd39edb 100644 --- a/pubsub/pubsub_router.go +++ b/pubsub/pubsub_router.go @@ -6,10 +6,8 @@ import ( "github.com/fxamacker/cbor/v2" - "github.com/Secured-Finance/dione/consensus/types" - - host "github.com/libp2p/go-libp2p-core/host" - peer "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/peer" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/sirupsen/logrus" ) @@ -20,11 +18,13 @@ type PubSubRouter struct { context context.Context contextCancel context.CancelFunc serviceSubscription *pubsub.Subscription - handlers map[types.MessageType][]Handler + handlers map[PubSubMessageType][]Handler oracleTopicName string oracleTopic *pubsub.Topic } +type Handler func(message *PubSubMessage) + func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubRouter { ctx, ctxCancel := context.WithCancel(context.Background()) @@ -32,7 +32,7 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR node: h, context: ctx, contextCancel: ctxCancel, - handlers: make(map[types.MessageType][]Handler), + handlers: make(map[PubSubMessageType][]Handler), } var pbOptions []pubsub.Option @@ -102,7 +102,7 @@ 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()) @@ -119,7 +119,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 +127,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 From 025bb9a6d11b4aba15c5cb256e2a8089016b11cb Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 4 Jun 2021 00:15:32 +0300 Subject: [PATCH 22/59] Refactor pubsub package again, make it more flexible --- consensus/consensus.go | 28 +++++++++++++--------------- consensus/utils.go | 20 ++++++++++++++------ pubsub/message.go | 7 ++++++- pubsub/pubsub_router.go | 32 ++++++++++++++++++++++++-------- 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 8079a5f..1aff9d1 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -1,11 +1,10 @@ package consensus import ( + "fmt" "math/big" "sync" - "github.com/fxamacker/cbor/v2" - "github.com/Secured-Finance/dione/cache" "github.com/Secured-Finance/dione/consensus/types" @@ -47,9 +46,9 @@ func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey pcm.ethereumClient = ethereumClient pcm.cache = evc pcm.consensusMap = map[string]*Consensus{} - pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare) - pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare) - pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit) + pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types2.DioneTask{}) + pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types2.DioneTask{}) + pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types2.DioneTask{}) return pcm } @@ -64,7 +63,7 @@ func (pcm *PBFTConsensusManager) Propose(task types2.DioneTask) error { return nil } -func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) { +func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage) { cmsg, err := unmarshalPayload(message) if err != nil { return @@ -87,6 +86,7 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) prepareMsg, err := NewMessage(message, pubsub.PrepareMessageType) if err != nil { logrus.Errorf("failed to create prepare message: %v", err) + return } pcm.createConsensusInfo(&cmsg.Task, false) @@ -94,7 +94,7 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) pcm.psb.BroadcastToServiceTopic(&prepareMsg) } -func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { +func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { cmsg, err := unmarshalPayload(message) if err != nil { return @@ -112,7 +112,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { pcm.msgLog.AddMessage(cmsg) if len(pcm.msgLog.Get(types.MessageTypePrepare, cmsg.Task.ConsensusID)) >= pcm.minApprovals { - commitMsg, err := NewMessage(message, types.MessageTypeCommit) + commitMsg, err := NewMessage(message, pubsub.CommitMessageType) if err != nil { logrus.Errorf("failed to create commit message: %w", err) } @@ -120,7 +120,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { } } -func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { +func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { cmsg, err := unmarshalPayload(message) if err != nil { return @@ -184,12 +184,10 @@ func (pcm *PBFTConsensusManager) GetConsensusInfo(consensusID string) *Consensus return c } -func unmarshalPayload(msg *pubsub.PubSubMessage) (types.ConsensusMessage, error) { - var task types2.DioneTask - err := cbor.Unmarshal(msg.Payload, &task) - if err != nil { - logrus.Debug(err) - return types.ConsensusMessage{}, err +func unmarshalPayload(msg *pubsub.GenericMessage) (types.ConsensusMessage, error) { + task, ok := msg.Payload.(types2.DioneTask) + if !ok { + return types.ConsensusMessage{}, fmt.Errorf("cannot convert payload to DioneTask") } var consensusMessageType types.MessageType switch msg.Type { diff --git a/consensus/utils.go b/consensus/utils.go index d1813a0..a94a1df 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -4,6 +4,10 @@ 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" @@ -107,17 +111,17 @@ func VerifyTaskSignature(task types.DioneTask) error { return nil } -func NewMessage(msg *types2.Message, typ types2.MessageType) (types2.Message, error) { - var newMsg types2.Message +func NewMessage(msg *pubsub.GenericMessage, typ pubsub.PubSubMessageType) (pubsub.GenericMessage, error) { + var newMsg pubsub.GenericMessage 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 +func CreatePrePrepareWithTaskSignature(task *types.DioneTask, privateKey []byte) (*pubsub.GenericMessage, error) { + var message pubsub.GenericMessage + message.Type = pubsub.PrePrepareMessageType cHash, err := hashstructure.Hash(task, hashstructure.FormatV2, nil) if err != nil { @@ -128,6 +132,10 @@ func CreatePrePrepareWithTaskSignature(task *types.DioneTask, privateKey []byte) return nil, err } task.Signature = signature.Data - message.Payload = types2.ConsensusMessage{Task: *task} + data, err := cbor.Marshal(types2.ConsensusMessage{Task: *task}) + if err != nil { + return nil, err + } + message.Payload = data return &message, nil } diff --git a/pubsub/message.go b/pubsub/message.go index 5537d51..4b803f3 100644 --- a/pubsub/message.go +++ b/pubsub/message.go @@ -13,8 +13,13 @@ const ( NewBlockMessageType ) -type PubSubMessage struct { +type GenericMessage struct { Type PubSubMessageType From peer.ID `cbor:"-"` + Payload interface{} +} + +type PubSubMessage struct { + Type PubSubMessageType Payload []byte } diff --git a/pubsub/pubsub_router.go b/pubsub/pubsub_router.go index bd39edb..e76cae8 100644 --- a/pubsub/pubsub_router.go +++ b/pubsub/pubsub_router.go @@ -21,9 +21,10 @@ type PubSubRouter struct { handlers map[PubSubMessageType][]Handler oracleTopicName string oracleTopic *pubsub.Topic + typeMapping map[PubSubMessageType]interface{} // message type -> sample } -type Handler func(message *PubSubMessage) +type Handler func(message *GenericMessage) func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubRouter { ctx, ctxCancel := context.WithCancel(context.Background()) @@ -102,16 +103,30 @@ func (psr *PubSubRouter) handleMessage(p *pubsub.Message) { if senderPeerID == psr.node.ID() { return } - var message PubSubMessage - err = cbor.Unmarshal(p.Data, &message) + var genericMessage PubSubMessage + var message GenericMessage + err = cbor.Unmarshal(p.Data, &genericMessage) if err != nil { - logrus.Warn("Unable to decode message data! " + err.Error()) + logrus.Warn("Unable to decode pubsub message data! " + err.Error()) + return + } + sampleMsg, ok := psr.typeMapping[genericMessage.Type] + if !ok { + logrus.Warnf("Unknown message type %d: we have no clue how to decode it", genericMessage.Type) + return + } + destMsg := sampleMsg + err = cbor.Unmarshal(genericMessage.Payload, &destMsg) + if err != nil { + logrus.Warn("Unable to decode pubsub message data! " + err.Error()) return } message.From = senderPeerID - handlers, ok := psr.handlers[message.Type] + message.Type = genericMessage.Type + message.Payload = destMsg + handlers, ok := psr.handlers[genericMessage.Type] if !ok { - logrus.Warn("Dropping message " + string(message.Type) + " because we don't have any handlers!") + logrus.Warn("Dropping pubsub message " + string(genericMessage.Type) + " because we don't have any handlers!") return } for _, v := range handlers { @@ -119,15 +134,16 @@ func (psr *PubSubRouter) handleMessage(p *pubsub.Message) { } } -func (psr *PubSubRouter) Hook(messageType PubSubMessageType, handler Handler) { +func (psr *PubSubRouter) Hook(messageType PubSubMessageType, handler Handler, sample interface{}) { _, ok := psr.handlers[messageType] if !ok { psr.handlers[messageType] = []Handler{} } psr.handlers[messageType] = append(psr.handlers[messageType], handler) + psr.typeMapping[messageType] = sample } -func (psr *PubSubRouter) BroadcastToServiceTopic(msg *PubSubMessage) error { +func (psr *PubSubRouter) BroadcastToServiceTopic(msg *GenericMessage) error { data, err := cbor.Marshal(msg) if err != nil { return err From 8445baafdbb42ce41f2dd54a991aaa37658af332 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 4 Jun 2021 00:21:14 +0300 Subject: [PATCH 23/59] Implement handling new transactions announced on network in SyncManager --- blockchain/sync/sync_mgr.go | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 3b6773d..a33f752 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -8,6 +8,8 @@ import ( "strings" "sync" + "github.com/Secured-Finance/dione/pubsub" + "github.com/Secured-Finance/dione/consensus/policy" "github.com/wealdtech/go-merkletree/keccak256" @@ -28,7 +30,6 @@ import ( type SyncManager interface { Start() - Stop() } @@ -41,11 +42,12 @@ type syncManager struct { initialSyncCompleted bool bootstrapPeer peer.ID rpcClient *gorpc.Client + psb *pubsub.PubSubRouter } -func NewSyncManager(bp *pool.BlockPool, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID) SyncManager { +func NewSyncManager(bp *pool.BlockPool, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID, psb *pubsub.PubSubRouter) SyncManager { ctx, cancelFunc := context.WithCancel(context.Background()) - return &syncManager{ + sm := &syncManager{ blockpool: bp, mempool: mp, ctx: ctx, @@ -53,7 +55,12 @@ func NewSyncManager(bp *pool.BlockPool, mp *pool.Mempool, p2pRPCClient *gorpc.Cl initialSyncCompleted: false, bootstrapPeer: bootstrapPeer, rpcClient: p2pRPCClient, + psb: psb, } + + psb.Hook(pubsub.NewTxMessageType, sm.onNewTransaction, types2.Transaction{}) + + return sm } func (sm *syncManager) Start() { @@ -179,6 +186,9 @@ func (sm *syncManager) doInitialMempoolSync() error { if err != nil { return err } + if getMempoolTxReply.Error != nil { + return getMempoolTxReply.Error + } for _, v := range getMempoolTxReply.Transactions { err := sm.mempool.StoreTx(&v) if err != nil { @@ -235,3 +245,21 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) error { func (sm *syncManager) syncLoop() { } + +func (sm *syncManager) onNewTransaction(message *pubsub.GenericMessage) { + tx, ok := message.Payload.(types2.Transaction) + if !ok { + logrus.Warn("failed to convert payload to Transaction") + return + } + + if !tx.ValidateHash() { + logrus.Warn("failed to validate tx hash, rejecting it") + return + } // TODO add more checks on tx + + err := sm.mempool.StoreTx(&tx) + if err != nil { + logrus.Warnf("failed to store incoming transaction in mempool: %s", err.Error()) + } +} From 2eae45ca384ee84f54f80f730901e85a8264f5b6 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sat, 5 Jun 2021 00:07:04 +0300 Subject: [PATCH 24/59] Add watching new DRAND entries and fix drand entry validation --- beacon/beacon.go | 50 +++++--------------------------- {drand => beacon/drand}/drand.go | 49 +++++++++++++++++-------------- drand/drand_test.go | 23 --------------- node/node_dep_providers.go | 5 ++-- types/beacon.go | 12 ++++---- types/task.go | 2 -- 6 files changed, 45 insertions(+), 96 deletions(-) rename {drand => beacon/drand}/drand.go (83%) delete mode 100644 drand/drand_test.go diff --git a/beacon/beacon.go b/beacon/beacon.go index 1589a51..f735319 100644 --- a/beacon/beacon.go +++ b/beacon/beacon.go @@ -42,8 +42,9 @@ type BeaconAPI interface { LatestBeaconRound() uint64 } -func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prevEpoch types.DrandRound, prevEntry types.BeaconEntry) error { - parentBeacon := beaconNetworks.BeaconNetworkForRound(prevEpoch) +// ValidateTaskBeacons is a function that verifies dione task randomness +func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prevEntry types.BeaconEntry) error { + parentBeacon := beaconNetworks.BeaconNetworkForRound(t.DrandRound - 1) currBeacon := beaconNetworks.BeaconNetworkForRound(t.DrandRound) if parentBeacon != currBeacon { if len(t.BeaconEntries) != 2 { @@ -89,59 +90,22 @@ func BeaconEntriesForTask(ctx context.Context, beaconNetworks BeaconNetworks) ([ 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 + prevBeaconEntry := beacon.Entry(ctx, round-1) + res := <-prevBeaconEntry 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 + curBeaconEntry := beacon.Entry(ctx, round) + res = <-curBeaconEntry 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/drand/drand.go b/beacon/drand/drand.go similarity index 83% rename from drand/drand.go rename to beacon/drand/drand.go index 529f158..c1181ca 100644 --- a/drand/drand.go +++ b/beacon/drand/drand.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "sync" - "time" "github.com/Secured-Finance/dione/beacon" "github.com/drand/drand/chain" @@ -43,18 +42,15 @@ type DrandResponse struct { } type DrandBeacon struct { - DrandClient client.Client - PublicKey kyber.Point - Interval time.Duration - chainGenesisTime uint64 - chainRoundTime uint64 + DrandClient client.Client + PublicKey kyber.Point + drandResultChannel <-chan client.Result - drandGenesisTime uint64 - cacheLock sync.Mutex - localCache map[uint64]types.BeaconEntry + cacheLock sync.Mutex + localCache map[uint64]types.BeaconEntry } -func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub) (*DrandBeacon, error) { +func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { cfg := config.NewDrandConfig() drandChain, err := chain.InfoFromJSON(bytes.NewReader([]byte(cfg.ChainInfo))) @@ -98,14 +94,29 @@ func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub) (*DrandBeacon } db.PublicKey = drandChain.PublicKey - db.Interval = drandChain.Period - db.drandGenesisTime = uint64(drandChain.GenesisTime) - db.chainRoundTime = interval - db.chainGenesisTime = genesisTs + + db.drandResultChannel = db.DrandClient.Watch(context.TODO()) + + go db.loop(context.TODO()) return db, nil } +func (db *DrandBeacon) loop(ctx context.Context) { + for { + select { + case <-ctx.Done(): + { + return + } + case res := <-db.drandResultChannel: + { + db.cacheValue(types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()})) + } + } + } +} + func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan beacon.BeaconResult { out := make(chan beacon.BeaconResult, 1) if round != 0 { @@ -160,15 +171,11 @@ 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 { 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/node/node_dep_providers.go b/node/node_dep_providers.go index 6107361..ba4b7be 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + drand2 "github.com/Secured-Finance/dione/beacon/drand" + "github.com/libp2p/go-libp2p-core/protocol" gorpc "github.com/libp2p/go-libp2p-gorpc" @@ -17,7 +19,6 @@ import ( "github.com/Secured-Finance/dione/cache" "github.com/Secured-Finance/dione/config" "github.com/Secured-Finance/dione/consensus" - "github.com/Secured-Finance/dione/drand" "github.com/Secured-Finance/dione/ethclient" "github.com/Secured-Finance/dione/pubsub" "github.com/Secured-Finance/dione/types" @@ -61,7 +62,7 @@ func provideMiner(peerID peer.ID, ethAddress common.Address, beacon beacon.Beaco func provideBeacon(ps *pubsub2.PubSub) (beacon.BeaconNetworks, error) { networks := beacon.BeaconNetworks{} - bc, err := drand.NewDrandBeacon(config.ChainGenesis, config.TaskEpochInterval, ps) + bc, err := drand2.NewDrandBeacon(ps) if err != nil { return nil, fmt.Errorf("failed to setup drand beacon: %w", err) } diff --git a/types/beacon.go b/types/beacon.go index 567b1b9..b10bc9c 100644 --- a/types/beacon.go +++ b/types/beacon.go @@ -1,15 +1,17 @@ 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/task.go b/types/task.go index cfd1a82..29338c7 100644 --- a/types/task.go +++ b/types/task.go @@ -59,5 +59,3 @@ func NewDioneTask( } var tasksPerEpoch = NewInt(config.TasksPerEpoch) - -const sha256bits = 256 From 34c88a8d0190ad5674012742678ad3fc79ccd8a8 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sat, 5 Jun 2021 00:14:24 +0300 Subject: [PATCH 25/59] Fix LatestBeaconRound in Drand --- beacon/drand/drand.go | 54 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/beacon/drand/drand.go b/beacon/drand/drand.go index c1181ca..4dec731 100644 --- a/beacon/drand/drand.go +++ b/beacon/drand/drand.go @@ -29,25 +29,14 @@ 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 drandResultChannel <-chan client.Result - cacheLock sync.Mutex - localCache map[uint64]types.BeaconEntry + cacheLock sync.Mutex + localCache map[uint64]types.BeaconEntry + latestDrandRound uint64 } func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { @@ -96,12 +85,26 @@ func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { db.PublicKey = drandChain.PublicKey db.drandResultChannel = db.DrandClient.Watch(context.TODO()) - + err = db.getLatestDrandResult() + if err != nil { + return nil, err + } go db.loop(context.TODO()) return db, 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(newBeaconResultFromDrandResult(latestDround)) + db.updateLatestDrandRound(latestDround.Round()) + return nil +} + func (db *DrandBeacon) loop(ctx context.Context) { for { select { @@ -111,7 +114,8 @@ func (db *DrandBeacon) loop(ctx context.Context) { } case res := <-db.drandResultChannel: { - db.cacheValue(types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()})) + db.cacheValue(newBeaconResultFromDrandResult(res)) + db.updateLatestDrandRound(res.Round()) } } } @@ -163,6 +167,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 @@ -179,11 +189,13 @@ func (db *DrandBeacon) VerifyEntry(curr, prev types.BeaconEntry) error { } 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 newBeaconResultFromDrandResult(res client.Result) types.BeaconEntry { + return types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()}) } var _ beacon.BeaconAPI = (*DrandBeacon)(nil) From 012d4a68c3e3389cd021b3ae948940abfb1f2dff Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sat, 5 Jun 2021 00:18:06 +0300 Subject: [PATCH 26/59] Rename BlockPool to BlockChain --- blockchain/{pool => }/blockpool.go | 52 +++++++++++++++-------------- blockchain/sync/sync_mgr.go | 37 ++++---------------- blockchain/{pool => utils}/index.go | 2 +- node/network_service.go | 6 ++-- node/node.go | 6 ++-- node/node_dep_providers.go | 10 +++--- 6 files changed, 48 insertions(+), 65 deletions(-) rename blockchain/{pool => }/blockpool.go (77%) rename blockchain/{pool => utils}/index.go (99%) diff --git a/blockchain/pool/blockpool.go b/blockchain/blockpool.go similarity index 77% rename from blockchain/pool/blockpool.go rename to blockchain/blockpool.go index 9313e7e..28b6846 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/blockpool.go @@ -1,10 +1,12 @@ -package pool +package blockchain import ( "encoding/binary" "encoding/hex" "errors" + "github.com/Secured-Finance/dione/blockchain/utils" + types2 "github.com/Secured-Finance/dione/blockchain/types" "github.com/fxamacker/cbor/v2" @@ -23,15 +25,15 @@ var ( ErrLatestHeightNil = errors.New("latest block height is nil") ) -type BlockPool struct { +type BlockChain struct { dbEnv *lmdb.Env db lmdb.DBI - metadataIndex *Index - heightIndex *Index + metadataIndex *utils.Index + heightIndex *utils.Index } -func NewBlockPool(path string) (*BlockPool, error) { - pool := &BlockPool{} +func NewBlockChain(path string) (*BlockChain, error) { + chain := &BlockChain{} // configure lmdb env env, err := lmdb.NewEnv() @@ -49,7 +51,7 @@ func NewBlockPool(path string) (*BlockPool, error) { return nil, err } - pool.dbEnv = env + chain.dbEnv = env var dbi lmdb.DBI err = env.Update(func(txn *lmdb.Txn) error { @@ -60,25 +62,25 @@ func NewBlockPool(path string) (*BlockPool, error) { return nil, err } - pool.db = dbi + chain.db = dbi // create index instances - metadataIndex := NewIndex(DefaultMetadataIndexName, env, dbi) - heightIndex := NewIndex("height", env, dbi) - pool.metadataIndex = metadataIndex - pool.heightIndex = heightIndex + metadataIndex := utils.NewIndex(DefaultMetadataIndexName, env, dbi) + heightIndex := utils.NewIndex("height", env, dbi) + chain.metadataIndex = metadataIndex + chain.heightIndex = heightIndex - return pool, nil + return chain, nil } -func (bp *BlockPool) setLatestBlockHeight(height uint64) error { +func (bp *BlockChain) setLatestBlockHeight(height uint64) error { return bp.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height) } -func (bp *BlockPool) GetLatestBlockHeight() (uint64, error) { +func (bp *BlockChain) GetLatestBlockHeight() (uint64, error) { height, err := bp.metadataIndex.GetUint64([]byte(LatestBlockHeightKey)) if err != nil { - if err == ErrIndexKeyNotFound { + if err == utils.ErrIndexKeyNotFound { return 0, ErrLatestHeightNil } return 0, err @@ -86,7 +88,7 @@ func (bp *BlockPool) GetLatestBlockHeight() (uint64, error) { return height, nil } -func (bp *BlockPool) StoreBlock(block *types2.Block) error { +func (bp *BlockChain) StoreBlock(block *types2.Block) error { err := bp.dbEnv.Update(func(txn *lmdb.Txn) error { data, err := cbor.Marshal(block.Data) if err != nil { @@ -136,7 +138,7 @@ func (bp *BlockPool) StoreBlock(block *types2.Block) error { return nil } -func (bp *BlockPool) HasBlock(blockHash []byte) (bool, error) { +func (bp *BlockChain) HasBlock(blockHash []byte) (bool, error) { var blockExists bool err := bp.dbEnv.View(func(txn *lmdb.Txn) error { h := hex.EncodeToString(blockHash) @@ -157,7 +159,7 @@ func (bp *BlockPool) HasBlock(blockHash []byte) (bool, error) { return blockExists, nil } -func (bp *BlockPool) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) { +func (bp *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) { var data []*types2.Transaction err := bp.dbEnv.View(func(txn *lmdb.Txn) error { h := hex.EncodeToString(blockHash) @@ -177,7 +179,7 @@ func (bp *BlockPool) FetchBlockData(blockHash []byte) ([]*types2.Transaction, er return data, nil } -func (bp *BlockPool) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) { +func (bp *BlockChain) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) { var blockHeader types2.BlockHeader err := bp.dbEnv.View(func(txn *lmdb.Txn) error { h := hex.EncodeToString(blockHash) @@ -197,7 +199,7 @@ func (bp *BlockPool) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, er return &blockHeader, nil } -func (bp *BlockPool) FetchBlock(blockHash []byte) (*types2.Block, error) { +func (bp *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) { var block types2.Block header, err := bp.FetchBlockHeader(blockHash) if err != nil { @@ -214,12 +216,12 @@ func (bp *BlockPool) FetchBlock(blockHash []byte) (*types2.Block, error) { return &block, nil } -func (bp *BlockPool) FetchBlockByHeight(height uint64) (*types2.Block, error) { +func (bp *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) { var heightBytes []byte binary.LittleEndian.PutUint64(heightBytes, height) blockHash, err := bp.heightIndex.GetBytes(heightBytes) if err != nil { - if err == ErrIndexKeyNotFound { + if err == utils.ErrIndexKeyNotFound { return nil, ErrBlockNotFound } } @@ -230,12 +232,12 @@ func (bp *BlockPool) FetchBlockByHeight(height uint64) (*types2.Block, error) { return block, nil } -func (bp *BlockPool) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) { +func (bp *BlockChain) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) { var heightBytes []byte binary.LittleEndian.PutUint64(heightBytes, height) blockHash, err := bp.heightIndex.GetBytes(heightBytes) if err != nil { - if err == ErrIndexKeyNotFound { + if err == utils.ErrIndexKeyNotFound { return nil, ErrBlockNotFound } } diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index a33f752..8e88855 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -8,6 +8,8 @@ import ( "strings" "sync" + "github.com/Secured-Finance/dione/blockchain" + "github.com/Secured-Finance/dione/pubsub" "github.com/Secured-Finance/dione/consensus/policy" @@ -28,13 +30,10 @@ import ( gorpc "github.com/libp2p/go-libp2p-gorpc" ) -type SyncManager interface { - Start() - Stop() -} +type SyncManager interface{} type syncManager struct { - blockpool *pool.BlockPool + blockpool *blockchain.BlockChain mempool *pool.Mempool wg sync.WaitGroup ctx context.Context @@ -45,7 +44,7 @@ type syncManager struct { psb *pubsub.PubSubRouter } -func NewSyncManager(bp *pool.BlockPool, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID, psb *pubsub.PubSubRouter) SyncManager { +func NewSyncManager(bp *blockchain.BlockChain, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID, psb *pubsub.PubSubRouter) SyncManager { ctx, cancelFunc := context.WithCancel(context.Background()) sm := &syncManager{ blockpool: bp, @@ -63,34 +62,13 @@ func NewSyncManager(bp *pool.BlockPool, mp *pool.Mempool, p2pRPCClient *gorpc.Cl return sm } -func (sm *syncManager) Start() { - sm.wg.Add(1) - - err := sm.doInitialBlockPoolSync() - if err != nil { - logrus.Error(err) - } - - err = sm.doInitialMempoolSync() - if err != nil { - logrus.Error(err) - } - - go sm.syncLoop() -} - -func (sm *syncManager) Stop() { - sm.ctxCancelFunc() - sm.wg.Wait() -} - func (sm *syncManager) doInitialBlockPoolSync() error { if sm.initialSyncCompleted { return nil } ourLastHeight, err := sm.blockpool.GetLatestBlockHeight() - if err == pool.ErrLatestHeightNil { + if err == blockchain.ErrLatestHeightNil { gBlock := types2.GenesisBlock() err = sm.blockpool.StoreBlock(gBlock) // commit genesis block if err != nil { @@ -243,9 +221,6 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) error { return nil } -func (sm *syncManager) syncLoop() { -} - func (sm *syncManager) onNewTransaction(message *pubsub.GenericMessage) { tx, ok := message.Payload.(types2.Transaction) if !ok { diff --git a/blockchain/pool/index.go b/blockchain/utils/index.go similarity index 99% rename from blockchain/pool/index.go rename to blockchain/utils/index.go index 7d46f7f..ff3b074 100644 --- a/blockchain/pool/index.go +++ b/blockchain/utils/index.go @@ -1,4 +1,4 @@ -package pool +package utils import ( "encoding/binary" diff --git a/node/network_service.go b/node/network_service.go index deb93d8..a5afb42 100644 --- a/node/network_service.go +++ b/node/network_service.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" + "github.com/Secured-Finance/dione/blockchain" + gorpc "github.com/libp2p/go-libp2p-gorpc" "github.com/sirupsen/logrus" @@ -16,12 +18,12 @@ import ( ) type NetworkService struct { - blockpool *pool.BlockPool + blockpool *blockchain.BlockChain mempool *pool.Mempool rpcClient *gorpc.Client } -func NewNetworkService(bp *pool.BlockPool) *NetworkService { +func NewNetworkService(bp *blockchain.BlockChain) *NetworkService { return &NetworkService{ blockpool: bp, } diff --git a/node/node.go b/node/node.go index 9675405..923a4dd 100644 --- a/node/node.go +++ b/node/node.go @@ -9,6 +9,8 @@ import ( "os" "time" + "github.com/Secured-Finance/dione/blockchain" + types2 "github.com/Secured-Finance/dione/blockchain/types" "github.com/fxamacker/cbor/v2" @@ -63,7 +65,7 @@ type Node struct { Wallet *wallet.LocalWallet Cache cache.Cache DisputeManager *consensus.DisputeManager - BlockPool *pool.BlockPool + BlockPool *blockchain.BlockChain MemPool *pool.Mempool SyncManager sync.SyncManager NetworkService *NetworkService @@ -139,7 +141,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim // == initialize blockchain modules // initialize blockpool database - bp, err := provideBlockPool(n.Config) + bp, err := provideBlockChain(n.Config) if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) } diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index ba4b7be..c47bfd6 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "github.com/Secured-Finance/dione/blockchain" + drand2 "github.com/Secured-Finance/dione/beacon/drand" "github.com/libp2p/go-libp2p-core/protocol" @@ -152,15 +154,15 @@ func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host, pexDiscover return pexDiscovery, nil } -func provideBlockPool(config *config.Config) (*pool.BlockPool, error) { - return pool.NewBlockPool(config.Blockchain.DatabasePath) +func provideBlockChain(config *config.Config) (*blockchain.BlockChain, error) { + return blockchain.NewBlockChain(config.Blockchain.DatabasePath) } func provideMemPool() (*pool.Mempool, error) { return pool.NewMempool() } -func provideSyncManager(bp *pool.BlockPool, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { +func provideSyncManager(bp *blockchain.BlockChain, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { addr, err := peer.AddrInfoFromP2pAddr(bootstrap) if err != nil { return nil, err @@ -172,6 +174,6 @@ func provideP2PRPCClient(h host.Host) *gorpc.Client { return gorpc.NewClient(h, DioneProtocolID) } -func provideNetworkService(bp *pool.BlockPool) *NetworkService { +func provideNetworkService(bp *blockchain.BlockChain) *NetworkService { return NewNetworkService(bp) } From 4e7294e0469761587c88d4e333cd9632db6807a2 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 9 Jun 2021 00:30:23 +0300 Subject: [PATCH 27/59] Do massive overhaul of consensus part due to new architecture with blockchain, implement chain selection --- beacon/beacon.go | 4 +- beacon/drand/drand.go | 21 ++- blockchain/pool/blockpool.go | 57 ++++++ blockchain/types/block.go | 24 ++- config/drand.go | 2 + config/tasks.go | 9 - config/win_config.go | 5 + consensus/consensus.go | 273 +++++++++++++++++---------- consensus/consensus_validator.go | 246 +++++++++++------------- consensus/dispute_manager.go | 178 +++++++++-------- consensus/miner.go | 130 +++++-------- consensus/msg_log.go | 34 ++-- consensus/types/consensus_message.go | 25 +++ consensus/types/message.go | 26 +-- consensus/utils.go | 92 ++++----- ethclient/stake.go | 25 +-- go.mod | 1 + go.sum | 2 + node/node.go | 78 +++----- node/node_dep_providers.go | 14 +- store/stake.go | 96 ---------- store/store.go | 84 ++++----- types/bigint.go | 32 ---- types/electionproof.go | 14 +- types/task.go | 27 +-- types/ticket.go | 21 --- 26 files changed, 712 insertions(+), 808 deletions(-) create mode 100644 blockchain/pool/blockpool.go delete mode 100644 config/tasks.go create mode 100644 config/win_config.go create mode 100644 consensus/types/consensus_message.go delete mode 100644 store/stake.go delete mode 100644 types/bigint.go delete mode 100644 types/ticket.go diff --git a/beacon/beacon.go b/beacon/beacon.go index f735319..d388340 100644 --- a/beacon/beacon.go +++ b/beacon/beacon.go @@ -17,7 +17,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 +28,7 @@ func (bn BeaconNetworks) BeaconNetworkForRound(e types.DrandRound) BeaconAPI { } type BeaconNetwork struct { - Start types.DrandRound + Start uint64 Beacon BeaconAPI } diff --git a/beacon/drand/drand.go b/beacon/drand/drand.go index 4dec731..1c4b493 100644 --- a/beacon/drand/drand.go +++ b/beacon/drand/drand.go @@ -6,6 +6,10 @@ import ( "fmt" "sync" + "github.com/Arceliar/phony" + + "github.com/Secured-Finance/dione/consensus" + "github.com/Secured-Finance/dione/beacon" "github.com/drand/drand/chain" "github.com/drand/drand/client" @@ -30,16 +34,17 @@ var log = logrus.WithFields(logrus.Fields{ }) type DrandBeacon struct { + phony.Inbox DrandClient client.Client PublicKey kyber.Point drandResultChannel <-chan client.Result - - cacheLock sync.Mutex - localCache map[uint64]types.BeaconEntry - latestDrandRound uint64 + cacheLock sync.Mutex + localCache map[uint64]types.BeaconEntry + latestDrandRound uint64 + consensusManager *consensus.PBFTConsensusManager } -func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { +func NewDrandBeacon(ps *pubsub.PubSub, pcm *consensus.PBFTConsensusManager) (*DrandBeacon, error) { cfg := config.NewDrandConfig() drandChain, err := chain.InfoFromJSON(bytes.NewReader([]byte(cfg.ChainInfo))) @@ -78,8 +83,9 @@ func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { } db := &DrandBeacon{ - DrandClient: drandClient, - localCache: make(map[uint64]types.BeaconEntry), + DrandClient: drandClient, + localCache: make(map[uint64]types.BeaconEntry), + consensusManager: pcm, } db.PublicKey = drandChain.PublicKey @@ -116,6 +122,7 @@ func (db *DrandBeacon) loop(ctx context.Context) { { db.cacheValue(newBeaconResultFromDrandResult(res)) db.updateLatestDrandRound(res.Round()) + db.consensusManager.NewDrandRound(db, res) } } } diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go new file mode 100644 index 0000000..5727464 --- /dev/null +++ b/blockchain/pool/blockpool.go @@ -0,0 +1,57 @@ +package pool + +import ( + "encoding/hex" + + "github.com/Secured-Finance/dione/blockchain/types" + "github.com/Secured-Finance/dione/cache" +) + +// BlockPool is pool for blocks that isn't not validated or committed yet +type BlockPool struct { + knownBlocks cache.Cache + acceptedBlocks cache.Cache +} + +func NewBlockPool() (*BlockPool, error) { + bp := &BlockPool{ + acceptedBlocks: cache.NewInMemoryCache(), // here we need to use separate cache + } + + return bp, nil +} + +func (bp *BlockPool) AddBlock(block *types.Block) error { + return bp.knownBlocks.Store(hex.EncodeToString(block.Header.Hash), block) +} + +func (bp *BlockPool) GetBlock(blockhash []byte) (*types.Block, error) { + var block *types.Block + return block, bp.knownBlocks.Get(hex.EncodeToString(blockhash), &block) +} + +// PruneBlocks cleans known blocks list. It is called when new consensus round starts. +func (bp *BlockPool) PruneBlocks() { + for k := range bp.knownBlocks.Items() { + bp.knownBlocks.Delete(k) + } +} + +func (bp *BlockPool) AddAcceptedBlock(block *types.Block) error { + return bp.acceptedBlocks.Store(hex.EncodeToString(block.Header.Hash), block) +} + +func (bp *BlockPool) GetAllAcceptedBlocks() []*types.Block { + var blocks []*types.Block + for _, v := range bp.acceptedBlocks.Items() { + blocks = append(blocks, v.(*types.Block)) + } + return blocks +} + +// PruneAcceptedBlocks cleans accepted blocks list. It is called when new consensus round starts. +func (bp *BlockPool) PruneAcceptedBlocks() { + for k := range bp.acceptedBlocks.Items() { + bp.acceptedBlocks.Delete(k) + } +} diff --git a/blockchain/types/block.go b/blockchain/types/block.go index c7e9495..c48f148 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -3,10 +3,13 @@ package types import ( "time" + "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/Secured-Finance/dione/wallet" "github.com/libp2p/go-libp2p-core/peer" ) @@ -22,6 +25,7 @@ type BlockHeader struct { LastHash []byte LastHashProof *merkletree.Proof Proposer peer.ID + ProposerEth common.Address Signature []byte } @@ -36,12 +40,8 @@ func GenesisBlock() *Block { } } -func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *wallet.LocalWallet) (*Block, error) { +func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth common.Address, privateKey crypto.PrivKey) (*Block, error) { timestamp := time.Now().Unix() - proposer, err := wallet.GetDefault() - if err != nil { - return nil, err - } // extract hashes from transactions var txHashes [][]byte @@ -58,8 +58,8 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *walle // fetch merkle tree root hash (block hash) blockHash := tree.Root() - // sign this block hash - s, err := wallet.Sign(proposer, blockHash) + // sign the block hash + s, err := privateKey.Sign(blockHash) if err != nil { return nil, err } @@ -69,12 +69,18 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, wallet *walle return nil, err } + proposer, err := peer.IDFromPrivateKey(privateKey) + if err != nil { + return nil, err + } + block := &Block{ Header: &BlockHeader{ Timestamp: timestamp, Height: lastBlockHeader.Height + 1, Proposer: proposer, - Signature: s.Data, + ProposerEth: minerEth, + Signature: s, Hash: blockHash, LastHash: lastBlockHeader.Hash, LastHashProof: lastHashProof, 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..4a624b2 --- /dev/null +++ b/config/win_config.go @@ -0,0 +1,5 @@ +package config + +import "math/big" + +var ExpectedLeadersPerEpoch = big.NewInt(5) diff --git a/consensus/consensus.go b/consensus/consensus.go index 1aff9d1..9139890 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -1,11 +1,21 @@ package consensus import ( - "fmt" + "errors" "math/big" "sync" - "github.com/Secured-Finance/dione/cache" + "github.com/Secured-Finance/dione/blockchain" + + "github.com/drand/drand/client" + + "github.com/Arceliar/phony" + + 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" @@ -13,65 +23,87 @@ import ( "github.com/sirupsen/logrus" "github.com/Secured-Finance/dione/pubsub" - types2 "github.com/Secured-Finance/dione/types" +) + +type StateStatus uint8 + +const ( + StateStatusUnknown = iota + + StateStatusPrePrepared + StateStatusPrepared + StateStatusCommited ) type PBFTConsensusManager struct { + phony.Inbox psb *pubsub.PubSubRouter - minApprovals int - privKey []byte - msgLog *MessageLog + minApprovals int // FIXME + privKey crypto.PrivKey + msgLog *ConsensusMessageLog validator *ConsensusValidator - consensusMap map[string]*Consensus ethereumClient *ethclient.EthereumClient miner *Miner - cache cache.Cache + blockPool pool.BlockPool + blockchain blockchain.BlockChain + state *State } -type Consensus struct { - mutex sync.Mutex - Finished bool - IsCurrentMinerLeader bool - Task *types2.DioneTask +type State struct { + mutex sync.Mutex + drandRound uint64 + randomness []byte + blockHeight uint64 + status StateStatus } -func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey []byte, ethereumClient *ethclient.EthereumClient, miner *Miner, evc cache.Cache) *PBFTConsensusManager { +func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey crypto.PrivKey, ethereumClient *ethclient.EthereumClient, miner *Miner) *PBFTConsensusManager { pcm := &PBFTConsensusManager{} pcm.psb = psb pcm.miner = miner - pcm.validator = NewConsensusValidator(evc, miner) - pcm.msgLog = NewMessageLog() + pcm.validator = NewConsensusValidator(miner) + pcm.msgLog = NewConsensusMessageLog() pcm.minApprovals = minApprovals pcm.privKey = privKey pcm.ethereumClient = ethereumClient - pcm.cache = evc - pcm.consensusMap = map[string]*Consensus{} - pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types2.DioneTask{}) - pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types2.DioneTask{}) - pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types2.DioneTask{}) + pcm.state = &State{} + pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types.PrePrepareMessage{}) + pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types.PrepareMessage{}) + pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types.CommitMessage{}) 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 { + pcm.state.mutex.Lock() + defer pcm.state.mutex.Unlock() + prePrepareMsg, err := NewMessage(types.ConsensusMessage{Block: blk}, types.ConsensusMessageTypePrePrepare, pcm.privKey) if err != nil { return err } pcm.psb.BroadcastToServiceTopic(prePrepareMsg) + pcm.state.status = StateStatusPrePrepared return nil } func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage) { - cmsg, err := unmarshalPayload(message) - if err != nil { + pcm.state.mutex.Lock() + defer pcm.state.mutex.Unlock() + prePrepare, ok := message.Payload.(types.PrePrepareMessage) + if !ok { + logrus.Warn("failed to convert payload to PrePrepare message") return } - if cmsg.Task.Miner == pcm.miner.address { + if prePrepare.Block.Header.Proposer == pcm.miner.address { return } + + cmsg := types.ConsensusMessage{ + Type: types.ConsensusMessageTypePrePrepare, + From: message.From, + Block: prePrepare.Block, + } + if pcm.msgLog.Exists(cmsg) { logrus.Debugf("received existing pre_prepare msg, dropping...") return @@ -82,21 +114,43 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage } pcm.msgLog.AddMessage(cmsg) + pcm.blockPool.AddBlock(cmsg.Block) - prepareMsg, err := NewMessage(message, pubsub.PrepareMessageType) + prepareMsg, err := NewMessage(cmsg, types.ConsensusMessageTypePrepare, pcm.privKey) if err != nil { logrus.Errorf("failed to create prepare message: %v", err) return } - pcm.createConsensusInfo(&cmsg.Task, false) - - pcm.psb.BroadcastToServiceTopic(&prepareMsg) + pcm.psb.BroadcastToServiceTopic(prepareMsg) + pcm.state.status = StateStatusPrePrepared } func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { - cmsg, err := unmarshalPayload(message) + pcm.state.mutex.Lock() + defer pcm.state.mutex.Unlock() + prepare, ok := message.Payload.(types.PrepareMessage) + if !ok { + logrus.Warn("failed to convert payload to Prepare message") + return + } + + cmsg := types.ConsensusMessage{ + Type: types.ConsensusMessageTypePrepare, + From: message.From, + Blockhash: prepare.Blockhash, + Signature: prepare.Signature, // TODO check the signature + } + + pk, _ := message.From.ExtractPublicKey() + ok, err := pk.Verify(cmsg.Blockhash, cmsg.Signature) if err != nil { + logrus.Warnf("Failed to verify PREPARE message signature: %s", err.Error()) + return + } + + if !ok { + logrus.Errorf("Signature of PREPARE message of peer %s isn't valid!", cmsg.From) return } @@ -111,18 +165,41 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { pcm.msgLog.AddMessage(cmsg) - if len(pcm.msgLog.Get(types.MessageTypePrepare, cmsg.Task.ConsensusID)) >= pcm.minApprovals { - commitMsg, err := NewMessage(message, pubsub.CommitMessageType) + if len(pcm.msgLog.Get(types.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= pcm.minApprovals { + commitMsg, err := NewMessage(cmsg, types.ConsensusMessageTypeCommit, pcm.privKey) if err != nil { - logrus.Errorf("failed to create commit message: %w", err) + logrus.Errorf("failed to create commit message: %v", err) } - pcm.psb.BroadcastToServiceTopic(&commitMsg) + pcm.psb.BroadcastToServiceTopic(commitMsg) + pcm.state.status = StateStatusPrepared } } func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { - cmsg, err := unmarshalPayload(message) + pcm.state.mutex.Lock() + defer pcm.state.mutex.Unlock() + commit, ok := message.Payload.(types.CommitMessage) + if !ok { + logrus.Warn("failed to convert payload to Prepare message") + return + } + + cmsg := types.ConsensusMessage{ + Type: types.ConsensusMessageTypeCommit, + From: message.From, + Blockhash: commit.Blockhash, + Signature: commit.Signature, // TODO check the signature + } + + pk, _ := message.From.ExtractPublicKey() + ok, err := pk.Verify(cmsg.Blockhash, cmsg.Signature) if err != nil { + logrus.Warnf("Failed to verify COMMIT message signature: %s", err.Error()) + return + } + + if !ok { + logrus.Errorf("Signature of COMMIT message of peer %s isn't valid!", cmsg.From) return } @@ -137,80 +214,70 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { pcm.msgLog.AddMessage(cmsg) - if len(pcm.msgLog.Get(types.MessageTypeCommit, cmsg.Task.ConsensusID)) >= pcm.minApprovals { - info := pcm.GetConsensusInfo(cmsg.Task.ConsensusID) - if info == nil { - logrus.Debugf("consensus doesn't exist in our consensus map - skipping...") + if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals { + block, err := pcm.blockPool.GetBlock(cmsg.Blockhash) + if err != nil { + logrus.Debug(err) 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", cmsg.Task.ConsensusID) - reqID, ok := new(big.Int).SetString(cmsg.Task.RequestID, 10) - if !ok { - logrus.Errorf("Failed to parse request ID: %v", cmsg.Task.RequestID) - } + pcm.blockPool.AddAcceptedBlock(block) + pcm.state.status = StateStatusCommited + } +} - err := pcm.ethereumClient.SubmitRequestAnswer(reqID, cmsg.Task.Payload) +func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Result) { + pcm.Act(from, func() { + pcm.state.mutex.Lock() + defer pcm.state.mutex.Unlock() + block, err := pcm.commitAcceptedBlocks() + if err != nil { + logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) + return + } + + minedBlock, err := pcm.miner.MineBlock(res.Randomness(), res.Round(), block.Header) + if err != nil { + logrus.Errorf("Failed to mine the block: %s", err.Error()) + return + } + + pcm.state.drandRound = res.Round() + pcm.state.randomness = res.Randomness() + pcm.state.blockHeight = pcm.state.blockHeight + 1 + + // if we are round winner + if minedBlock != nil { + err = pcm.propose(minedBlock) if err != nil { - logrus.Errorf("Failed to submit on-chain result: %v", err) + logrus.Errorf("Failed to propose the block: %s", err.Error()) + return } } - - 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) commitAcceptedBlocks() (*types3.Block, error) { + blocks := pcm.blockPool.GetAllAcceptedBlocks() + if blocks == nil { + return nil, errors.New("there is no accepted blocks") } -} + var maxStake *big.Int + var selectedBlock *types3.Block + for _, v := range blocks { + stake, err := pcm.ethereumClient.GetMinerStake(v.Header.ProposerEth) + if err != nil { + return nil, err + } -func (pcm *PBFTConsensusManager) GetConsensusInfo(consensusID string) *Consensus { - c, ok := pcm.consensusMap[consensusID] - if !ok { - return nil + if maxStake != nil { + if stake.Cmp(maxStake) == -1 { + continue + } + } + maxStake = stake + selectedBlock = v } - - return c -} - -func unmarshalPayload(msg *pubsub.GenericMessage) (types.ConsensusMessage, error) { - task, ok := msg.Payload.(types2.DioneTask) - if !ok { - return types.ConsensusMessage{}, fmt.Errorf("cannot convert payload to DioneTask") - } - var consensusMessageType types.MessageType - switch msg.Type { - case pubsub.PrePrepareMessageType: - { - consensusMessageType = types.MessageTypePrePrepare - break - } - case pubsub.PrepareMessageType: - { - consensusMessageType = types.MessageTypePrepare - break - } - case pubsub.CommitMessageType: - { - consensusMessageType = types.MessageTypeCommit - break - } - } - cmsg := types.ConsensusMessage{ - Type: consensusMessageType, - From: msg.From, - Task: task, - } - return cmsg, nil + logrus.Debugf("Selected block of miner %s", selectedBlock.Header.ProposerEth.Hex()) + pcm.blockPool.PruneAcceptedBlocks() + return selectedBlock, pcm.blockchain.StoreBlock(selectedBlock) } diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index b6d16d5..d9d28fd 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -1,153 +1,127 @@ 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/contracts/dioneOracle" - "github.com/Secured-Finance/dione/types" - "github.com/ethereum/go-ethereum/common" - "github.com/filecoin-project/go-state-types/crypto" - "github.com/sirupsen/logrus" ) type ConsensusValidator struct { - validationFuncMap map[types2.MessageType]func(msg types2.ConsensusMessage) bool - cache cache.Cache + validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool miner *Miner } -func NewConsensusValidator(ec cache.Cache, miner *Miner) *ConsensusValidator { +func NewConsensusValidator(miner *Miner) *ConsensusValidator { cv := &ConsensusValidator{ - cache: ec, miner: miner, } - cv.validationFuncMap = map[types2.MessageType]func(msg types2.ConsensusMessage) bool{ - types2.MessageTypePrePrepare: func(msg types2.ConsensusMessage) bool { - // TODO here we need to do validation of tx itself - - // === verify task signature === - err := VerifyTaskSignature(msg.Task) - if err != nil { - logrus.Errorf("unable to verify signature: %v", err) - return false - } - ///////////////////////////////// - - // === verify if request exists in cache === - var requestEvent *dioneOracle.DioneOracleNewOracleRequest - err = cv.cache.Get("request_"+msg.Task.RequestID, &requestEvent) - if err != nil { - logrus.Errorf("the request doesn't exist in the cache or has been failed to decode: %v", err) - return false - } - - if requestEvent.OriginChain != msg.Task.OriginChain || - requestEvent.RequestType != msg.Task.RequestType || - requestEvent.RequestParams != msg.Task.RequestParams { - - logrus.Errorf("the incoming task and cached request requestEvent don't match!") - return false - } - ///////////////////////////////// - - // === verify election proof wincount preliminarily === - if msg.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(msg.Task.MinerEth)) - if err != nil { - logrus.Errorf("miner is not eligible to propose task: %v", err) - return false - } - ///////////////////////////////// - - // === verify election proof vrf === - minerAddressMarshalled, err := msg.Task.Miner.MarshalBinary() - if err != nil { - logrus.Errorf("failed to marshal miner address: %v", err) - return false - } - electionProofRandomness, err := DrawRandomness( - msg.Task.BeaconEntries[1].Data, - crypto.DomainSeparationTag_ElectionProofProduction, - msg.Task.DrandRound, - minerAddressMarshalled, - ) - if err != nil { - logrus.Errorf("failed to draw electionProofRandomness: %v", err) - return false - } - err = VerifyVRF(msg.Task.Miner, electionProofRandomness, msg.Task.ElectionProof.VRFProof) - if err != nil { - logrus.Errorf("failed to verify election proof vrf: %v", err) - } - ////////////////////////////////////// - - // === verify ticket vrf === - ticketRandomness, err := DrawRandomness( - msg.Task.BeaconEntries[1].Data, - crypto.DomainSeparationTag_TicketProduction, - msg.Task.DrandRound-types.TicketRandomnessLookback, - minerAddressMarshalled, - ) - if err != nil { - logrus.Errorf("failed to draw ticket electionProofRandomness: %v", err) - return false - } - - err = VerifyVRF(msg.Task.Miner, ticketRandomness, msg.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(msg.Task.MinerEth)) - if err != nil { - logrus.Errorf("failed to get miner stake: %v", err) - return false - } - actualWinCount := msg.Task.ElectionProof.ComputeWinCount(*mStake, *nStake) - if msg.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(msg.Task.OriginChain, msg.Task.RequestType); validationFunc != nil { - err := validationFunc(msg.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!", msg.Task.OriginChain, msg.Task.RequestType) - } - ///////////////////////////////// - - return true - }, - types2.MessageTypePrepare: func(msg types2.ConsensusMessage) bool { - err := VerifyTaskSignature(msg.Task) - if err != nil { - return false - } - return true - }, - types2.MessageTypeCommit: func(msg types2.ConsensusMessage) bool { - err := VerifyTaskSignature(msg.Task) - if err != nil { - return false - } - return true - }, + cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool{ + // FIXME it all + //types2.ConsensusMessageTypePrePrepare: func(msg types2.PrePrepareMessage) bool { + // // TODO here we need to do validation of block itself + // + // // === verify task signature === + // err := VerifyTaskSignature(msg.Task) + // if err != nil { + // logrus.Errorf("unable to verify signature: %v", err) + // return false + // } + // ///////////////////////////////// + // + // // === verify if request exists in cache === + // var requestEvent *dioneOracle.DioneOracleNewOracleRequest + // err = cv.cache.Get("request_"+msg.Task.RequestID, &requestEvent) + // if err != nil { + // logrus.Errorf("the request doesn't exist in the cache or has been failed to decode: %v", err) + // return false + // } + // + // if requestEvent.OriginChain != msg.Task.OriginChain || + // requestEvent.RequestType != msg.Task.RequestType || + // requestEvent.RequestParams != msg.Task.RequestParams { + // + // logrus.Errorf("the incoming task and cached request requestEvent don't match!") + // return false + // } + // ///////////////////////////////// + // + // // === verify election proof wincount preliminarily === + // if msg.Task.ElectionProof.WinCount < 1 { + // logrus.Error("miner isn't a winner!") + // return false + // } + // ///////////////////////////////// + // + // // === verify miner's eligibility to propose this task === + // err = cv.miner.IsMinerEligibleToProposeBlock(common.HexToAddress(msg.Task.MinerEth)) + // if err != nil { + // logrus.Errorf("miner is not eligible to propose task: %v", err) + // return false + // } + // ///////////////////////////////// + // + // // === verify election proof vrf === + // minerAddressMarshalled, err := msg.Task.Miner.MarshalBinary() + // if err != nil { + // logrus.Errorf("failed to marshal miner address: %v", err) + // return false + // } + // electionProofRandomness, err := DrawRandomness( + // msg.Task.BeaconEntries[1].Data, + // crypto.DomainSeparationTag_ElectionProofProduction, + // msg.Task.DrandRound, + // minerAddressMarshalled, + // ) + // if err != nil { + // logrus.Errorf("failed to draw electionProofRandomness: %v", err) + // return false + // } + // err = VerifyVRF(msg.Task.Miner, electionProofRandomness, msg.Task.ElectionProof.VRFProof) + // if err != nil { + // logrus.Errorf("failed to verify election proof vrf: %v", err) + // } + // ////////////////////////////////////// + // + // // === compute wincount locally and verify values === + // mStake, nStake, err := cv.miner.GetStakeInfo(common.HexToAddress(msg.Task.MinerEth)) + // if err != nil { + // logrus.Errorf("failed to get miner stake: %v", err) + // return false + // } + // actualWinCount := msg.Task.ElectionProof.ComputeWinCount(*mStake, *nStake) + // if msg.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(msg.Task.OriginChain, msg.Task.RequestType); validationFunc != nil { + // err := validationFunc(msg.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!", msg.Task.OriginChain, msg.Task.RequestType) + // } + // ///////////////////////////////// + // + // return true + //}, + //types2.ConsensusMessageTypePrepare: func(msg types2.PrePrepareMessage) bool { + // err := VerifyTaskSignature(msg.Task) + // if err != nil { + // return false + // } + // return true + //}, + //types2.ConsensusMessageTypeCommit: func(msg types2.PrePrepareMessage) bool { + // err := VerifyTaskSignature(msg.Task) + // if err != nil { + // return false + // } + // return true + //}, } return cv diff --git a/consensus/dispute_manager.go b/consensus/dispute_manager.go index f967589..70bfb7b 100644 --- a/consensus/dispute_manager.go +++ b/consensus/dispute_manager.go @@ -2,17 +2,11 @@ package consensus import ( "context" - "encoding/hex" "time" - "math/big" - "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 { @@ -69,92 +63,94 @@ func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, } func (dm *DisputeManager) onNewSubmission(submittion *dioneOracle.DioneOracleSubmittedOracleRequest) { - c := dm.pcm.GetConsensusInfo(submittion.ReqID.String()) - if c == nil { - // todo: warn - return - } - - dm.submissionMap[submittion.ReqID.String()] = submittion - - submHashBytes := sha3.Sum256(submittion.Data) - localHashBytes := sha3.Sum256(c.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) - if err != nil { - logrus.Errorf(err.Error()) - return - } - disputeFinishTimer := time.NewTimer(dm.voteWindow) - go func() { - for { - select { - case <-dm.ctx.Done(): - return - case <-disputeFinishTimer.C: - { - d, ok := dm.disputeMap[reqID.String()] - if !ok { - logrus.Error("cannot finish dispute: it doesn't exist in manager's dispute map!") - return - } - err := dm.ethClient.FinishDispute(d.Dhash) - if err != nil { - logrus.Errorf(err.Error()) - return - } - disputeFinishTimer.Stop() - return - } - } - } - }() - } + //c := dm.pcm.GetConsensusInfo(submittion.ReqID.String()) + //if c == nil { + // // todo: warn + // return + //} + // + //dm.submissionMap[submittion.ReqID.String()] = submittion + // + //submHashBytes := sha3.Sum256(submittion.Data) + //localHashBytes := sha3.Sum256(c.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) + // if err != nil { + // logrus.Errorf(err.Error()) + // return + // } + // disputeFinishTimer := time.NewTimer(dm.voteWindow) + // go func() { + // for { + // select { + // case <-dm.ctx.Done(): + // return + // case <-disputeFinishTimer.C: + // { + // d, ok := dm.disputeMap[reqID.String()] + // if !ok { + // logrus.Error("cannot finish dispute: it doesn't exist in manager's dispute map!") + // return + // } + // err := dm.ethClient.FinishDispute(d.Dhash) + // if err != nil { + // logrus.Errorf(err.Error()) + // return + // } + // disputeFinishTimer.Stop() + // return + // } + // } + // } + // }() + //} + // TODO refactor due to new architecture with blockchain } func (dm *DisputeManager) onNewDispute(dispute *dioneDispute.DioneDisputeNewDispute) { - c := dm.pcm.GetConsensusInfo(dispute.RequestID.String()) - if c == nil { - // todo: warn - return - } - - subm, ok := dm.submissionMap[dispute.RequestID.String()] - if !ok { - // todo: warn - return - } - - dm.disputeMap[dispute.RequestID.String()] = dispute - - if dispute.DisputeInitiator.Hex() == dm.ethClient.GetEthAddress().Hex() { - return - } - - submHashBytes := sha3.Sum256(subm.Data) - localHashBytes := sha3.Sum256(c.Task.Payload) - submHash := hex.EncodeToString(submHashBytes[:]) - localHash := hex.EncodeToString(localHashBytes[:]) - if submHash == localHash { - err := dm.ethClient.VoteDispute(dispute.Dhash, false) - if err != nil { - logrus.Errorf(err.Error()) - return - } - } - - err := dm.ethClient.VoteDispute(dispute.Dhash, true) - if err != nil { - logrus.Errorf(err.Error()) - return - } + //c := dm.pcm.GetConsensusInfo(dispute.RequestID.String()) + //if c == nil { + // // todo: warn + // return + //} + // + //subm, ok := dm.submissionMap[dispute.RequestID.String()] + //if !ok { + // // todo: warn + // return + //} + // + //dm.disputeMap[dispute.RequestID.String()] = dispute + // + //if dispute.DisputeInitiator.Hex() == dm.ethClient.GetEthAddress().Hex() { + // return + //} + // + //submHashBytes := sha3.Sum256(subm.Data) + //localHashBytes := sha3.Sum256(c.Task.Payload) + //submHash := hex.EncodeToString(submHashBytes[:]) + //localHash := hex.EncodeToString(localHashBytes[:]) + //if submHash == localHash { + // err := dm.ethClient.VoteDispute(dispute.Dhash, false) + // if err != nil { + // logrus.Errorf(err.Error()) + // return + // } + //} + // + //err := dm.ethClient.VoteDispute(dispute.Dhash, true) + //if err != nil { + // logrus.Errorf(err.Error()) + // return + //} + // TODO refactor due to new architecture with blockchain } diff --git a/consensus/miner.go b/consensus/miner.go index c7247bf..29313c6 100644 --- a/consensus/miner.go +++ b/consensus/miner.go @@ -1,25 +1,23 @@ package consensus import ( - "context" + "errors" + "fmt" + "math/big" "sync" - big2 "github.com/filecoin-project/go-state-types/big" + "github.com/Secured-Finance/dione/blockchain/pool" - "github.com/Secured-Finance/dione/sigs" + "github.com/libp2p/go-libp2p-core/crypto" - "github.com/Secured-Finance/dione/rpc" + types2 "github.com/Secured-Finance/dione/blockchain/types" "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 { @@ -28,9 +26,10 @@ type Miner struct { mutex sync.Mutex beacon beacon.BeaconNetworks ethClient *ethclient.EthereumClient - minerStake types.BigInt - networkStake types.BigInt - privateKey []byte + minerStake *big.Int + networkStake *big.Int + privateKey crypto.PrivKey + mempool *pool.Mempool } func NewMiner( @@ -38,7 +37,8 @@ func NewMiner( ethAddress common.Address, beacon beacon.BeaconNetworks, ethClient *ethclient.EthereumClient, - privateKey []byte, + privateKey crypto.PrivKey, + mempool *pool.Mempool, ) *Miner { return &Miner{ address: address, @@ -46,6 +46,7 @@ func NewMiner( beacon: beacon, ethClient: ethClient, privateKey: privateKey, + mempool: mempool, } } @@ -64,13 +65,13 @@ func (m *Miner) UpdateCurrentStakeInfo() error { return err } - m.minerStake = *mStake - m.networkStake = *nStake + m.minerStake = mStake + m.networkStake = nStake return nil } -func (m *Miner) GetStakeInfo(miner common.Address) (*types.BigInt, *types.BigInt, error) { +func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, error) { mStake, err := m.ethClient.GetMinerStake(miner) if err != nil { @@ -88,100 +89,59 @@ func (m *Miner) GetStakeInfo(miner common.Address) (*types.BigInt, *types.BigInt 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] +func (m *Miner) MineBlock(randomness []byte, drandRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) { + logrus.Debug("attempting to mine the block at epoch: ", drandRound) 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) + return nil, fmt.Errorf("failed to update miner stake: %w", err) } winner, err := IsRoundWinner( - types.DrandRound(randomBase.Round), + drandRound, m.address, - randomBase, + randomness, m.minerStake, m.networkStake, - func(id peer.ID, bytes []byte) (*types.Signature, error) { - return sigs.Sign(types.SigTypeEd25519, m.privateKey, bytes) - }, + m.privateKey, ) if err != nil { - return nil, xerrors.Errorf("failed to check if we win next round: %w", err) + return nil, fmt.Errorf("failed to check if we winned in 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) + // TODO get rpc responses for oracle requests + //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) + //} + + txs := m.mempool.GetAllTransactions() + if txs == nil { + return nil, fmt.Errorf("there is no txes for processing") // skip new consensus round because there is no transaction for processing } - 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 + newBlock, err := types2.CreateBlock(lastBlockHeader, txs, m.ethAddress, m.privateKey) + if err != nil { + return nil, fmt.Errorf("failed to create new block: %w", err) + } + + return newBlock, 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 { +func (m *Miner) IsMinerEligibleToProposeBlock(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") + if mStake.Cmp(big.NewInt(ethclient.MinMinerStake)) == -1 { + return errors.New("miner doesn't have enough staked tokens") } return nil } diff --git a/consensus/msg_log.go b/consensus/msg_log.go index 3f0bac7..77a57aa 100644 --- a/consensus/msg_log.go +++ b/consensus/msg_log.go @@ -1,18 +1,19 @@ 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.ConsensusMessage) +type ConsensusMessageLog struct { + messages mapset.Set + maxLogSize int } -func NewMessageLog() *MessageLog { - msgLog := &MessageLog{ +func NewConsensusMessageLog() *ConsensusMessageLog { + msgLog := &ConsensusMessageLog{ messages: mapset.NewSet(), maxLogSize: 0, // TODO } @@ -20,21 +21,32 @@ func NewMessageLog() *MessageLog { return msgLog } -func (ml *MessageLog) AddMessage(msg types2.ConsensusMessage) { +func (ml *ConsensusMessageLog) AddMessage(msg types2.ConsensusMessage) { ml.messages.Add(msg) } -func (ml *MessageLog) Exists(msg types2.ConsensusMessage) bool { +func (ml *ConsensusMessageLog) Exists(msg types2.ConsensusMessage) bool { return ml.messages.Contains(msg) } -func (ml *MessageLog) Get(typ types2.MessageType, consensusID string) []*types2.ConsensusMessage { +func (ml *ConsensusMessageLog) Get(typ types2.ConsensusMessageType, blockhash []byte) []*types2.ConsensusMessage { var result []*types2.ConsensusMessage for v := range ml.messages.Iter() { msg := v.(types2.ConsensusMessage) - if msg.Type == typ && msg.Task.ConsensusID == consensusID { - result = append(result, &msg) + 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/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 f603839..33e8918 100644 --- a/consensus/types/message.go +++ b/consensus/types/message.go @@ -1,22 +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 - From peer.ID - Type MessageType +type PrePrepareMessage struct { + Block *types2.Block } + +type PrepareMessage struct { + Blockhash []byte + Signature []byte +} + +type CommitMessage PrepareMessage diff --git a/consensus/utils.go b/consensus/utils.go index a94a1df..e75df0e 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -3,8 +3,7 @@ package consensus import ( "encoding/binary" "fmt" - - "github.com/fxamacker/cbor/v2" + "math/big" "github.com/Secured-Finance/dione/pubsub" @@ -18,23 +17,15 @@ import ( "github.com/libp2p/go-libp2p-core/peer" "github.com/Secured-Finance/dione/types" - "github.com/filecoin-project/go-state-types/crypto" + crypto2 "github.com/filecoin-project/go-state-types/crypto" + "github.com/libp2p/go-libp2p-core/crypto" "golang.org/x/xerrors" ) 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 - } - - if sig.Type != types.SigTypeEd25519 { - return nil, fmt.Errorf("miner worker address was not a Ed25519 key") - } - - return sig.Data, nil +func ComputeVRF(privKey crypto.PrivKey, sigInput []byte) ([]byte, error) { + return privKey.Sign(sigInput) } func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error { @@ -46,20 +37,20 @@ func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error { return nil } -func IsRoundWinner(round types.DrandRound, - worker peer.ID, brand types.BeaconEntry, minerStake, networkStake types.BigInt, sign SignFunc) (*types.ElectionProof, error) { +func IsRoundWinner(round uint64, + worker peer.ID, randomness []byte, minerStake, networkStake *big.Int, privKey crypto.PrivKey) (*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) + electionRand, err := DrawRandomness(randomness, crypto2.DomainSeparationTag_ElectionProofProduction, round, buf) if err != nil { return nil, xerrors.Errorf("failed to draw randomness: %w", err) } - vrfout, err := ComputeVRF(sign, worker, electionRand) + vrfout, err := ComputeVRF(privKey, electionRand) if err != nil { return nil, xerrors.Errorf("failed to compute VRF: %w", err) } @@ -74,7 +65,7 @@ func IsRoundWinner(round types.DrandRound, return ep, nil } -func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round types.DrandRound, entropy []byte) ([]byte, error) { +func DrawRandomness(rbase []byte, pers crypto2.DomainSeparationTag, round uint64, 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) @@ -111,31 +102,46 @@ func VerifyTaskSignature(task types.DioneTask) error { return nil } -func NewMessage(msg *pubsub.GenericMessage, typ pubsub.PubSubMessageType) (pubsub.GenericMessage, error) { - var newMsg pubsub.GenericMessage - newMsg.Type = typ - newCMsg := msg.Payload - newMsg.Payload = newCMsg - return newMsg, nil -} - -func CreatePrePrepareWithTaskSignature(task *types.DioneTask, privateKey []byte) (*pubsub.GenericMessage, error) { +func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, privKey crypto.PrivKey) (*pubsub.GenericMessage, error) { var message pubsub.GenericMessage - message.Type = pubsub.PrePrepareMessageType + switch typ { + case types2.ConsensusMessageTypePrePrepare: + { + message.Type = pubsub.PrePrepareMessageType + message.Payload = types2.PrePrepareMessage{ + Block: cmsg.Block, + } + break + } + case types2.ConsensusMessageTypePrepare: + { + message.Type = pubsub.PrepareMessageType + pm := types2.PrepareMessage{ + Blockhash: cmsg.Blockhash, + } + signature, err := privKey.Sign(cmsg.Blockhash) + if err != nil { + return nil, fmt.Errorf("failed to create signature: %v", err) + } + pm.Signature = signature + message.Payload = pm + break + } + case types2.ConsensusMessageTypeCommit: + { + message.Type = pubsub.CommitMessageType + pm := types2.CommitMessage{ + Blockhash: cmsg.Blockhash, + } + signature, err := privKey.Sign(cmsg.Blockhash) + if err != nil { + return nil, fmt.Errorf("failed to create signature: %v", err) + } + pm.Signature = signature + message.Payload = pm + break + } + } - 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 - data, err := cbor.Marshal(types2.ConsensusMessage{Task: *task}) - if err != nil { - return nil, err - } - message.Payload = data return &message, nil } 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 025a0d6..567e402 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Secured-Finance/dione go 1.14 require ( + github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 // indirect github.com/Secured-Finance/go-libp2p-pex v1.1.0 github.com/Secured-Finance/golang-set v1.8.0 github.com/aristanetworks/goarista v0.0.0-20210308203447-b196d8410f1d // indirect diff --git a/go.sum b/go.sum index 57efd7a..3481542 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= +github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= diff --git a/node/node.go b/node/node.go index 923a4dd..d8dca1b 100644 --- a/node/node.go +++ b/node/node.go @@ -13,15 +13,12 @@ import ( types2 "github.com/Secured-Finance/dione/blockchain/types" - "github.com/fxamacker/cbor/v2" - gorpc "github.com/libp2p/go-libp2p-gorpc" "github.com/Secured-Finance/dione/blockchain/pool" "github.com/Secured-Finance/dione/blockchain/sync" - "github.com/Secured-Finance/dione/cache" "github.com/Secured-Finance/dione/consensus" pubsub2 "github.com/Secured-Finance/dione/pubsub" @@ -34,8 +31,6 @@ import ( "github.com/Secured-Finance/dione/rpc/filecoin" - "github.com/Secured-Finance/dione/wallet" - "golang.org/x/xerrors" "github.com/Secured-Finance/dione/beacon" @@ -62,14 +57,14 @@ type Node struct { ConsensusManager *consensus.PBFTConsensusManager Miner *consensus.Miner Beacon beacon.BeaconNetworks - Wallet *wallet.LocalWallet - Cache cache.Cache DisputeManager *consensus.DisputeManager BlockPool *blockchain.BlockChain MemPool *pool.Mempool SyncManager sync.SyncManager NetworkService *NetworkService NetworkRPCHost *gorpc.Server + //Cache cache.Cache + //Wallet *wallet.LocalWallet } func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTime time.Duration) (*Node, error) { @@ -119,24 +114,10 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim n.PeerDiscovery = peerDiscovery logrus.Info("Peer discovery subsystem has been 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 been initialized!") - // initialize event log cache subsystem - c := provideCache(config) - n.Cache = c - logrus.Info("Event cache subsystem has initialized!") + //c := provideCache(config) + //n.Cache = c + //logrus.Info("Event cache subsystem has initialized!") // == initialize blockchain modules @@ -177,17 +158,25 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Blockchain synchronization subsystem has been successfully initialized!") // initialize mining subsystem - miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Beacon, n.Ethereum, rawPrivKey) + miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Beacon, n.Ethereum, prvKey, mp) n.Miner = miner logrus.Info("Mining subsystem has initialized!") // initialize consensus subsystem - cManager := provideConsensusManager(psb, miner, ethClient, rawPrivKey, n.Config.ConsensusMinApprovals, c) - n.ConsensusManager = cManager + consensusManager := provideConsensusManager(psb, miner, ethClient, prvKey, n.Config.ConsensusMinApprovals) + n.ConsensusManager = consensusManager logrus.Info("Consensus subsystem has initialized!") + // initialize random beacon network subsystem + randomBeaconNetwork, err := provideBeacon(psb.Pubsub, consensusManager) + if err != nil { + logrus.Fatal(err) + } + n.Beacon = randomBeaconNetwork + logrus.Info("Random beacon subsystem has been initialized!") + // initialize dispute subsystem - disputeManager, err := provideDisputeManager(context.TODO(), ethClient, cManager, config) + disputeManager, err := provideDisputeManager(context.TODO(), ethClient, consensusManager, config) if err != nil { logrus.Fatal(err) } @@ -195,11 +184,11 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Dispute subsystem has initialized!") // initialize internal eth wallet - w, err := provideWallet(n.Host.ID(), rawPrivKey) - if err != nil { - logrus.Fatal(err) - } - n.Wallet = w + //w, err := provideWallet(n.Host.ID(), rawPrivKey) + //if err != nil { + // logrus.Fatal(err) + //} + //n.Wallet = w return n, nil } @@ -272,35 +261,18 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { EventLoop: for { select { - case event := <-eventChan: + case <-eventChan: { logrus.Info("Let's wait a little so that all nodes have time to receive the request") 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 { - logrus.Warnf("Task is nil!") - continue - } - payload, err := cbor.Marshal(task) - if err != nil { - logrus.Errorf("Failed to marshal request event") - continue - } - tx := types2.CreateTransaction(payload) + // TODO make the rpc request and save response as tx payload + tx := types2.CreateTransaction([]byte{}) err = n.MemPool.StoreTx(tx) if err != nil { logrus.Errorf("Failed to store tx in mempool: %s", err.Error()) continue } - //logrus.Infof("Proposed new Dione task with ID: %s", event.ReqID.String()) - //err = n.ConsensusManager.Propose(*task) - //if err != nil { - // logrus.Errorf("Failed to propose task: %v", err) - //} } case <-ctx.Done(): break EventLoop diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index c47bfd6..af14769 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -58,17 +58,17 @@ func provideDisputeManager(ctx context.Context, ethClient *ethclient.EthereumCli 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 provideMiner(peerID peer.ID, ethAddress common.Address, beacon beacon.BeaconNetworks, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *consensus.Miner { + return consensus.NewMiner(peerID, ethAddress, beacon, ethClient, privateKey, mempool) } -func provideBeacon(ps *pubsub2.PubSub) (beacon.BeaconNetworks, error) { +func provideBeacon(ps *pubsub2.PubSub, pcm *consensus.PBFTConsensusManager) (beacon.BeaconNetworks, error) { networks := beacon.BeaconNetworks{} - bc, err := drand2.NewDrandBeacon(ps) + bc, err := drand2.NewDrandBeacon(ps, pcm) 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}) + networks = append(networks, beacon.BeaconNetwork{Start: config.DrandChainGenesisTime, Beacon: bc}) // NOTE: currently we use only one network return networks, nil } @@ -103,8 +103,8 @@ func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubR return pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap) } -func provideConsensusManager(psb *pubsub.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey []byte, minApprovals int, evc cache.Cache) *consensus.PBFTConsensusManager { - return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner, evc) +func provideConsensusManager(psb *pubsub.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, minApprovals int) *consensus.PBFTConsensusManager { + return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner) } func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) (host.Host, error) { 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/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..62a7d7a 100644 --- a/types/electionproof.go +++ b/types/electionproof.go @@ -99,13 +99,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 = 3 * config.ExpectedLeadersPerEpoch.Int64() type poiss struct { lam *big.Int @@ -175,10 +175,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 +191,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/task.go b/types/task.go index 29338c7..f567e7a 100644 --- a/types/task.go +++ b/types/task.go @@ -1,33 +1,24 @@ package types import ( - "strconv" + "github.com/ethereum/go-ethereum/common" "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 +// DEPRECATED! type DioneTask struct { OriginChain uint8 RequestType string RequestParams string Miner peer.ID - MinerEth string - Ticket *Ticket + MinerEth common.Address ElectionProof *ElectionProof BeaconEntries []BeaconEntry - DrandRound DrandRound + DrandRound uint64 Payload []byte RequestID string ConsensusID string @@ -39,10 +30,9 @@ func NewDioneTask( requestType string, requestParams string, miner peer.ID, - ticket *Ticket, electionProof *ElectionProof, beacon []BeaconEntry, - drand DrandRound, + drandRound uint64, payload []byte, ) *DioneTask { return &DioneTask{ @@ -50,12 +40,9 @@ func NewDioneTask( RequestType: requestType, RequestParams: requestParams, Miner: miner, - Ticket: ticket, ElectionProof: electionProof, BeaconEntries: beacon, - DrandRound: drand, + DrandRound: drandRound, Payload: payload, } } - -var tasksPerEpoch = NewInt(config.TasksPerEpoch) 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 -} From ad45e0c5542e9ca1587e8fe11ba65527178c3cc6 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 9 Jun 2021 00:39:48 +0300 Subject: [PATCH 28/59] Fix validation for PREPARE/COMMIT messages --- consensus/consensus_validator.go | 40 +++++++++++++++++++++----------- consensus/utils.go | 18 -------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index d9d28fd..60aab94 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -108,20 +108,32 @@ func NewConsensusValidator(miner *Miner) *ConsensusValidator { // // return true //}, - //types2.ConsensusMessageTypePrepare: func(msg types2.PrePrepareMessage) bool { - // err := VerifyTaskSignature(msg.Task) - // if err != nil { - // return false - // } - // return true - //}, - //types2.ConsensusMessageTypeCommit: func(msg types2.PrePrepareMessage) bool { - // err := VerifyTaskSignature(msg.Task) - // if err != nil { - // return false - // } - // return true - //}, + types2.ConsensusMessageTypePrepare: func(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 + }, + types2.ConsensusMessageTypeCommit: func(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 + }, } return cv diff --git a/consensus/utils.go b/consensus/utils.go index e75df0e..76463ed 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -9,8 +9,6 @@ import ( types2 "github.com/Secured-Finance/dione/consensus/types" - "github.com/mitchellh/hashstructure/v2" - "github.com/Secured-Finance/dione/sigs" "github.com/minio/blake2b-simd" @@ -86,22 +84,6 @@ func DrawRandomness(rbase []byte, pers crypto2.DomainSeparationTag, round uint64 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(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, privKey crypto.PrivKey) (*pubsub.GenericMessage, error) { var message pubsub.GenericMessage switch typ { From ac2bf637a81d1e839819709b78e62d300f5aaa37 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 10 Jun 2021 23:31:44 +0300 Subject: [PATCH 29/59] Simplify validation process of beacon entries --- beacon/beacon.go | 70 +++----------------------------------- blockchain/types/block.go | 4 +++ consensus/miner.go | 14 -------- node/node.go | 8 ++--- node/node_dep_providers.go | 4 +-- types/beacon.go | 2 -- 6 files changed, 15 insertions(+), 87 deletions(-) diff --git a/beacon/beacon.go b/beacon/beacon.go index d388340..e1a1503 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" ) @@ -42,70 +39,13 @@ type BeaconAPI interface { LatestBeaconRound() uint64 } -// ValidateTaskBeacons is a function that verifies dione task randomness -func ValidateTaskBeacons(beaconNetworks BeaconNetworks, t *types.DioneTask, prevEntry types.BeaconEntry) error { - parentBeacon := beaconNetworks.BeaconNetworkForRound(t.DrandRound - 1) - 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() - - start := lib.Clock.Now() - - out := make([]types.BeaconEntry, 2) - prevBeaconEntry := beacon.Entry(ctx, round-1) - res := <-prevBeaconEntry - if res.Err != nil { - return nil, fmt.Errorf("getting entry %d returned error: %w", round-1, res.Err) - } - out[0] = res.Entry - curBeaconEntry := beacon.Entry(ctx, round) - res = <-curBeaconEntry - 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)) - return out, nil -} diff --git a/blockchain/types/block.go b/blockchain/types/block.go index c48f148..09af79d 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -3,6 +3,8 @@ package types import ( "time" + "github.com/Secured-Finance/dione/types" + "github.com/libp2p/go-libp2p-core/crypto" "github.com/ethereum/go-ethereum/common" @@ -27,6 +29,8 @@ type BlockHeader struct { Proposer peer.ID ProposerEth common.Address Signature []byte + BeaconEntry types.BeaconEntry + ElectionProof *types.ElectionProof } func GenesisBlock() *Block { diff --git a/consensus/miner.go b/consensus/miner.go index 29313c6..b007e81 100644 --- a/consensus/miner.go +++ b/consensus/miner.go @@ -12,7 +12,6 @@ import ( types2 "github.com/Secured-Finance/dione/blockchain/types" - "github.com/Secured-Finance/dione/beacon" "github.com/libp2p/go-libp2p-core/peer" "github.com/Secured-Finance/dione/ethclient" @@ -24,7 +23,6 @@ type Miner struct { address peer.ID ethAddress common.Address mutex sync.Mutex - beacon beacon.BeaconNetworks ethClient *ethclient.EthereumClient minerStake *big.Int networkStake *big.Int @@ -35,7 +33,6 @@ type Miner struct { func NewMiner( address peer.ID, ethAddress common.Address, - beacon beacon.BeaconNetworks, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool, @@ -43,7 +40,6 @@ func NewMiner( return &Miner{ address: address, ethAddress: ethAddress, - beacon: beacon, ethClient: ethClient, privateKey: privateKey, mempool: mempool, @@ -112,16 +108,6 @@ func (m *Miner) MineBlock(randomness []byte, drandRound uint64, lastBlockHeader return nil, nil } - // TODO get rpc responses for oracle requests - //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) - //} - txs := m.mempool.GetAllTransactions() if txs == nil { return nil, fmt.Errorf("there is no txes for processing") // skip new consensus round because there is no transaction for processing diff --git a/node/node.go b/node/node.go index d8dca1b..bf4d649 100644 --- a/node/node.go +++ b/node/node.go @@ -158,14 +158,14 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Blockchain synchronization subsystem has been successfully initialized!") // initialize mining subsystem - miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Beacon, n.Ethereum, prvKey, mp) + miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Ethereum, prvKey, mp) n.Miner = miner - logrus.Info("Mining subsystem has initialized!") + logrus.Info("Mining subsystem has been initialized!") // initialize consensus subsystem consensusManager := provideConsensusManager(psb, miner, ethClient, prvKey, n.Config.ConsensusMinApprovals) n.ConsensusManager = consensusManager - logrus.Info("Consensus subsystem has initialized!") + logrus.Info("Consensus subsystem has been initialized!") // initialize random beacon network subsystem randomBeaconNetwork, err := provideBeacon(psb.Pubsub, consensusManager) @@ -181,7 +181,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Fatal(err) } n.DisputeManager = disputeManager - logrus.Info("Dispute subsystem has initialized!") + logrus.Info("Dispute subsystem has been initialized!") // initialize internal eth wallet //w, err := provideWallet(n.Host.ID(), rawPrivKey) diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index af14769..212658d 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -58,8 +58,8 @@ func provideDisputeManager(ctx context.Context, ethClient *ethclient.EthereumCli return consensus.NewDisputeManager(ctx, ethClient, pcm, cfg.Ethereum.DisputeVoteWindow) } -func provideMiner(peerID peer.ID, ethAddress common.Address, beacon beacon.BeaconNetworks, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *consensus.Miner { - return consensus.NewMiner(peerID, ethAddress, beacon, ethClient, privateKey, mempool) +func provideMiner(peerID peer.ID, ethAddress common.Address, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *consensus.Miner { + return consensus.NewMiner(peerID, ethAddress, ethClient, privateKey, mempool) } func provideBeacon(ps *pubsub2.PubSub, pcm *consensus.PBFTConsensusManager) (beacon.BeaconNetworks, error) { diff --git a/types/beacon.go b/types/beacon.go index b10bc9c..d3cc45f 100644 --- a/types/beacon.go +++ b/types/beacon.go @@ -6,8 +6,6 @@ type BeaconEntry struct { Metadata map[string]interface{} } -type Randomness []byte - func NewBeaconEntry(round uint64, data []byte, metadata map[string]interface{}) BeaconEntry { return BeaconEntry{ Round: round, From de5d9cf664a2cf68d4cd142276c160f694b1eff4 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 10 Jun 2021 23:32:39 +0300 Subject: [PATCH 30/59] Implement making RPC request & creating DioneTask for holding result of oracle request in transaction payload --- node/node.go | 34 ++++++++++++++++++++++++++++------ types/task.go | 36 ------------------------------------ 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/node/node.go b/node/node.go index bf4d649..3ab3af8 100644 --- a/node/node.go +++ b/node/node.go @@ -9,6 +9,10 @@ import ( "os" "time" + "github.com/fxamacker/cbor/v2" + + "github.com/Secured-Finance/dione/types" + "github.com/Secured-Finance/dione/blockchain" types2 "github.com/Secured-Finance/dione/blockchain/types" @@ -261,13 +265,31 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { EventLoop: for { select { - case <-eventChan: + case event := <-eventChan: { - logrus.Info("Let's wait a little so that all nodes have time to receive the request") - time.Sleep(5 * time.Second) - - // TODO make the rpc request and save response as tx payload - tx := types2.CreateTransaction([]byte{}) + 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 + } + res, err := rpcMethod(event.RequestParams) + if err != nil { + 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 = n.MemPool.StoreTx(tx) if err != nil { logrus.Errorf("Failed to store tx in mempool: %s", err.Error()) diff --git a/types/task.go b/types/task.go index f567e7a..87ec8b8 100644 --- a/types/task.go +++ b/types/task.go @@ -1,48 +1,12 @@ package types -import ( - "github.com/ethereum/go-ethereum/common" - - "github.com/libp2p/go-libp2p-core/peer" -) - const TicketRandomnessLookback = 1 // DioneTask represents the values of task computation -// DEPRECATED! type DioneTask struct { OriginChain uint8 RequestType string RequestParams string - Miner peer.ID - MinerEth common.Address - ElectionProof *ElectionProof - BeaconEntries []BeaconEntry - DrandRound uint64 Payload []byte RequestID string - ConsensusID string - Signature []byte `hash:"-"` -} - -func NewDioneTask( - originChain uint8, - requestType string, - requestParams string, - miner peer.ID, - electionProof *ElectionProof, - beacon []BeaconEntry, - drandRound uint64, - payload []byte, -) *DioneTask { - return &DioneTask{ - OriginChain: originChain, - RequestType: requestType, - RequestParams: requestParams, - Miner: miner, - ElectionProof: electionProof, - BeaconEntries: beacon, - DrandRound: drandRound, - Payload: payload, - } } From ea9ceaeda9461d771c060144011d4c6d53f686e0 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 11 Jun 2021 14:40:32 +0300 Subject: [PATCH 31/59] Implement block validation at PREPREPARE stage --- blockchain/pool/blockpool.go | 12 +- blockchain/sync/sync_mgr.go | 41 ++-- blockchain/types/block.go | 3 +- blockchain/utils/verification.go | 27 +++ consensus/consensus.go | 27 ++- consensus/consensus_validator.go | 266 ++++++++++++++-------- consensus/miner.go | 8 +- consensus/utils.go | 18 +- consensus/validation/filecoin/filecoin.go | 46 ++-- consensus/validation/payload_validator.go | 8 +- consensus/validation/solana/solana.go | 17 ++ consensus/validation/utils.go | 24 ++ go.mod | 1 + go.sum | 2 + node/node.go | 16 +- node/node_dep_providers.go | 10 +- 16 files changed, 355 insertions(+), 171 deletions(-) create mode 100644 blockchain/utils/verification.go create mode 100644 consensus/validation/solana/solana.go create mode 100644 consensus/validation/utils.go diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go index 5727464..48c3296 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -9,13 +9,16 @@ import ( // BlockPool is pool for blocks that isn't not validated or committed yet type BlockPool struct { + mempool *Mempool knownBlocks cache.Cache acceptedBlocks cache.Cache } -func NewBlockPool() (*BlockPool, error) { +func NewBlockPool(mp *Mempool) (*BlockPool, error) { bp := &BlockPool{ acceptedBlocks: cache.NewInMemoryCache(), // here we need to use separate cache + knownBlocks: cache.NewInMemoryCache(), + mempool: mp, } return bp, nil @@ -51,7 +54,12 @@ func (bp *BlockPool) GetAllAcceptedBlocks() []*types.Block { // PruneAcceptedBlocks cleans accepted blocks list. It is called when new consensus round starts. func (bp *BlockPool) PruneAcceptedBlocks() { - for k := range bp.acceptedBlocks.Items() { + for k, v := range bp.acceptedBlocks.Items() { + block := v.(*types.Block) + for _, v := range block.Data { + v.MerkleProof = nil + bp.mempool.StoreTx(v) // return transactions back to mempool + } bp.acceptedBlocks.Delete(k) } } diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 8e88855..08fca6e 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -8,6 +8,10 @@ import ( "strings" "sync" + "github.com/asaskevich/EventBus" + + "github.com/Secured-Finance/dione/blockchain/utils" + "github.com/Secured-Finance/dione/blockchain" "github.com/Secured-Finance/dione/pubsub" @@ -42,11 +46,13 @@ type syncManager struct { bootstrapPeer peer.ID rpcClient *gorpc.Client psb *pubsub.PubSubRouter + bus EventBus.Bus } -func NewSyncManager(bp *blockchain.BlockChain, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID, psb *pubsub.PubSubRouter) SyncManager { +func NewSyncManager(bus EventBus.Bus, bp *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: bp, mempool: mp, ctx: ctx, @@ -59,9 +65,26 @@ func NewSyncManager(bp *blockchain.BlockChain, mp *pool.Mempool, p2pRPCClient *g psb.Hook(pubsub.NewTxMessageType, sm.onNewTransaction, types2.Transaction{}) + go func() { + if err := sm.initialSync(); err != nil { + logrus.Error(err) + } + }() + return sm } +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 @@ -173,7 +196,7 @@ func (sm *syncManager) doInitialMempoolSync() error { logrus.Warnf(err.Error()) } } - // FIXME handle not found transactions + // TODO handle not found transactions } return nil @@ -198,18 +221,8 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) error { // 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 tx.MerkleProof == nil { - return fmt.Errorf("block transaction hasn't merkle proof") - } - txProofVerified, err := merkletree.VerifyProofUsing(tx.Hash, false, tx.MerkleProof, [][]byte{block.Header.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") + if err := utils.VerifyTx(block.Header, tx); err != nil { + return err } } diff --git a/blockchain/types/block.go b/blockchain/types/block.go index 09af79d..ff0877e 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -44,7 +44,7 @@ func GenesisBlock() *Block { } } -func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth common.Address, privateKey crypto.PrivKey) (*Block, error) { +func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth common.Address, privateKey crypto.PrivKey, eproof *types.ElectionProof) (*Block, error) { timestamp := time.Now().Unix() // extract hashes from transactions @@ -88,6 +88,7 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth comm Hash: blockHash, LastHash: lastBlockHeader.Hash, LastHashProof: lastHashProof, + ElectionProof: eproof, }, Data: txs, } diff --git a/blockchain/utils/verification.go b/blockchain/utils/verification.go new file mode 100644 index 0000000..bd5f0ba --- /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 hasn't merkle proof") + } + txProofVerified, err := merkletree.VerifyProofUsing(tx.Hash, false, 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/consensus/consensus.go b/consensus/consensus.go index 9139890..861ca22 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -5,6 +5,8 @@ import ( "math/big" "sync" + "github.com/asaskevich/EventBus" + "github.com/Secured-Finance/dione/blockchain" "github.com/drand/drand/client" @@ -37,6 +39,7 @@ const ( type PBFTConsensusManager struct { phony.Inbox + bus EventBus.Bus psb *pubsub.PubSubRouter minApprovals int // FIXME privKey crypto.PrivKey @@ -55,21 +58,29 @@ type State struct { randomness []byte blockHeight uint64 status StateStatus + ready chan bool } -func NewPBFTConsensusManager(psb *pubsub.PubSubRouter, minApprovals int, privKey crypto.PrivKey, ethereumClient *ethclient.EthereumClient, miner *Miner) *PBFTConsensusManager { +func NewPBFTConsensusManager(bus EventBus.Bus, psb *pubsub.PubSubRouter, minApprovals int, privKey crypto.PrivKey, ethereumClient *ethclient.EthereumClient, miner *Miner, bc *blockchain.BlockChain) *PBFTConsensusManager { pcm := &PBFTConsensusManager{} pcm.psb = psb pcm.miner = miner - pcm.validator = NewConsensusValidator(miner) + pcm.validator = NewConsensusValidator(miner, bc) pcm.msgLog = NewConsensusMessageLog() pcm.minApprovals = minApprovals pcm.privKey = privKey pcm.ethereumClient = ethereumClient - pcm.state = &State{} + pcm.state = &State{ + ready: make(chan bool, 1), + status: StateStatusUnknown, + } + pcm.bus = bus pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types.PrePrepareMessage{}) pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types.PrepareMessage{}) pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types.CommitMessage{}) + bus.SubscribeOnce("sync:initialSyncCompleted", func() { + pcm.state.ready <- true + }) return pcm } @@ -104,11 +115,13 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage Block: prePrepare.Block, } + <-pcm.state.ready + if pcm.msgLog.Exists(cmsg) { logrus.Debugf("received existing pre_prepare msg, dropping...") return } - if !pcm.validator.Valid(cmsg) { + if !pcm.validator.Valid(cmsg, map[string]interface{}{"randomness": pcm.state.randomness}) { logrus.Warn("received invalid pre_prepare msg, dropping...") return } @@ -158,7 +171,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { logrus.Debugf("received existing prepare msg, dropping...") return } - if !pcm.validator.Valid(cmsg) { + if !pcm.validator.Valid(cmsg, nil) { logrus.Warn("received invalid prepare msg, dropping...") return } @@ -207,7 +220,7 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { logrus.Debugf("received existing commit msg, dropping...") return } - if !pcm.validator.Valid(cmsg) { + if !pcm.validator.Valid(cmsg, nil) { logrus.Warn("received invalid commit msg, dropping...") return } @@ -235,7 +248,7 @@ func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Resu return } - minedBlock, err := pcm.miner.MineBlock(res.Randomness(), res.Round(), block.Header) + minedBlock, err := pcm.miner.MineBlock(res.Randomness(), block.Header) if err != nil { logrus.Errorf("Failed to mine the block: %s", err.Error()) return diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 60aab94..1c43a24 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -1,114 +1,180 @@ package consensus import ( + "bytes" + "fmt" + "sync" + + types3 "github.com/Secured-Finance/dione/blockchain/types" + + "github.com/Secured-Finance/dione/blockchain" + "github.com/Secured-Finance/dione/blockchain/utils" types2 "github.com/Secured-Finance/dione/consensus/types" + "github.com/Secured-Finance/dione/consensus/validation" + "github.com/Secured-Finance/dione/types" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/fxamacker/cbor/v2" + "github.com/sirupsen/logrus" + "github.com/wealdtech/go-merkletree" + "github.com/wealdtech/go-merkletree/keccak256" ) type ConsensusValidator struct { - validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool + validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool miner *Miner + blockchain *blockchain.BlockChain } -func NewConsensusValidator(miner *Miner) *ConsensusValidator { +func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain) *ConsensusValidator { cv := &ConsensusValidator{ - miner: miner, + miner: miner, + blockchain: bc, } - cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool{ + cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool{ // FIXME it all - //types2.ConsensusMessageTypePrePrepare: func(msg types2.PrePrepareMessage) bool { - // // TODO here we need to do validation of block itself - // - // // === verify task signature === - // err := VerifyTaskSignature(msg.Task) - // if err != nil { - // logrus.Errorf("unable to verify signature: %v", err) - // return false - // } - // ///////////////////////////////// - // - // // === verify if request exists in cache === - // var requestEvent *dioneOracle.DioneOracleNewOracleRequest - // err = cv.cache.Get("request_"+msg.Task.RequestID, &requestEvent) - // if err != nil { - // logrus.Errorf("the request doesn't exist in the cache or has been failed to decode: %v", err) - // return false - // } - // - // if requestEvent.OriginChain != msg.Task.OriginChain || - // requestEvent.RequestType != msg.Task.RequestType || - // requestEvent.RequestParams != msg.Task.RequestParams { - // - // logrus.Errorf("the incoming task and cached request requestEvent don't match!") - // return false - // } - // ///////////////////////////////// - // - // // === verify election proof wincount preliminarily === - // if msg.Task.ElectionProof.WinCount < 1 { - // logrus.Error("miner isn't a winner!") - // return false - // } - // ///////////////////////////////// - // - // // === verify miner's eligibility to propose this task === - // err = cv.miner.IsMinerEligibleToProposeBlock(common.HexToAddress(msg.Task.MinerEth)) - // if err != nil { - // logrus.Errorf("miner is not eligible to propose task: %v", err) - // return false - // } - // ///////////////////////////////// - // - // // === verify election proof vrf === - // minerAddressMarshalled, err := msg.Task.Miner.MarshalBinary() - // if err != nil { - // logrus.Errorf("failed to marshal miner address: %v", err) - // return false - // } - // electionProofRandomness, err := DrawRandomness( - // msg.Task.BeaconEntries[1].Data, - // crypto.DomainSeparationTag_ElectionProofProduction, - // msg.Task.DrandRound, - // minerAddressMarshalled, - // ) - // if err != nil { - // logrus.Errorf("failed to draw electionProofRandomness: %v", err) - // return false - // } - // err = VerifyVRF(msg.Task.Miner, electionProofRandomness, msg.Task.ElectionProof.VRFProof) - // if err != nil { - // logrus.Errorf("failed to verify election proof vrf: %v", err) - // } - // ////////////////////////////////////// - // - // // === compute wincount locally and verify values === - // mStake, nStake, err := cv.miner.GetStakeInfo(common.HexToAddress(msg.Task.MinerEth)) - // if err != nil { - // logrus.Errorf("failed to get miner stake: %v", err) - // return false - // } - // actualWinCount := msg.Task.ElectionProof.ComputeWinCount(*mStake, *nStake) - // if msg.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(msg.Task.OriginChain, msg.Task.RequestType); validationFunc != nil { - // err := validationFunc(msg.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!", msg.Task.OriginChain, msg.Task.RequestType) - // } - // ///////////////////////////////// - // - // return true - //}, - types2.ConsensusMessageTypePrepare: func(msg types2.ConsensusMessage) bool { + types2.ConsensusMessageTypePrePrepare: func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool { + // === verify block signature === + pubkey, err := msg.Block.Header.Proposer.ExtractPublicKey() + if err != nil { + logrus.Errorf("unable to extract public key from block proposer's peer id: %s", err.Error()) + return false + } + + ok, err := pubkey.Verify(msg.Block.Header.Hash, msg.Block.Header.Signature) + if err != nil { + logrus.Errorf("failed to verify block signature: %s", err.Error()) + return false + } + if !ok { + logrus.Errorf("signature of block %x is invalid", msg.Block.Header.Hash) + return false + } + ///////////////////////////////// + + // === check last hash merkle proof === + latestHeight, err := cv.blockchain.GetLatestBlockHeight() + if err != nil { + logrus.Error(err) + return false + } + previousBlockHeader, err := cv.blockchain.FetchBlockHeaderByHeight(latestHeight) + if err != nil { + logrus.Error(err) + return false + } + if bytes.Compare(msg.Block.Header.LastHash, previousBlockHeader.Hash) != 0 { + logrus.Error("block header has invalid last block hash") + return false + } + + verified, err := merkletree.VerifyProofUsing(previousBlockHeader.Hash, false, msg.Block.Header.LastHashProof, [][]byte{msg.Block.Header.Hash}, keccak256.New()) + if err != nil { + logrus.Error("failed to verify last block hash merkle proof: %s", err.Error()) + return false + } + if !verified { + logrus.Error("merkle hash of current block doesn't contain hash of previous block: %s", err.Error()) + return false + } + ///////////////////////////////// + + // === verify election proof wincount preliminarily === + if msg.Block.Header.ElectionProof.WinCount < 1 { + logrus.Error("miner isn't a winner!") + return false + } + ///////////////////////////////// + + // === verify miner's eligibility to propose this task === + err = cv.miner.IsMinerEligibleToProposeBlock(msg.Block.Header.ProposerEth) + if err != nil { + logrus.Errorf("miner is not eligible to propose block: %v", err) + return false + } + ///////////////////////////////// + + // === verify election proof vrf === + proposerBuf, err := msg.Block.Header.Proposer.MarshalBinary() + if err != nil { + logrus.Error(err) + return false + } + + eproofRandomness, err := DrawRandomness( + metadata["randomness"].([]byte), + crypto.DomainSeparationTag_ElectionProofProduction, + msg.Block.Header.Height, + proposerBuf, + ) + if err != nil { + logrus.Errorf("failed to draw ElectionProof randomness: %s", err.Error()) + return false + } + err = VerifyVRF(msg.Block.Header.Proposer, eproofRandomness, msg.Block.Header.ElectionProof.VRFProof) + if err != nil { + logrus.Errorf("failed to verify election proof vrf: %v", err) + return false + } + ////////////////////////////////////// + + // === compute wincount locally and verify values === + mStake, nStake, err := cv.miner.GetStakeInfo(msg.Block.Header.ProposerEth) + if err != nil { + logrus.Errorf("failed to get miner stake: %v", err) + return false + } + actualWinCount := msg.Block.Header.ElectionProof.ComputeWinCount(mStake, nStake) + if msg.Block.Header.ElectionProof.WinCount != actualWinCount { + logrus.Errorf("locally computed wincount of block %x isn't matching received value!", msg.Block.Header.Hash) + return false + } + ////////////////////////////////////// + + // === validate block transactions === + result := make(chan error) + var wg sync.WaitGroup + for _, v := range msg.Block.Data { + wg.Add(1) + go func(v *types3.Transaction, c chan error) { + if err := utils.VerifyTx(msg.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.Debugf("Origin chain [%v]/request type[%v] doesn't have any payload validation!", task.OriginChain, task.RequestType) + } + wg.Done() + }(v, result) + } + go func() { + wg.Wait() + close(result) + }() + for err := range result { + if err != nil { + logrus.Error(err) + return false + } + } + ///////////////////////////////// + + return true + }, + types2.ConsensusMessageTypePrepare: func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool { pubKey, err := msg.From.ExtractPublicKey() if err != nil { // TODO logging @@ -121,7 +187,7 @@ func NewConsensusValidator(miner *Miner) *ConsensusValidator { } return ok }, - types2.ConsensusMessageTypeCommit: func(msg types2.ConsensusMessage) bool { + types2.ConsensusMessageTypeCommit: func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool { pubKey, err := msg.From.ExtractPublicKey() if err != nil { // TODO logging @@ -139,6 +205,6 @@ func NewConsensusValidator(miner *Miner) *ConsensusValidator { return cv } -func (cv *ConsensusValidator) Valid(msg types2.ConsensusMessage) bool { - return cv.validationFuncMap[msg.Type](msg) +func (cv *ConsensusValidator) Valid(msg types2.ConsensusMessage, metadata map[string]interface{}) bool { + return cv.validationFuncMap[msg.Type](msg, metadata) } diff --git a/consensus/miner.go b/consensus/miner.go index b007e81..42a7317 100644 --- a/consensus/miner.go +++ b/consensus/miner.go @@ -85,15 +85,15 @@ func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, error) { return mStake, nStake, nil } -func (m *Miner) MineBlock(randomness []byte, drandRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) { - logrus.Debug("attempting to mine the block at epoch: ", drandRound) +func (m *Miner) MineBlock(randomness []byte, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) { + logrus.Debug("attempting to mine the block at epoch: ", lastBlockHeader.Height+1) if err := m.UpdateCurrentStakeInfo(); err != nil { return nil, fmt.Errorf("failed to update miner stake: %w", err) } winner, err := IsRoundWinner( - drandRound, + lastBlockHeader.Height+1, m.address, randomness, m.minerStake, @@ -113,7 +113,7 @@ func (m *Miner) MineBlock(randomness []byte, drandRound uint64, lastBlockHeader return nil, fmt.Errorf("there is no txes for processing") // skip new consensus round because there is no transaction for processing } - newBlock, err := types2.CreateBlock(lastBlockHeader, txs, m.ethAddress, m.privateKey) + newBlock, err := types2.CreateBlock(lastBlockHeader, txs, m.ethAddress, m.privateKey, winner) if err != nil { return nil, fmt.Errorf("failed to create new block: %w", err) } diff --git a/consensus/utils.go b/consensus/utils.go index 76463ed..490e992 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -9,7 +9,6 @@ import ( types2 "github.com/Secured-Finance/dione/consensus/types" - "github.com/Secured-Finance/dione/sigs" "github.com/minio/blake2b-simd" "github.com/libp2p/go-libp2p-core/peer" @@ -27,9 +26,16 @@ func ComputeVRF(privKey crypto.PrivKey, sigInput []byte) ([]byte, error) { } func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error { - err := sigs.Verify(&types.Signature{Type: types.SigTypeEd25519, Data: vrfproof}, []byte(worker), vrfBase) + pk, err := worker.ExtractPublicKey() if err != nil { - return xerrors.Errorf("vrf was invalid: %w", err) + return err + } + ok, err := pk.Verify(vrfproof, vrfBase) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("vrf was invalid") } return nil @@ -40,17 +46,17 @@ func IsRoundWinner(round uint64, buf, err := worker.MarshalBinary() if err != nil { - return nil, xerrors.Errorf("failed to marshal address: %w", err) + return nil, fmt.Errorf("failed to marshal address: %w", err) } electionRand, err := DrawRandomness(randomness, crypto2.DomainSeparationTag_ElectionProofProduction, round, buf) if err != nil { - return nil, xerrors.Errorf("failed to draw randomness: %w", err) + return nil, fmt.Errorf("failed to draw randomness: %w", err) } vrfout, err := ComputeVRF(privKey, electionRand) if err != nil { - return nil, xerrors.Errorf("failed to compute VRF: %w", err) + return nil, fmt.Errorf("failed to compute VRF: %w", err) } ep := &types.ElectionProof{VRFProof: vrfout} diff --git a/consensus/validation/filecoin/filecoin.go b/consensus/validation/filecoin/filecoin.go index 076f43d..fb5c867 100644 --- a/consensus/validation/filecoin/filecoin.go +++ b/consensus/validation/filecoin/filecoin.go @@ -1,39 +1,35 @@ 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()) - } - } - - 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 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) } func init() { - validation.RegisterValidation(rtypes.RPCTypeFilecoin, map[string]func([]byte) error{ + validation.RegisterValidation(rtypes.RPCTypeFilecoin, map[string]func(*types.DioneTask) error{ "getTransaction": ValidateGetTransaction, }) } 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/go.mod b/go.mod index 567e402..2a52f26 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Secured-Finance/go-libp2p-pex v1.1.0 github.com/Secured-Finance/golang-set v1.8.0 github.com/aristanetworks/goarista v0.0.0-20210308203447-b196d8410f1d // indirect + github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/cespare/cp v1.1.1 // indirect github.com/deckarep/golang-set v1.7.1 // indirect diff --git a/go.sum b/go.sum index 3481542..3907208 100644 --- a/go.sum +++ b/go.sum @@ -109,6 +109,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/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/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/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= diff --git a/node/node.go b/node/node.go index 3ab3af8..a3bae20 100644 --- a/node/node.go +++ b/node/node.go @@ -9,6 +9,8 @@ import ( "os" "time" + "github.com/asaskevich/EventBus" + "github.com/fxamacker/cbor/v2" "github.com/Secured-Finance/dione/types" @@ -67,6 +69,7 @@ type Node struct { SyncManager sync.SyncManager NetworkService *NetworkService NetworkRPCHost *gorpc.Server + Bus EventBus.Bus //Cache cache.Cache //Wallet *wallet.LocalWallet } @@ -76,6 +79,9 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim Config: config, } + bus := EventBus.New() + n.Bus = bus + // initialize libp2p host lhost, err := provideLibp2pHost(n.Config, prvKey) if err != nil { @@ -126,11 +132,11 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim // == initialize blockchain modules // initialize blockpool database - bp, err := provideBlockChain(n.Config) + bc, err := provideBlockChain(n.Config) if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) } - n.BlockPool = bp + n.BlockPool = bc logrus.Info("Block pool database has been successfully initialized!") // initialize mempool @@ -141,7 +147,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim n.MemPool = mp logrus.Info("Mempool has been successfully initialized!") - ns := provideNetworkService(bp) + ns := provideNetworkService(bc) n.NetworkService = ns rpcHost := provideNetworkRPCHost(lhost) err = rpcHost.Register(ns) @@ -154,7 +160,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim r := provideP2PRPCClient(lhost) // initialize sync manager - sm, err := provideSyncManager(bp, mp, r, baddrs[0], psb) // FIXME here we just pick up first bootstrap in list + sm, err := provideSyncManager(bus, bc, mp, r, baddrs[0], psb) // FIXME here we just pick up first bootstrap in list if err != nil { logrus.Fatal(err) } @@ -167,7 +173,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Mining subsystem has been initialized!") // initialize consensus subsystem - consensusManager := provideConsensusManager(psb, miner, ethClient, prvKey, n.Config.ConsensusMinApprovals) + consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals) n.ConsensusManager = consensusManager logrus.Info("Consensus subsystem has been initialized!") diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 212658d..3ec1836 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "github.com/asaskevich/EventBus" + "github.com/Secured-Finance/dione/blockchain" drand2 "github.com/Secured-Finance/dione/beacon/drand" @@ -103,8 +105,8 @@ func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubR return pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap) } -func provideConsensusManager(psb *pubsub.PubSubRouter, miner *consensus.Miner, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, minApprovals int) *consensus.PBFTConsensusManager { - return consensus.NewPBFTConsensusManager(psb, minApprovals, privateKey, ethClient, miner) +func provideConsensusManager(bus EventBus.Bus, psb *pubsub.PubSubRouter, miner *consensus.Miner, bc *blockchain.BlockChain, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, minApprovals int) *consensus.PBFTConsensusManager { + return consensus.NewPBFTConsensusManager(bus, psb, minApprovals, privateKey, ethClient, miner, bc) } func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) (host.Host, error) { @@ -162,12 +164,12 @@ func provideMemPool() (*pool.Mempool, error) { return pool.NewMempool() } -func provideSyncManager(bp *blockchain.BlockChain, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { +func provideSyncManager(bus EventBus.Bus, bp *blockchain.BlockChain, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { addr, err := peer.AddrInfoFromP2pAddr(bootstrap) if err != nil { return nil, err } - return sync.NewSyncManager(bp, mp, r, addr.ID, psb), nil + return sync.NewSyncManager(bus, bp, mp, r, addr.ID, psb), nil } func provideP2PRPCClient(h host.Host) *gorpc.Client { From 29c38e80b729bb55242cef362195ecea9796f77e Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 11 Jun 2021 14:43:48 +0300 Subject: [PATCH 32/59] Add missing send to ready channel in consensus state when mining new block --- consensus/consensus.go | 1 + 1 file changed, 1 insertion(+) diff --git a/consensus/consensus.go b/consensus/consensus.go index 861ca22..09da50d 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -247,6 +247,7 @@ func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Resu logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) return } + pcm.state.ready <- true minedBlock, err := pcm.miner.MineBlock(res.Randomness(), block.Header) if err != nil { From 2952983c94695c87e90b3f3d735a41908bab81a3 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 15 Jun 2021 00:45:35 +0300 Subject: [PATCH 33/59] Fix some minor mistakes in various places --- consensus/consensus.go | 28 +++++++++------------------- consensus/consensus_validator.go | 2 +- consensus/miner.go | 2 +- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 09da50d..b115bc5 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -5,6 +5,8 @@ import ( "math/big" "sync" + "github.com/Secured-Finance/dione/cache" + "github.com/asaskevich/EventBus" "github.com/Secured-Finance/dione/blockchain" @@ -92,6 +94,7 @@ func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { return err } pcm.psb.BroadcastToServiceTopic(prePrepareMsg) + pcm.blockPool.AddBlock(blk) pcm.state.status = StateStatusPrePrepared return nil } @@ -152,18 +155,11 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { Type: types.ConsensusMessageTypePrepare, From: message.From, Blockhash: prepare.Blockhash, - Signature: prepare.Signature, // TODO check the signature + Signature: prepare.Signature, } - pk, _ := message.From.ExtractPublicKey() - ok, err := pk.Verify(cmsg.Blockhash, cmsg.Signature) - if err != nil { - logrus.Warnf("Failed to verify PREPARE message signature: %s", err.Error()) - return - } - - if !ok { - logrus.Errorf("Signature of PREPARE message of peer %s isn't valid!", cmsg.From) + if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { + logrus.Debugf("received unknown block") return } @@ -171,6 +167,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { logrus.Debugf("received existing prepare msg, dropping...") return } + if !pcm.validator.Valid(cmsg, nil) { logrus.Warn("received invalid prepare msg, dropping...") return @@ -204,15 +201,8 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { Signature: commit.Signature, // TODO check the signature } - pk, _ := message.From.ExtractPublicKey() - ok, err := pk.Verify(cmsg.Blockhash, cmsg.Signature) - if err != nil { - logrus.Warnf("Failed to verify COMMIT message signature: %s", err.Error()) - return - } - - if !ok { - logrus.Errorf("Signature of COMMIT message of peer %s isn't valid!", cmsg.From) + if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { + logrus.Debugf("received unknown block") return } diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 1c43a24..571cc36 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -137,6 +137,7 @@ func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain) *ConsensusVa for _, v := range msg.Block.Data { wg.Add(1) go func(v *types3.Transaction, c chan error) { + defer wg.Done() if err := utils.VerifyTx(msg.Block.Header, v); err != nil { c <- fmt.Errorf("failed to verify tx: %w", err) return @@ -157,7 +158,6 @@ func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain) *ConsensusVa } else { logrus.Debugf("Origin chain [%v]/request type[%v] doesn't have any payload validation!", task.OriginChain, task.RequestType) } - wg.Done() }(v, result) } go func() { diff --git a/consensus/miner.go b/consensus/miner.go index 42a7317..a4527a3 100644 --- a/consensus/miner.go +++ b/consensus/miner.go @@ -108,7 +108,7 @@ func (m *Miner) MineBlock(randomness []byte, lastBlockHeader *types2.BlockHeader return nil, nil } - txs := m.mempool.GetAllTransactions() + txs := m.mempool.GetTransactionsForNewBlock() if txs == nil { return nil, fmt.Errorf("there is no txes for processing") // skip new consensus round because there is no transaction for processing } From 5645d1951b329abe4e0b0fa2d11fbaac727bc7ee Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sun, 11 Jul 2021 01:07:04 +0300 Subject: [PATCH 34/59] Fix some typos --- blockchain/types/transaction.go | 5 +- node/network_service.go | 2 +- types/electionproof_test.go | 145 -------------------------------- 3 files changed, 2 insertions(+), 150 deletions(-) delete mode 100644 types/electionproof_test.go diff --git a/blockchain/types/transaction.go b/blockchain/types/transaction.go index abb4c6d..f892c66 100644 --- a/blockchain/types/transaction.go +++ b/blockchain/types/transaction.go @@ -31,8 +31,5 @@ func CreateTransaction(data []byte) *Transaction { func (tx *Transaction) ValidateHash() bool { h := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", tx.Timestamp.Unix(), tx.Hash))) - if bytes.Compare(h, tx.Hash) != 0 { - return false - } - return true + return bytes.Equal(h, tx.Hash) } diff --git a/node/network_service.go b/node/network_service.go index a5afb42..5b44b9a 100644 --- a/node/network_service.go +++ b/node/network_service.go @@ -73,7 +73,7 @@ func (s *NetworkService) Mempool(ctx context.Context, arg struct{}, reply *wire. } func (s *NetworkService) GetMempoolTxs(ctx context.Context, arg wire.GetMempoolTxsArg, reply *wire.GetMempoolTxsReply) { - if len(arg.Items) > MaxTransactionCountForRetrieving { + if len(arg.Items) > policy.MaxTransactionCountForRetrieving { pid, _ := gorpc.GetRequestSender(ctx) logrus.Warnf("Max tx count limit exceeded for GetMempoolTxs request of node %s", pid) reply.Error = fmt.Errorf("max tx count limit exceeded") 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) - } -} From 956de703e83f1893c1c690f01abcb1abe9f09e4e Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sun, 11 Jul 2021 01:07:35 +0300 Subject: [PATCH 35/59] Implement submission for dione task from block transaction --- consensus/consensus.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/consensus/consensus.go b/consensus/consensus.go index b115bc5..240beca 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -5,6 +5,8 @@ import ( "math/big" "sync" + "github.com/fxamacker/cbor/v2" + "github.com/Secured-Finance/dione/cache" "github.com/asaskevich/EventBus" @@ -22,6 +24,7 @@ import ( "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" @@ -237,6 +240,31 @@ func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Resu logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) return } + + // if we are miner for this block + // then post dione tasks to target chains (currently, only Ethereum) + if block.Header.Proposer == pcm.miner.address { + for _, v := range block.Data { + var task *types2.DioneTask + err := cbor.Unmarshal(v.Data, &task) + if err != nil { + logrus.Errorf("Failed to unmarshal transaction %x payload: %s", v.Hash, err.Error()) + continue // FIXME + } + reqIDNumber, ok := big.NewInt(0).SetString(task.RequestID, 10) + if !ok { + logrus.Errorf("Failed to parse request id number in task of tx %x", v.Hash) + continue // FIXME + } + + err = pcm.ethereumClient.SubmitRequestAnswer(reqIDNumber, task.Payload) + if err != nil { + logrus.Errorf("Failed to submit task in tx %x: %s", v.Hash, err.Error()) + continue // FIXME + } + } + } + pcm.state.ready <- true minedBlock, err := pcm.miner.MineBlock(res.Randomness(), block.Header) From 5aadbab60065f8e7f548a041ebf9a3cb60c80c27 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sun, 11 Jul 2021 03:08:03 +0300 Subject: [PATCH 36/59] Refactor dispute manager to new blockchain architecture --- blockchain/{blockpool.go => blockchain.go} | 0 consensus/dispute_manager.go | 213 ++++++++++++--------- node/node.go | 13 +- node/node_dep_providers.go | 17 +- 4 files changed, 146 insertions(+), 97 deletions(-) rename blockchain/{blockpool.go => blockchain.go} (100%) diff --git a/blockchain/blockpool.go b/blockchain/blockchain.go similarity index 100% rename from blockchain/blockpool.go rename to blockchain/blockchain.go diff --git a/consensus/dispute_manager.go b/consensus/dispute_manager.go index 70bfb7b..672ca9c 100644 --- a/consensus/dispute_manager.go +++ b/consensus/dispute_manager.go @@ -2,8 +2,19 @@ package consensus import ( "context" + "encoding/hex" "time" + 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" @@ -16,9 +27,10 @@ type DisputeManager struct { submissionMap map[string]*dioneOracle.DioneOracleSubmittedOracleRequest disputeMap map[string]*dioneDispute.DioneDisputeNewDispute voteWindow time.Duration + blockchain *blockchain.BlockChain } -func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, pcm *PBFTConsensusManager, voteWindow int) (*DisputeManager, error) { +func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, pcm *PBFTConsensusManager, voteWindow int, bc *blockchain.BlockChain) (*DisputeManager, error) { newSubmittionsChan, submSubscription, err := ethClient.SubscribeOnNewSubmittions(ctx) if err != nil { return nil, err @@ -36,6 +48,7 @@ func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, submissionMap: map[string]*dioneOracle.DioneOracleSubmittedOracleRequest{}, disputeMap: map[string]*dioneDispute.DioneDisputeNewDispute{}, voteWindow: time.Duration(voteWindow) * time.Second, + blockchain: bc, } go func() { @@ -62,95 +75,117 @@ func NewDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, return dm, nil } -func (dm *DisputeManager) onNewSubmission(submittion *dioneOracle.DioneOracleSubmittedOracleRequest) { - //c := dm.pcm.GetConsensusInfo(submittion.ReqID.String()) - //if c == nil { - // // todo: warn - // return - //} - // - //dm.submissionMap[submittion.ReqID.String()] = submittion - // - //submHashBytes := sha3.Sum256(submittion.Data) - //localHashBytes := sha3.Sum256(c.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) - // if err != nil { - // logrus.Errorf(err.Error()) - // return - // } - // disputeFinishTimer := time.NewTimer(dm.voteWindow) - // go func() { - // for { - // select { - // case <-dm.ctx.Done(): - // return - // case <-disputeFinishTimer.C: - // { - // d, ok := dm.disputeMap[reqID.String()] - // if !ok { - // logrus.Error("cannot finish dispute: it doesn't exist in manager's dispute map!") - // return - // } - // err := dm.ethClient.FinishDispute(d.Dhash) - // if err != nil { - // logrus.Errorf(err.Error()) - // return - // } - // disputeFinishTimer.Stop() - // return - // } - // } - // } - // }() - //} - // TODO refactor due to new architecture with blockchain +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[submission.ReqID.String()] = submission + + 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", submission.ReqID) + err := dm.ethClient.BeginDispute(block.Header.ProposerEth, submission.ReqID) + if err != nil { + logrus.Errorf(err.Error()) + return + } + disputeFinishTimer := time.NewTimer(dm.voteWindow) + go func() { + for { + select { + case <-dm.ctx.Done(): + return + case <-disputeFinishTimer.C: + { + d, ok := dm.disputeMap[submission.ReqID.String()] + if !ok { + logrus.Error("cannot finish dispute: it doesn't exist in manager's dispute map!") + return + } + err := dm.ethClient.FinishDispute(d.Dhash) + if err != nil { + logrus.Errorf(err.Error()) + return + } + disputeFinishTimer.Stop() + return + } + } + } + }() + } +} + +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 - // return - //} - // - //subm, ok := dm.submissionMap[dispute.RequestID.String()] - //if !ok { - // // todo: warn - // return - //} - // - //dm.disputeMap[dispute.RequestID.String()] = dispute - // - //if dispute.DisputeInitiator.Hex() == dm.ethClient.GetEthAddress().Hex() { - // return - //} - // - //submHashBytes := sha3.Sum256(subm.Data) - //localHashBytes := sha3.Sum256(c.Task.Payload) - //submHash := hex.EncodeToString(submHashBytes[:]) - //localHash := hex.EncodeToString(localHashBytes[:]) - //if submHash == localHash { - // err := dm.ethClient.VoteDispute(dispute.Dhash, false) - // if err != nil { - // logrus.Errorf(err.Error()) - // return - // } - //} - // - //err := dm.ethClient.VoteDispute(dispute.Dhash, true) - //if err != nil { - // logrus.Errorf(err.Error()) - // return - //} - // TODO refactor due to new architecture with blockchain + task, _, err := dm.findTaskAndBlockWithRequestID(dispute.RequestID.String()) + if err != nil { + logrus.Error(err) + return + } + + subm, ok := dm.submissionMap[dispute.RequestID.String()] + if !ok { + logrus.Warn("desired submission isn't found in map") + return + } + + dm.disputeMap[dispute.RequestID.String()] = dispute + + if dispute.DisputeInitiator.Hex() == dm.ethClient.GetEthAddress().Hex() { + return + } + + submHashBytes := sha3.Sum256(subm.Data) + localHashBytes := sha3.Sum256(task.Payload) + submHash := hex.EncodeToString(submHashBytes[:]) + localHash := hex.EncodeToString(localHashBytes[:]) + if submHash == localHash { + err := dm.ethClient.VoteDispute(dispute.Dhash, false) + if err != nil { + logrus.Errorf(err.Error()) + return + } + } + + err = dm.ethClient.VoteDispute(dispute.Dhash, true) + if err != nil { + logrus.Errorf(err.Error()) + return + } } diff --git a/node/node.go b/node/node.go index a3bae20..400e39d 100644 --- a/node/node.go +++ b/node/node.go @@ -9,6 +9,8 @@ import ( "os" "time" + "github.com/multiformats/go-multiaddr" + "github.com/asaskevich/EventBus" "github.com/fxamacker/cbor/v2" @@ -160,7 +162,14 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim r := provideP2PRPCClient(lhost) // initialize sync manager - sm, err := provideSyncManager(bus, bc, mp, r, baddrs[0], psb) // FIXME here we just pick up first bootstrap in list + + var baddr multiaddr.Multiaddr + if len(baddrs) == 0 { + baddr = nil + } else { + baddr = baddrs[0] + } + sm, err := provideSyncManager(bus, bc, mp, r, baddr, psb) // FIXME here we just pick up first bootstrap in list if err != nil { logrus.Fatal(err) } @@ -186,7 +195,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Random beacon subsystem has been initialized!") // initialize dispute subsystem - disputeManager, err := provideDisputeManager(context.TODO(), ethClient, consensusManager, config) + disputeManager, err := provideDisputeManager(context.TODO(), ethClient, consensusManager, config, bc) if err != nil { logrus.Fatal(err) } diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 3ec1836..6ddc7e1 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -56,8 +56,8 @@ func provideCache(config *config.Config) cache.Cache { 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 provideDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, pcm *consensus.PBFTConsensusManager, cfg *config.Config, bc *blockchain.BlockChain) (*consensus.DisputeManager, error) { + return consensus.NewDisputeManager(ctx, ethClient, pcm, cfg.Ethereum.DisputeVoteWindow, bc) } func provideMiner(peerID peer.ID, ethAddress common.Address, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *consensus.Miner { @@ -165,11 +165,16 @@ func provideMemPool() (*pool.Mempool, error) { } func provideSyncManager(bus EventBus.Bus, bp *blockchain.BlockChain, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { - addr, err := peer.AddrInfoFromP2pAddr(bootstrap) - if err != nil { - return nil, err + bootstrapPeerID := peer.ID("") + if bootstrap != nil { + addr, err := peer.AddrInfoFromP2pAddr(bootstrap) + if err != nil { + return nil, err + } + bootstrapPeerID = addr.ID } - return sync.NewSyncManager(bus, bp, mp, r, addr.ID, psb), nil + + return sync.NewSyncManager(bus, bp, mp, r, bootstrapPeerID, psb), nil } func provideP2PRPCClient(h host.Host) *gorpc.Client { From 2845a04704aaa172759bdf61d967468bac4552f1 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sun, 11 Jul 2021 03:32:58 +0300 Subject: [PATCH 37/59] Fix small bugs/mistakes/typos --- blockchain/blockchain.go | 14 ++++++++-- blockchain/sync/sync_mgr.go | 14 +++++----- blockchain/utils/index.go | 2 +- consensus/consensus.go | 28 +++++++++++++++---- node/network_service.go | 46 ++++++++++++++++---------------- node/node.go | 20 +++++++++----- node/node_dep_providers.go | 30 ++++++++++++++++++--- node/wire/get_mempool_tx.go | 1 - node/wire/get_range_of_blocks.go | 1 - node/wire/last_block_height.go | 1 - pubsub/pubsub_router.go | 1 + 11 files changed, 108 insertions(+), 50 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 28b6846..092b4ff 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "encoding/hex" "errors" + "os" "github.com/Secured-Finance/dione/blockchain/utils" @@ -41,12 +42,21 @@ func NewBlockChain(path string) (*BlockChain, error) { 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 = env.Open(path, 0, 0664) + err = os.MkdirAll(path, 0755) + if err != nil { + return nil, err + } + + err = env.Open(path, 0, 0755) if err != nil { return nil, err } @@ -111,7 +121,7 @@ func (bp *BlockChain) StoreBlock(block *types2.Block) error { } // update index "height -> block hash" - var heightBytes []byte + heightBytes := make([]byte, 8) binary.LittleEndian.PutUint64(heightBytes, block.Header.Height) err = bp.heightIndex.PutBytes(heightBytes, block.Header.Hash) if err != nil { diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 08fca6e..88d07a5 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -99,14 +99,15 @@ func (sm *syncManager) doInitialBlockPoolSync() error { } } + 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.Error != nil { - return reply.Error - } if reply.Height > ourLastHeight { heightCount := reply.Height - ourLastHeight @@ -150,6 +151,10 @@ func (sm *syncManager) doInitialBlockPoolSync() error { } 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 { @@ -187,9 +192,6 @@ func (sm *syncManager) doInitialMempoolSync() error { if err != nil { return err } - if getMempoolTxReply.Error != nil { - return getMempoolTxReply.Error - } for _, v := range getMempoolTxReply.Transactions { err := sm.mempool.StoreTx(&v) if err != nil { diff --git a/blockchain/utils/index.go b/blockchain/utils/index.go index ff3b074..24a7896 100644 --- a/blockchain/utils/index.go +++ b/blockchain/utils/index.go @@ -32,7 +32,7 @@ func NewIndex(name string, dbEnv *lmdb.Env, db lmdb.DBI) *Index { func (i *Index) PutUint64(key []byte, value uint64) error { return i.dbEnv.Update(func(txn *lmdb.Txn) error { - var data []byte + data := make([]byte, 8) binary.LittleEndian.PutUint64(data, value) return txn.Put(i.db, i.constructIndexKey(key), data, 0) }) diff --git a/consensus/consensus.go b/consensus/consensus.go index 240beca..e7d6af8 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -32,6 +32,10 @@ import ( "github.com/Secured-Finance/dione/pubsub" ) +var ( + ErrNoAcceptedBlocks = errors.New("there is no accepted blocks") +) + type StateStatus uint8 const ( @@ -52,7 +56,7 @@ type PBFTConsensusManager struct { validator *ConsensusValidator ethereumClient *ethclient.EthereumClient miner *Miner - blockPool pool.BlockPool + blockPool *pool.BlockPool blockchain blockchain.BlockChain state *State } @@ -66,7 +70,16 @@ type State struct { ready chan bool } -func NewPBFTConsensusManager(bus EventBus.Bus, psb *pubsub.PubSubRouter, minApprovals int, privKey crypto.PrivKey, ethereumClient *ethclient.EthereumClient, miner *Miner, bc *blockchain.BlockChain) *PBFTConsensusManager { +func NewPBFTConsensusManager( + bus EventBus.Bus, + psb *pubsub.PubSubRouter, + minApprovals int, + privKey crypto.PrivKey, + ethereumClient *ethclient.EthereumClient, + miner *Miner, + bc *blockchain.BlockChain, + bp *pool.BlockPool, +) *PBFTConsensusManager { pcm := &PBFTConsensusManager{} pcm.psb = psb pcm.miner = miner @@ -80,6 +93,7 @@ func NewPBFTConsensusManager(bus EventBus.Bus, psb *pubsub.PubSubRouter, minAppr status: StateStatusUnknown, } pcm.bus = bus + pcm.blockPool = bp pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types.PrePrepareMessage{}) pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types.PrepareMessage{}) pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types.CommitMessage{}) @@ -237,7 +251,11 @@ func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Resu defer pcm.state.mutex.Unlock() block, err := pcm.commitAcceptedBlocks() if err != nil { - logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) + if errors.Is(err, ErrNoAcceptedBlocks) { + logrus.Warnf("No accepted blocks for consensus round %d", pcm.state.blockHeight) + } else { + logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) + } return } @@ -245,7 +263,7 @@ func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Resu // then post dione tasks to target chains (currently, only Ethereum) if block.Header.Proposer == pcm.miner.address { for _, v := range block.Data { - var task *types2.DioneTask + var task types2.DioneTask err := cbor.Unmarshal(v.Data, &task) if err != nil { logrus.Errorf("Failed to unmarshal transaction %x payload: %s", v.Hash, err.Error()) @@ -291,7 +309,7 @@ func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Resu func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { blocks := pcm.blockPool.GetAllAcceptedBlocks() if blocks == nil { - return nil, errors.New("there is no accepted blocks") + return nil, ErrNoAcceptedBlocks } var maxStake *big.Int var selectedBlock *types3.Block diff --git a/node/network_service.go b/node/network_service.go index 5b44b9a..ff3fc04 100644 --- a/node/network_service.go +++ b/node/network_service.go @@ -18,39 +18,36 @@ import ( ) type NetworkService struct { - blockpool *blockchain.BlockChain - mempool *pool.Mempool - rpcClient *gorpc.Client + blockchain *blockchain.BlockChain + mempool *pool.Mempool + rpcClient *gorpc.Client } -func NewNetworkService(bp *blockchain.BlockChain) *NetworkService { +func NewNetworkService(bc *blockchain.BlockChain, mp *pool.Mempool) *NetworkService { return &NetworkService{ - blockpool: bp, + blockchain: bc, + mempool: mp, } } -func (s *NetworkService) LastBlockHeight(ctx context.Context, arg struct{}, reply *wire.LastBlockHeightReply) { - height, err := s.blockpool.GetLatestBlockHeight() +func (s *NetworkService) LastBlockHeight(ctx context.Context, arg struct{}, reply *wire.LastBlockHeightReply) error { + height, err := s.blockchain.GetLatestBlockHeight() if err != nil { - reply.Error = err - return + return err } reply.Height = height + return nil } -func (s *NetworkService) GetRangeOfBlocks(ctx context.Context, arg wire.GetRangeOfBlocksArg, reply *wire.GetRangeOfBlocksReply) { +func (s *NetworkService) GetRangeOfBlocks(ctx context.Context, arg wire.GetRangeOfBlocksArg, reply *wire.GetRangeOfBlocksReply) error { if arg.From > arg.To { - errText := "incorrect arguments: from > to" - reply.Error = &errText - return + return fmt.Errorf("incorrect arguments: from > to") } if arg.To-arg.From > policy.MaxBlockCountForRetrieving { - errText := "incorrect arguments: count of block for retrieving is exceeded the limit" - reply.Error = &errText - return + 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.blockpool.FetchBlockByHeight(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) @@ -58,9 +55,10 @@ func (s *NetworkService) GetRangeOfBlocks(ctx context.Context, arg wire.GetRange } reply.Blocks = append(reply.Blocks, *block) } + return nil } -func (s *NetworkService) Mempool(ctx context.Context, arg struct{}, reply *wire.InvMessage) { +func (s *NetworkService) Mempool(ctx context.Context, arg struct{}, reply *wire.InvMessage) error { txs := s.mempool.GetAllTransactions() // extract hashes of txs @@ -70,14 +68,15 @@ func (s *NetworkService) Mempool(ctx context.Context, arg struct{}, reply *wire. Hash: v.Hash, }) } + + return nil } -func (s *NetworkService) GetMempoolTxs(ctx context.Context, arg wire.GetMempoolTxsArg, reply *wire.GetMempoolTxsReply) { +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) - reply.Error = fmt.Errorf("max tx count limit exceeded") - return + return fmt.Errorf("max tx count limit exceeded") } for _, v := range arg.Items { @@ -86,10 +85,11 @@ func (s *NetworkService) GetMempoolTxs(ctx context.Context, arg wire.GetMempoolT if errors.Is(err, pool.ErrTxNotFound) { reply.NotFoundTxs = append(reply.NotFoundTxs, v) } else { - reply.Error = err - return + return err } } reply.Transactions = append(reply.Transactions, *tx) } + + return nil } diff --git a/node/node.go b/node/node.go index 400e39d..932b981 100644 --- a/node/node.go +++ b/node/node.go @@ -9,6 +9,8 @@ import ( "os" "time" + "github.com/Secured-Finance/dione/blockchain" + "github.com/multiformats/go-multiaddr" "github.com/asaskevich/EventBus" @@ -17,8 +19,6 @@ import ( "github.com/Secured-Finance/dione/types" - "github.com/Secured-Finance/dione/blockchain" - types2 "github.com/Secured-Finance/dione/blockchain/types" gorpc "github.com/libp2p/go-libp2p-gorpc" @@ -66,8 +66,9 @@ type Node struct { Miner *consensus.Miner Beacon beacon.BeaconNetworks DisputeManager *consensus.DisputeManager - BlockPool *blockchain.BlockChain + BlockPool *pool.BlockPool MemPool *pool.Mempool + BlockChain *blockchain.BlockChain SyncManager sync.SyncManager NetworkService *NetworkService NetworkRPCHost *gorpc.Server @@ -138,7 +139,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) } - n.BlockPool = bc + n.BlockChain = bc logrus.Info("Block pool database has been successfully initialized!") // initialize mempool @@ -149,7 +150,14 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim n.MemPool = mp logrus.Info("Mempool has been successfully initialized!") - ns := provideNetworkService(bc) + bp, err := provideBlockPool(mp) + if err != nil { + logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) + } + n.BlockPool = bp + logrus.Info("Blockpool has been successfully initialized!") + + ns := provideNetworkService(bc, mp) n.NetworkService = ns rpcHost := provideNetworkRPCHost(lhost) err = rpcHost.Register(ns) @@ -182,7 +190,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Mining subsystem has been initialized!") // initialize consensus subsystem - consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals) + consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals, bp) n.ConsensusManager = consensusManager logrus.Info("Consensus subsystem has been initialized!") diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 6ddc7e1..8f43b78 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -105,8 +105,26 @@ func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubR return pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap) } -func provideConsensusManager(bus EventBus.Bus, psb *pubsub.PubSubRouter, miner *consensus.Miner, bc *blockchain.BlockChain, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, minApprovals int) *consensus.PBFTConsensusManager { - return consensus.NewPBFTConsensusManager(bus, psb, minApprovals, privateKey, ethClient, miner, bc) +func provideConsensusManager( + bus EventBus.Bus, + psb *pubsub.PubSubRouter, + miner *consensus.Miner, + bc *blockchain.BlockChain, + ethClient *ethclient.EthereumClient, + privateKey crypto.PrivKey, + minApprovals int, + bp *pool.BlockPool, +) *consensus.PBFTConsensusManager { + return consensus.NewPBFTConsensusManager( + bus, + psb, + minApprovals, + privateKey, + ethClient, + miner, + bc, + bp, + ) } func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) (host.Host, error) { @@ -181,6 +199,10 @@ func provideP2PRPCClient(h host.Host) *gorpc.Client { return gorpc.NewClient(h, DioneProtocolID) } -func provideNetworkService(bp *blockchain.BlockChain) *NetworkService { - return NewNetworkService(bp) +func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *NetworkService { + return NewNetworkService(bp, mp) +} + +func provideBlockPool(mp *pool.Mempool) (*pool.BlockPool, error) { + return pool.NewBlockPool(mp) } diff --git a/node/wire/get_mempool_tx.go b/node/wire/get_mempool_tx.go index da364e5..b9881fb 100644 --- a/node/wire/get_mempool_tx.go +++ b/node/wire/get_mempool_tx.go @@ -9,5 +9,4 @@ type GetMempoolTxsArg struct { type GetMempoolTxsReply struct { Transactions []types.Transaction NotFoundTxs [][]byte - Error error } diff --git a/node/wire/get_range_of_blocks.go b/node/wire/get_range_of_blocks.go index 5ecc770..2c8321d 100644 --- a/node/wire/get_range_of_blocks.go +++ b/node/wire/get_range_of_blocks.go @@ -10,5 +10,4 @@ type GetRangeOfBlocksArg struct { type GetRangeOfBlocksReply struct { Blocks []types.Block FailedBlockHeights []uint64 // list of block heights the node was unable to retrieve - Error *string } diff --git a/node/wire/last_block_height.go b/node/wire/last_block_height.go index c7ceaac..eb3f2e3 100644 --- a/node/wire/last_block_height.go +++ b/node/wire/last_block_height.go @@ -2,5 +2,4 @@ package wire type LastBlockHeightReply struct { Height uint64 - Error error } diff --git a/pubsub/pubsub_router.go b/pubsub/pubsub_router.go index e76cae8..1c7e97a 100644 --- a/pubsub/pubsub_router.go +++ b/pubsub/pubsub_router.go @@ -34,6 +34,7 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR context: ctx, contextCancel: ctxCancel, handlers: make(map[PubSubMessageType][]Handler), + typeMapping: map[PubSubMessageType]interface{}{}, } var pbOptions []pubsub.Option From 6c18edd471f42e3862fd5dc44dcf3426526fc8ff Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sun, 11 Jul 2021 03:33:28 +0300 Subject: [PATCH 38/59] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8e77007..03dfc01 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ 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 From d7a1e8793917d74847a48e36adcb1f870e5542f5 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Mon, 12 Jul 2021 02:23:00 +0300 Subject: [PATCH 39/59] Make blockchain finally working! --- beacon/beacon.go | 3 +- beacon/drand/drand.go | 63 +++++----- blockchain/blockchain.go | 7 +- blockchain/pool/blockpool.go | 28 ++++- blockchain/pool/mempool.go | 13 +- blockchain/sync/sync_mgr.go | 15 ++- blockchain/types/block.go | 12 +- blockchain/types/transaction.go | 3 +- blockchain/utils/verification.go | 2 +- cache/inmemory_cache.go | 11 +- consensus/consensus.go | 204 +++++++++++++++++++------------ consensus/consensus_validator.go | 18 ++- consensus/miner.go | 9 +- consensus/utils.go | 39 ++++-- node/node.go | 24 ++-- node/node_dep_providers.go | 14 ++- pubsub/message.go | 7 +- pubsub/pubsub_router.go | 31 ++--- rpc/filecoin/filecoin.go | 2 +- types/electionproof.go | 5 +- 20 files changed, 313 insertions(+), 197 deletions(-) diff --git a/beacon/beacon.go b/beacon/beacon.go index e1a1503..9773573 100644 --- a/beacon/beacon.go +++ b/beacon/beacon.go @@ -34,8 +34,9 @@ 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 + NewEntries() <-chan types.BeaconEntry LatestBeaconRound() uint64 } diff --git a/beacon/drand/drand.go b/beacon/drand/drand.go index 1c4b493..a51aff5 100644 --- a/beacon/drand/drand.go +++ b/beacon/drand/drand.go @@ -8,8 +8,6 @@ import ( "github.com/Arceliar/phony" - "github.com/Secured-Finance/dione/consensus" - "github.com/Secured-Finance/dione/beacon" "github.com/drand/drand/chain" "github.com/drand/drand/client" @@ -38,13 +36,13 @@ type DrandBeacon struct { DrandClient client.Client PublicKey kyber.Point drandResultChannel <-chan client.Result + beaconEntryChannel chan types.BeaconEntry cacheLock sync.Mutex localCache map[uint64]types.BeaconEntry latestDrandRound uint64 - consensusManager *consensus.PBFTConsensusManager } -func NewDrandBeacon(ps *pubsub.PubSub, pcm *consensus.PBFTConsensusManager) (*DrandBeacon, error) { +func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { cfg := config.NewDrandConfig() drandChain, err := chain.InfoFromJSON(bytes.NewReader([]byte(cfg.ChainInfo))) @@ -83,14 +81,14 @@ func NewDrandBeacon(ps *pubsub.PubSub, pcm *consensus.PBFTConsensusManager) (*Dr } db := &DrandBeacon{ - DrandClient: drandClient, - localCache: make(map[uint64]types.BeaconEntry), - consensusManager: pcm, + DrandClient: drandClient, + localCache: make(map[uint64]types.BeaconEntry), } db.PublicKey = drandChain.PublicKey db.drandResultChannel = db.DrandClient.Watch(context.TODO()) + db.beaconEntryChannel = make(chan types.BeaconEntry) err = db.getLatestDrandResult() if err != nil { return nil, err @@ -106,7 +104,7 @@ func (db *DrandBeacon) getLatestDrandResult() error { log.Errorf("failed to get latest drand round: %v", err) return err } - db.cacheValue(newBeaconResultFromDrandResult(latestDround)) + db.cacheValue(newBeaconEntryFromDrandResult(latestDround)) db.updateLatestDrandRound(latestDround.Round()) return nil } @@ -120,43 +118,30 @@ func (db *DrandBeacon) loop(ctx context.Context) { } case res := <-db.drandResultChannel: { - db.cacheValue(newBeaconResultFromDrandResult(res)) + db.cacheValue(newBeaconEntryFromDrandResult(res)) db.updateLatestDrandRound(res.Round()) - db.consensusManager.NewDrandRound(db, res) + db.newEntry(res) } } } } -func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan beacon.BeaconResult { - out := make(chan beacon.BeaconResult, 1) +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() @@ -201,7 +186,17 @@ func (db *DrandBeacon) LatestBeaconRound() uint64 { return db.latestDrandRound } -func newBeaconResultFromDrandResult(res client.Result) types.BeaconEntry { +func (db *DrandBeacon) newEntry(res client.Result) { + db.Act(nil, func() { + db.beaconEntryChannel <- types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()}) + }) +} + +func (db *DrandBeacon) NewEntries() <-chan types.BeaconEntry { + return db.beaconEntryChannel +} + +func newBeaconEntryFromDrandResult(res client.Result) types.BeaconEntry { return types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()}) } diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 092b4ff..a46c39b 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -180,12 +180,13 @@ func (bp *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, e } return err } - err = cbor.Unmarshal(blockData, data) + err = cbor.Unmarshal(blockData, &data) return err }) if err != nil { return nil, err } + return data, nil } @@ -227,7 +228,7 @@ func (bp *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) { } func (bp *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) { - var heightBytes []byte + var heightBytes = make([]byte, 8) binary.LittleEndian.PutUint64(heightBytes, height) blockHash, err := bp.heightIndex.GetBytes(heightBytes) if err != nil { @@ -243,7 +244,7 @@ func (bp *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) { } func (bp *BlockChain) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) { - var heightBytes []byte + var heightBytes = make([]byte, 8) binary.LittleEndian.PutUint64(heightBytes, height) blockHash, err := bp.heightIndex.GetBytes(heightBytes) if err != nil { diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go index 48c3296..ae49e52 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -1,8 +1,11 @@ package pool import ( + "bytes" "encoding/hex" + "github.com/sirupsen/logrus" + "github.com/Secured-Finance/dione/blockchain/types" "github.com/Secured-Finance/dione/cache" ) @@ -29,8 +32,9 @@ func (bp *BlockPool) AddBlock(block *types.Block) error { } func (bp *BlockPool) GetBlock(blockhash []byte) (*types.Block, error) { - var block *types.Block - return block, bp.knownBlocks.Get(hex.EncodeToString(blockhash), &block) + var block types.Block + err := bp.knownBlocks.Get(hex.EncodeToString(blockhash), &block) + return &block, err } // PruneBlocks cleans known blocks list. It is called when new consensus round starts. @@ -53,13 +57,27 @@ func (bp *BlockPool) GetAllAcceptedBlocks() []*types.Block { } // PruneAcceptedBlocks cleans accepted blocks list. It is called when new consensus round starts. -func (bp *BlockPool) PruneAcceptedBlocks() { +func (bp *BlockPool) PruneAcceptedBlocks(committedBlock *types.Block) { for k, v := range bp.acceptedBlocks.Items() { block := v.(*types.Block) for _, v := range block.Data { - v.MerkleProof = nil - bp.mempool.StoreTx(v) // return transactions back to mempool + if !containsTx(committedBlock.Data, v) { + v.MerkleProof = nil + err := bp.mempool.StoreTx(v) // return transactions back to mempool + if err != nil { + logrus.Error(err) + } + } } bp.acceptedBlocks.Delete(k) } } + +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/blockchain/pool/mempool.go b/blockchain/pool/mempool.go index 22fec10..c94a99c 100644 --- a/blockchain/pool/mempool.go +++ b/blockchain/pool/mempool.go @@ -6,6 +6,8 @@ import ( "sort" "time" + "github.com/sirupsen/logrus" + types2 "github.com/Secured-Finance/dione/blockchain/types" "github.com/Secured-Finance/dione/consensus/policy" @@ -37,9 +39,16 @@ func NewMempool() (*Mempool, error) { func (mp *Mempool) StoreTx(tx *types2.Transaction) error { hashStr := hex.EncodeToString(tx.Hash) err := mp.cache.StoreWithTTL(DefaultTxPrefix+hashStr, tx, DefaultTxTTL) + logrus.Infof("Submitted new transaction in mempool with hash %x", tx.Hash) return err } +func (mp *Mempool) DeleteTx(txHash []byte) { + hashStr := hex.EncodeToString(txHash) + mp.cache.Delete(DefaultTxPrefix + hashStr) + logrus.Debugf("Deleted transaction from mempool %x", txHash) +} + func (mp *Mempool) GetTransactionsForNewBlock() []*types2.Transaction { var txForBlock []*types2.Transaction allTxs := mp.GetAllTransactions() @@ -63,8 +72,8 @@ func (mp *Mempool) GetAllTransactions() []*types2.Transaction { var allTxs []*types2.Transaction for _, v := range mp.cache.Items() { - tx := v.(types2.Transaction) - allTxs = append(allTxs, &tx) + tx := v.(*types2.Transaction) + allTxs = append(allTxs, tx) } return allTxs } diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 88d07a5..70ce135 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -8,6 +8,8 @@ import ( "strings" "sync" + "github.com/fxamacker/cbor/v2" + "github.com/asaskevich/EventBus" "github.com/Secured-Finance/dione/blockchain/utils" @@ -63,7 +65,7 @@ func NewSyncManager(bus EventBus.Bus, bp *blockchain.BlockChain, mp *pool.Mempoo psb: psb, } - psb.Hook(pubsub.NewTxMessageType, sm.onNewTransaction, types2.Transaction{}) + psb.Hook(pubsub.NewTxMessageType, sm.onNewTransaction) go func() { if err := sm.initialSync(); err != nil { @@ -236,10 +238,11 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) error { return nil } -func (sm *syncManager) onNewTransaction(message *pubsub.GenericMessage) { - tx, ok := message.Payload.(types2.Transaction) - if !ok { - logrus.Warn("failed to convert payload to Transaction") +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 } @@ -248,7 +251,7 @@ func (sm *syncManager) onNewTransaction(message *pubsub.GenericMessage) { return } // TODO add more checks on tx - err := sm.mempool.StoreTx(&tx) + err = sm.mempool.StoreTx(&tx) if err != nil { logrus.Warnf("failed to store incoming transaction in mempool: %s", err.Error()) } diff --git a/blockchain/types/block.go b/blockchain/types/block.go index ff0877e..262599d 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -26,7 +26,7 @@ type BlockHeader struct { Hash []byte LastHash []byte LastHashProof *merkletree.Proof - Proposer peer.ID + Proposer *peer.ID ProposerEth common.Address Signature []byte BeaconEntry types.BeaconEntry @@ -78,11 +78,19 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth comm 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, + Proposer: &proposer, ProposerEth: minerEth, Signature: s, Hash: blockHash, diff --git a/blockchain/types/transaction.go b/blockchain/types/transaction.go index f892c66..a3b8ebb 100644 --- a/blockchain/types/transaction.go +++ b/blockchain/types/transaction.go @@ -30,6 +30,7 @@ func CreateTransaction(data []byte) *Transaction { } func (tx *Transaction) ValidateHash() bool { - h := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", tx.Timestamp.Unix(), tx.Hash))) + encodedData := hex.EncodeToString(tx.Data) + h := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", tx.Timestamp.Unix(), encodedData))) return bytes.Equal(h, tx.Hash) } diff --git a/blockchain/utils/verification.go b/blockchain/utils/verification.go index bd5f0ba..964c32d 100644 --- a/blockchain/utils/verification.go +++ b/blockchain/utils/verification.go @@ -10,7 +10,7 @@ import ( func VerifyTx(blockHeader *types.BlockHeader, tx *types.Transaction) error { if tx.MerkleProof == nil { - return fmt.Errorf("block transaction hasn't merkle proof") + return fmt.Errorf("block transaction doesn't have merkle proof") } txProofVerified, err := merkletree.VerifyProofUsing(tx.Hash, false, tx.MerkleProof, [][]byte{blockHeader.Hash}, keccak256.New()) if err != nil { diff --git a/cache/inmemory_cache.go b/cache/inmemory_cache.go index fd4333f..902180a 100644 --- a/cache/inmemory_cache.go +++ b/cache/inmemory_cache.go @@ -1,6 +1,8 @@ package cache import ( + "fmt" + "reflect" "time" "github.com/patrickmn/go-cache" @@ -37,7 +39,14 @@ func (imc *InMemoryCache) Get(key string, value interface{}) error { if !exists { return ErrNotFound } - value = v + 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 } diff --git a/consensus/consensus.go b/consensus/consensus.go index e7d6af8..86cbb02 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -5,6 +5,8 @@ import ( "math/big" "sync" + "github.com/Secured-Finance/dione/beacon" + "github.com/fxamacker/cbor/v2" "github.com/Secured-Finance/dione/cache" @@ -13,8 +15,6 @@ import ( "github.com/Secured-Finance/dione/blockchain" - "github.com/drand/drand/client" - "github.com/Arceliar/phony" types3 "github.com/Secured-Finance/dione/blockchain/types" @@ -57,7 +57,8 @@ type PBFTConsensusManager struct { ethereumClient *ethclient.EthereumClient miner *Miner blockPool *pool.BlockPool - blockchain blockchain.BlockChain + mempool *pool.Mempool + blockchain *blockchain.BlockChain state *State } @@ -67,7 +68,7 @@ type State struct { randomness []byte blockHeight uint64 status StateStatus - ready chan bool + ready bool } func NewPBFTConsensusManager( @@ -79,33 +80,47 @@ func NewPBFTConsensusManager( miner *Miner, bc *blockchain.BlockChain, bp *pool.BlockPool, + b beacon.BeaconNetwork, + mempool *pool.Mempool, ) *PBFTConsensusManager { pcm := &PBFTConsensusManager{} pcm.psb = psb pcm.miner = miner - pcm.validator = NewConsensusValidator(miner, bc) + pcm.validator = NewConsensusValidator(miner, bc, b) pcm.msgLog = NewConsensusMessageLog() pcm.minApprovals = minApprovals pcm.privKey = privKey pcm.ethereumClient = ethereumClient pcm.state = &State{ - ready: make(chan bool, 1), + ready: false, status: StateStatusUnknown, } pcm.bus = bus pcm.blockPool = bp - pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types.PrePrepareMessage{}) - pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types.PrepareMessage{}) - pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types.CommitMessage{}) - bus.SubscribeOnce("sync:initialSyncCompleted", func() { - pcm.state.ready <- true - }) + pcm.mempool = mempool + pcm.blockchain = bc + pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare) + pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare) + pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit) + //bus.SubscribeOnce("sync:initialSyncCompleted", func() { + // pcm.state.ready = true + //}) + height, _ := pcm.blockchain.GetLatestBlockHeight() + pcm.state.blockHeight = height + 1 + go func() { + for { + select { + case e := <-b.Beacon.NewEntries(): + { + pcm.NewDrandRound(nil, e) + } + } + } + }() return pcm } func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { - pcm.state.mutex.Lock() - defer pcm.state.mutex.Unlock() prePrepareMsg, err := NewMessage(types.ConsensusMessage{Block: blk}, types.ConsensusMessageTypePrePrepare, pcm.privKey) if err != nil { return err @@ -116,33 +131,33 @@ func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { return nil } -func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage) { +func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) { pcm.state.mutex.Lock() defer pcm.state.mutex.Unlock() - prePrepare, ok := message.Payload.(types.PrePrepareMessage) - if !ok { - logrus.Warn("failed to convert payload to PrePrepare message") + var prePrepare types.PrePrepareMessage + err := cbor.Unmarshal(message.Payload, &prePrepare) + if err != nil { + logrus.Errorf("failed to convert payload to PrePrepare message: %s", err.Error()) return } - if prePrepare.Block.Header.Proposer == pcm.miner.address { + if *prePrepare.Block.Header.Proposer == pcm.miner.address { return } cmsg := types.ConsensusMessage{ - Type: types.ConsensusMessageTypePrePrepare, - From: message.From, - Block: prePrepare.Block, + Type: types.ConsensusMessageTypePrePrepare, + From: message.From, + Block: prePrepare.Block, + Blockhash: prePrepare.Block.Header.Hash, } - <-pcm.state.ready - if pcm.msgLog.Exists(cmsg) { - logrus.Debugf("received existing pre_prepare msg, dropping...") + logrus.Tracef("received existing pre_prepare msg for block %x", cmsg.Block.Header.Hash) return } if !pcm.validator.Valid(cmsg, map[string]interface{}{"randomness": pcm.state.randomness}) { - logrus.Warn("received invalid pre_prepare msg, dropping...") + logrus.Warnf("received invalid pre_prepare msg for block %x", cmsg.Block.Header.Hash) return } @@ -159,12 +174,13 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage pcm.state.status = StateStatusPrePrepared } -func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { +func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { pcm.state.mutex.Lock() defer pcm.state.mutex.Unlock() - prepare, ok := message.Payload.(types.PrepareMessage) - if !ok { - logrus.Warn("failed to convert payload to Prepare message") + 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 } @@ -176,17 +192,17 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { } if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { - logrus.Debugf("received unknown block") + logrus.Warnf("received unknown block %x", cmsg.Blockhash) return } if pcm.msgLog.Exists(cmsg) { - logrus.Debugf("received existing prepare msg, dropping...") + logrus.Tracef("received existing prepare msg for block %x", cmsg.Blockhash) return } if !pcm.validator.Valid(cmsg, nil) { - logrus.Warn("received invalid prepare msg, dropping...") + logrus.Warnf("received invalid prepare msg for block %x", cmsg.Blockhash) return } @@ -196,18 +212,20 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) { commitMsg, err := NewMessage(cmsg, types.ConsensusMessageTypeCommit, pcm.privKey) if err != nil { logrus.Errorf("failed to create commit message: %v", err) + return } pcm.psb.BroadcastToServiceTopic(commitMsg) pcm.state.status = StateStatusPrepared } } -func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { +func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { pcm.state.mutex.Lock() defer pcm.state.mutex.Unlock() - commit, ok := message.Payload.(types.CommitMessage) - if !ok { - logrus.Warn("failed to convert payload to Prepare message") + 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 } @@ -215,20 +233,20 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { Type: types.ConsensusMessageTypeCommit, From: message.From, Blockhash: commit.Blockhash, - Signature: commit.Signature, // TODO check the signature + Signature: commit.Signature, } if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { - logrus.Debugf("received unknown block") + logrus.Warnf("received unknown block %x", cmsg.Blockhash) return } if pcm.msgLog.Exists(cmsg) { - logrus.Debugf("received existing commit msg, dropping...") + logrus.Tracef("received existing commit msg for block %x", cmsg.Blockhash) return } if !pcm.validator.Valid(cmsg, nil) { - logrus.Warn("received invalid commit msg, dropping...") + logrus.Warnf("received invalid commit msg for block %x", cmsg.Blockhash) return } @@ -237,7 +255,7 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals { block, err := pcm.blockPool.GetBlock(cmsg.Blockhash) if err != nil { - logrus.Debug(err) + logrus.Error(err) return } pcm.blockPool.AddAcceptedBlock(block) @@ -245,58 +263,87 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) { } } -func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Result) { +func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, entry types2.BeaconEntry) { pcm.Act(from, func() { pcm.state.mutex.Lock() defer pcm.state.mutex.Unlock() block, err := pcm.commitAcceptedBlocks() if err != nil { if errors.Is(err, ErrNoAcceptedBlocks) { - logrus.Warnf("No accepted blocks for consensus round %d", pcm.state.blockHeight) + logrus.Infof("No accepted blocks for consensus round %d", pcm.state.blockHeight) } else { logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) - } - return - } - - // if we are miner for this block - // then post dione tasks to target chains (currently, only Ethereum) - if block.Header.Proposer == pcm.miner.address { - for _, v := range block.Data { - var task types2.DioneTask - err := cbor.Unmarshal(v.Data, &task) - if err != nil { - logrus.Errorf("Failed to unmarshal transaction %x payload: %s", v.Hash, err.Error()) - continue // FIXME - } - reqIDNumber, ok := big.NewInt(0).SetString(task.RequestID, 10) - if !ok { - logrus.Errorf("Failed to parse request id number in task of tx %x", v.Hash) - continue // FIXME - } - - err = pcm.ethereumClient.SubmitRequestAnswer(reqIDNumber, task.Payload) - if err != nil { - logrus.Errorf("Failed to submit task in tx %x: %s", v.Hash, err.Error()) - continue // FIXME - } + return } } - pcm.state.ready <- true + if block != nil { + // broadcast new block + var newBlockMessage pubsub.PubSubMessage + newBlockMessage.Type = pubsub.NewBlockMessageType + blockSerialized, err := cbor.Marshal(block) + if err != nil { + logrus.Errorf("Failed to serialize block %x for broadcasting!", block.Header.Hash) + } else { + newBlockMessage.Payload = blockSerialized + pcm.psb.BroadcastToServiceTopic(&newBlockMessage) + } - minedBlock, err := pcm.miner.MineBlock(res.Randomness(), block.Header) + // if we are miner for this block + // then post dione tasks to target chains (currently, only Ethereum) + if *block.Header.Proposer == pcm.miner.address { + for _, v := range block.Data { + var task types2.DioneTask + err := cbor.Unmarshal(v.Data, &task) + if err != nil { + logrus.Errorf("Failed to unmarshal transaction %x payload: %s", v.Hash, err.Error()) + continue // FIXME + } + reqIDNumber, ok := big.NewInt(0).SetString(task.RequestID, 10) + if !ok { + logrus.Errorf("Failed to parse request id number in task of tx %x", v.Hash) + continue // FIXME + } + + err = pcm.ethereumClient.SubmitRequestAnswer(reqIDNumber, task.Payload) + if err != nil { + logrus.Errorf("Failed to submit task in tx %x: %s", v.Hash, err.Error()) + continue // FIXME + } + } + } + + pcm.state.blockHeight = pcm.state.blockHeight + 1 + } + + // get latest block + height, err := pcm.blockchain.GetLatestBlockHeight() if err != nil { - logrus.Errorf("Failed to mine the block: %s", err.Error()) + logrus.Error(err) + return + } + blockHeader, err := pcm.blockchain.FetchBlockHeaderByHeight(height) + if err != nil { + logrus.Error(err) return } - pcm.state.drandRound = res.Round() - pcm.state.randomness = res.Randomness() - pcm.state.blockHeight = pcm.state.blockHeight + 1 + pcm.state.drandRound = entry.Round + pcm.state.randomness = entry.Data + + minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round, blockHeader) + if err != nil { + if errors.Is(err, ErrNoTxForBlock) { + logrus.Info("Skipping consensus round, because we don't have transactions in mempool for including into block") + } else { + logrus.Errorf("Failed to mine the block: %s", err.Error()) + } + return + } // if we are round winner if minedBlock != nil { + logrus.Infof("We are elected in consensus round %d", pcm.state.blockHeight) err = pcm.propose(minedBlock) if err != nil { logrus.Errorf("Failed to propose the block: %s", err.Error()) @@ -327,7 +374,10 @@ func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { maxStake = stake selectedBlock = v } - logrus.Debugf("Selected block of miner %s", selectedBlock.Header.ProposerEth.Hex()) - pcm.blockPool.PruneAcceptedBlocks() + logrus.Infof("Committed block %x with height %d of miner %s", selectedBlock.Header.Hash, selectedBlock.Header.Height, selectedBlock.Header.Proposer.String()) + pcm.blockPool.PruneAcceptedBlocks(selectedBlock) + for _, v := range selectedBlock.Data { + pcm.mempool.DeleteTx(v.Hash) + } return selectedBlock, pcm.blockchain.StoreBlock(selectedBlock) } diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 571cc36..90145e7 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -2,9 +2,12 @@ package consensus import ( "bytes" + "context" "fmt" "sync" + "github.com/Secured-Finance/dione/beacon" + types3 "github.com/Secured-Finance/dione/blockchain/types" "github.com/Secured-Finance/dione/blockchain" @@ -22,13 +25,15 @@ import ( type ConsensusValidator struct { validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool miner *Miner + beacon beacon.BeaconNetwork blockchain *blockchain.BlockChain } -func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain) *ConsensusValidator { +func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain, b beacon.BeaconNetwork) *ConsensusValidator { cv := &ConsensusValidator{ miner: miner, blockchain: bc, + beacon: b, } cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool{ @@ -64,7 +69,7 @@ func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain) *ConsensusVa return false } if bytes.Compare(msg.Block.Header.LastHash, previousBlockHeader.Hash) != 0 { - logrus.Error("block header has invalid last block hash") + logrus.Errorf("block header has invalid last block hash (expected: %x, actual %x)", previousBlockHeader.Hash, msg.Block.Header.LastHash) return false } @@ -101,8 +106,13 @@ func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain) *ConsensusVa return false } + res, err := b.Beacon.Entry(context.TODO(), msg.Block.Header.ElectionProof.RandomnessRound) + if err != nil { + logrus.Error(err) + return false + } eproofRandomness, err := DrawRandomness( - metadata["randomness"].([]byte), + res.Data, crypto.DomainSeparationTag_ElectionProofProduction, msg.Block.Header.Height, proposerBuf, @@ -111,7 +121,7 @@ func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain) *ConsensusVa logrus.Errorf("failed to draw ElectionProof randomness: %s", err.Error()) return false } - err = VerifyVRF(msg.Block.Header.Proposer, eproofRandomness, msg.Block.Header.ElectionProof.VRFProof) + err = VerifyVRF(*msg.Block.Header.Proposer, eproofRandomness, msg.Block.Header.ElectionProof.VRFProof) if err != nil { logrus.Errorf("failed to verify election proof vrf: %v", err) return false diff --git a/consensus/miner.go b/consensus/miner.go index a4527a3..5b6891f 100644 --- a/consensus/miner.go +++ b/consensus/miner.go @@ -19,6 +19,10 @@ import ( "github.com/sirupsen/logrus" ) +var ( + ErrNoTxForBlock = fmt.Errorf("no transactions for including into block") +) + type Miner struct { address peer.ID ethAddress common.Address @@ -85,7 +89,7 @@ func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, error) { return mStake, nStake, nil } -func (m *Miner) MineBlock(randomness []byte, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) { +func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) { logrus.Debug("attempting to mine the block at epoch: ", lastBlockHeader.Height+1) if err := m.UpdateCurrentStakeInfo(); err != nil { @@ -96,6 +100,7 @@ func (m *Miner) MineBlock(randomness []byte, lastBlockHeader *types2.BlockHeader lastBlockHeader.Height+1, m.address, randomness, + randomnessRound, m.minerStake, m.networkStake, m.privateKey, @@ -110,7 +115,7 @@ func (m *Miner) MineBlock(randomness []byte, lastBlockHeader *types2.BlockHeader txs := m.mempool.GetTransactionsForNewBlock() if txs == nil { - return nil, fmt.Errorf("there is no txes for processing") // skip new consensus round because there is no transaction for processing + return nil, ErrNoTxForBlock // skip new consensus round because there is no transaction for processing } newBlock, err := types2.CreateBlock(lastBlockHeader, txs, m.ethAddress, m.privateKey, winner) diff --git a/consensus/utils.go b/consensus/utils.go index 490e992..7d62f77 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" + "github.com/fxamacker/cbor/v2" + "github.com/Secured-Finance/dione/pubsub" types2 "github.com/Secured-Finance/dione/consensus/types" @@ -30,7 +32,7 @@ func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error { if err != nil { return err } - ok, err := pk.Verify(vrfproof, vrfBase) + ok, err := pk.Verify(vrfBase, vrfproof) if err != nil { return err } @@ -42,7 +44,7 @@ func VerifyVRF(worker peer.ID, vrfBase, vrfproof []byte) error { } func IsRoundWinner(round uint64, - worker peer.ID, randomness []byte, minerStake, networkStake *big.Int, privKey crypto.PrivKey) (*types.ElectionProof, error) { + worker peer.ID, randomness []byte, randomnessRound uint64, minerStake, networkStake *big.Int, privKey crypto.PrivKey) (*types.ElectionProof, error) { buf, err := worker.MarshalBinary() if err != nil { @@ -59,7 +61,7 @@ func IsRoundWinner(round uint64, return nil, fmt.Errorf("failed to compute VRF: %w", err) } - ep := &types.ElectionProof{VRFProof: vrfout} + ep := &types.ElectionProof{VRFProof: vrfout, RandomnessRound: randomnessRound} j := ep.ComputeWinCount(minerStake, networkStake) ep.WinCount = j if j < 1 { @@ -90,29 +92,38 @@ func DrawRandomness(rbase []byte, pers crypto2.DomainSeparationTag, round uint64 return h.Sum(nil), nil } -func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, privKey crypto.PrivKey) (*pubsub.GenericMessage, error) { - var message pubsub.GenericMessage +func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, privKey crypto.PrivKey) (*pubsub.PubSubMessage, error) { + var message pubsub.PubSubMessage switch typ { case types2.ConsensusMessageTypePrePrepare: { message.Type = pubsub.PrePrepareMessageType - message.Payload = types2.PrePrepareMessage{ + 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 - pm := types2.PrepareMessage{ - Blockhash: cmsg.Blockhash, - } signature, err := privKey.Sign(cmsg.Blockhash) if err != nil { return nil, fmt.Errorf("failed to create signature: %v", err) } - pm.Signature = signature - message.Payload = pm + pm := types2.PrepareMessage{ + Blockhash: cmsg.Block.Header.Hash, + 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: @@ -126,7 +137,11 @@ func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, p return nil, fmt.Errorf("failed to create signature: %v", err) } pm.Signature = signature - message.Payload = pm + 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 } } diff --git a/node/node.go b/node/node.go index 932b981..212b4c2 100644 --- a/node/node.go +++ b/node/node.go @@ -7,6 +7,8 @@ import ( "fmt" "io/ioutil" "os" + "path" + "runtime" "time" "github.com/Secured-Finance/dione/blockchain" @@ -64,7 +66,7 @@ type Node struct { Ethereum *ethclient.EthereumClient ConsensusManager *consensus.PBFTConsensusManager Miner *consensus.Miner - Beacon beacon.BeaconNetworks + Beacon beacon.BeaconNetwork DisputeManager *consensus.DisputeManager BlockPool *pool.BlockPool MemPool *pool.Mempool @@ -189,19 +191,19 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim n.Miner = miner logrus.Info("Mining subsystem has been initialized!") - // initialize consensus subsystem - consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals, bp) - n.ConsensusManager = consensusManager - logrus.Info("Consensus subsystem has been initialized!") - // initialize random beacon network subsystem - randomBeaconNetwork, err := provideBeacon(psb.Pubsub, consensusManager) + randomBeaconNetwork, err := provideBeacon(psb.Pubsub) if err != nil { logrus.Fatal(err) } n.Beacon = randomBeaconNetwork logrus.Info("Random beacon subsystem has been initialized!") + // initialize consensus subsystem + consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals, bp, randomBeaconNetwork, mp) + n.ConsensusManager = consensusManager + logrus.Info("Consensus subsystem has been initialized!") + // initialize dispute subsystem disputeManager, err := provideDisputeManager(context.TODO(), ethClient, consensusManager, config, bc) if err != nil { @@ -344,6 +346,14 @@ func (n *Node) setupRPCClients() error { } func Start() { + logrus.SetReportCaller(true) + logrus.SetFormatter(&logrus.TextFormatter{ + CallerPrettyfier: func(f *runtime.Frame) (string, string) { + filename := path.Base(f.File) + return "", fmt.Sprintf("%s:%d:", filename, f.Line) + }, + }) + configPath := flag.String("config", "", "Path to config") verbose := flag.Bool("verbose", false, "Verbose logging") flag.Parse() diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 8f43b78..124b97d 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -64,15 +64,13 @@ func provideMiner(peerID peer.ID, ethAddress common.Address, ethClient *ethclien return consensus.NewMiner(peerID, ethAddress, ethClient, privateKey, mempool) } -func provideBeacon(ps *pubsub2.PubSub, pcm *consensus.PBFTConsensusManager) (beacon.BeaconNetworks, error) { - networks := beacon.BeaconNetworks{} - bc, err := drand2.NewDrandBeacon(ps, pcm) +func provideBeacon(ps *pubsub2.PubSub) (beacon.BeaconNetwork, error) { + bc, err := drand2.NewDrandBeacon(ps) if err != nil { - return nil, fmt.Errorf("failed to setup drand beacon: %w", err) + return beacon.BeaconNetwork{}, fmt.Errorf("failed to setup drand beacon: %w", err) } - networks = append(networks, beacon.BeaconNetwork{Start: config.DrandChainGenesisTime, Beacon: bc}) // NOTE: currently we use only one network - return networks, nil + return beacon.BeaconNetwork{Start: config.DrandChainGenesisTime, Beacon: bc}, nil } // FIXME: do we really need this? @@ -114,6 +112,8 @@ func provideConsensusManager( privateKey crypto.PrivKey, minApprovals int, bp *pool.BlockPool, + b beacon.BeaconNetwork, + mp *pool.Mempool, ) *consensus.PBFTConsensusManager { return consensus.NewPBFTConsensusManager( bus, @@ -124,6 +124,8 @@ func provideConsensusManager( miner, bc, bp, + b, + mp, ) } diff --git a/pubsub/message.go b/pubsub/message.go index 4b803f3..5537d51 100644 --- a/pubsub/message.go +++ b/pubsub/message.go @@ -13,13 +13,8 @@ const ( NewBlockMessageType ) -type GenericMessage struct { - Type PubSubMessageType - From peer.ID `cbor:"-"` - Payload interface{} -} - type PubSubMessage struct { Type PubSubMessageType + From peer.ID `cbor:"-"` Payload []byte } diff --git a/pubsub/pubsub_router.go b/pubsub/pubsub_router.go index 1c7e97a..4653583 100644 --- a/pubsub/pubsub_router.go +++ b/pubsub/pubsub_router.go @@ -21,10 +21,9 @@ type PubSubRouter struct { handlers map[PubSubMessageType][]Handler oracleTopicName string oracleTopic *pubsub.Topic - typeMapping map[PubSubMessageType]interface{} // message type -> sample } -type Handler func(message *GenericMessage) +type Handler func(message *PubSubMessage) func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubRouter { ctx, ctxCancel := context.WithCancel(context.Background()) @@ -34,7 +33,6 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR context: ctx, contextCancel: ctxCancel, handlers: make(map[PubSubMessageType][]Handler), - typeMapping: map[PubSubMessageType]interface{}{}, } var pbOptions []pubsub.Option @@ -104,30 +102,16 @@ func (psr *PubSubRouter) handleMessage(p *pubsub.Message) { if senderPeerID == psr.node.ID() { return } - var genericMessage PubSubMessage - var message GenericMessage - err = cbor.Unmarshal(p.Data, &genericMessage) - if err != nil { - logrus.Warn("Unable to decode pubsub message data! " + err.Error()) - return - } - sampleMsg, ok := psr.typeMapping[genericMessage.Type] - if !ok { - logrus.Warnf("Unknown message type %d: we have no clue how to decode it", genericMessage.Type) - return - } - destMsg := sampleMsg - err = cbor.Unmarshal(genericMessage.Payload, &destMsg) + var message PubSubMessage + err = cbor.Unmarshal(p.Data, &message) if err != nil { logrus.Warn("Unable to decode pubsub message data! " + err.Error()) return } message.From = senderPeerID - message.Type = genericMessage.Type - message.Payload = destMsg - handlers, ok := psr.handlers[genericMessage.Type] + handlers, ok := psr.handlers[message.Type] if !ok { - logrus.Warn("Dropping pubsub message " + string(genericMessage.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 { @@ -135,16 +119,15 @@ func (psr *PubSubRouter) handleMessage(p *pubsub.Message) { } } -func (psr *PubSubRouter) Hook(messageType PubSubMessageType, handler Handler, sample interface{}) { +func (psr *PubSubRouter) Hook(messageType PubSubMessageType, handler Handler) { _, ok := psr.handlers[messageType] if !ok { psr.handlers[messageType] = []Handler{} } psr.handlers[messageType] = append(psr.handlers[messageType], handler) - psr.typeMapping[messageType] = sample } -func (psr *PubSubRouter) BroadcastToServiceTopic(msg *GenericMessage) 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/types/electionproof.go b/types/electionproof.go index 62a7d7a..5350dde 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 From 13951d5c32292c056b420b87a7a7cf8778400f44 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 15 Jul 2021 23:51:22 +0300 Subject: [PATCH 40/59] Implement event bus for cross-communication between Dione components, overhaul logging part --- beacon/beacon.go | 1 - beacon/drand/drand.go | 21 +--- blockchain/blockchain.go | 27 ++-- blockchain/pool/blockpool.go | 22 +++- blockchain/pool/mempool.go | 16 ++- consensus/consensus.go | 208 ++++++++++++++++--------------- consensus/consensus_validator.go | 5 +- consensus/miner.go | 2 +- node/node.go | 36 +++--- node/node_dep_providers.go | 16 +-- 10 files changed, 200 insertions(+), 154 deletions(-) diff --git a/beacon/beacon.go b/beacon/beacon.go index 9773573..fa4e57b 100644 --- a/beacon/beacon.go +++ b/beacon/beacon.go @@ -36,7 +36,6 @@ type BeaconNetwork struct { type BeaconAPI interface { Entry(context.Context, uint64) (types.BeaconEntry, error) VerifyEntry(types.BeaconEntry, types.BeaconEntry) error - NewEntries() <-chan types.BeaconEntry LatestBeaconRound() uint64 } diff --git a/beacon/drand/drand.go b/beacon/drand/drand.go index a51aff5..b555ed6 100644 --- a/beacon/drand/drand.go +++ b/beacon/drand/drand.go @@ -6,7 +6,7 @@ import ( "fmt" "sync" - "github.com/Arceliar/phony" + "github.com/asaskevich/EventBus" "github.com/Secured-Finance/dione/beacon" "github.com/drand/drand/chain" @@ -32,17 +32,16 @@ var log = logrus.WithFields(logrus.Fields{ }) type DrandBeacon struct { - phony.Inbox DrandClient client.Client PublicKey kyber.Point drandResultChannel <-chan client.Result - beaconEntryChannel chan types.BeaconEntry cacheLock sync.Mutex localCache map[uint64]types.BeaconEntry latestDrandRound uint64 + bus EventBus.Bus } -func NewDrandBeacon(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))) @@ -83,12 +82,12 @@ func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { db := &DrandBeacon{ DrandClient: drandClient, localCache: make(map[uint64]types.BeaconEntry), + bus: bus, } db.PublicKey = drandChain.PublicKey db.drandResultChannel = db.DrandClient.Watch(context.TODO()) - db.beaconEntryChannel = make(chan types.BeaconEntry) err = db.getLatestDrandResult() if err != nil { return nil, err @@ -120,7 +119,7 @@ func (db *DrandBeacon) loop(ctx context.Context) { { db.cacheValue(newBeaconEntryFromDrandResult(res)) db.updateLatestDrandRound(res.Round()) - db.newEntry(res) + db.bus.Publish("beacon:newEntry", types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()})) } } } @@ -186,16 +185,6 @@ func (db *DrandBeacon) LatestBeaconRound() uint64 { return db.latestDrandRound } -func (db *DrandBeacon) newEntry(res client.Result) { - db.Act(nil, func() { - db.beaconEntryChannel <- types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()}) - }) -} - -func (db *DrandBeacon) NewEntries() <-chan types.BeaconEntry { - return db.beaconEntryChannel -} - func newBeaconEntryFromDrandResult(res client.Result) types.BeaconEntry { return types.NewBeaconEntry(res.Round(), res.Randomness(), map[string]interface{}{"signature": res.Signature()}) } diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index a46c39b..6d5874c 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -6,6 +6,8 @@ import ( "errors" "os" + "github.com/asaskevich/EventBus" + "github.com/Secured-Finance/dione/blockchain/utils" types2 "github.com/Secured-Finance/dione/blockchain/types" @@ -31,10 +33,13 @@ type BlockChain struct { db lmdb.DBI metadataIndex *utils.Index heightIndex *utils.Index + bus EventBus.Bus } -func NewBlockChain(path string) (*BlockChain, error) { - chain := &BlockChain{} +func NewBlockChain(path string, bus EventBus.Bus) (*BlockChain, error) { + chain := &BlockChain{ + bus: bus, + } // configure lmdb env env, err := lmdb.NewEnv() @@ -84,7 +89,11 @@ func NewBlockChain(path string) (*BlockChain, error) { } func (bp *BlockChain) setLatestBlockHeight(height uint64) error { - return bp.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height) + err := bp.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height) + if err != nil { + return err + } + return nil } func (bp *BlockChain) GetLatestBlockHeight() (uint64, error) { @@ -134,17 +143,17 @@ func (bp *BlockChain) StoreBlock(block *types2.Block) error { return err } - if err == ErrLatestHeightNil { + if err == ErrLatestHeightNil || block.Header.Height > height { if err = bp.setLatestBlockHeight(block.Header.Height); err != nil { return err } - } else { - if block.Header.Height > height { - if err = bp.setLatestBlockHeight(block.Header.Height); err != nil { - return err - } + } else if block.Header.Height > height { + if err = bp.setLatestBlockHeight(block.Header.Height); err != nil { + return err } + bp.bus.Publish("blockchain:latestBlockHeightUpdated", block) } + bp.bus.Publish("blockchain:blockCommitted", block) return nil } diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go index ae49e52..23246ce 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -3,6 +3,9 @@ package pool import ( "bytes" "encoding/hex" + "time" + + "github.com/asaskevich/EventBus" "github.com/sirupsen/logrus" @@ -15,20 +18,27 @@ type BlockPool struct { mempool *Mempool knownBlocks cache.Cache acceptedBlocks cache.Cache + bus EventBus.Bus } -func NewBlockPool(mp *Mempool) (*BlockPool, error) { +func NewBlockPool(mp *Mempool, bus EventBus.Bus) (*BlockPool, error) { bp := &BlockPool{ acceptedBlocks: cache.NewInMemoryCache(), // here we need to use separate cache knownBlocks: cache.NewInMemoryCache(), mempool: mp, + bus: bus, } return bp, nil } func (bp *BlockPool) AddBlock(block *types.Block) error { - return bp.knownBlocks.Store(hex.EncodeToString(block.Header.Hash), block) + err := bp.knownBlocks.StoreWithTTL(hex.EncodeToString(block.Header.Hash), block, 10*time.Minute) + if err != nil { + return err + } + bp.bus.Publish("blockpool:knownBlockAdded", block) + return nil } func (bp *BlockPool) GetBlock(blockhash []byte) (*types.Block, error) { @@ -42,10 +52,16 @@ func (bp *BlockPool) PruneBlocks() { for k := range bp.knownBlocks.Items() { bp.knownBlocks.Delete(k) } + bp.bus.Publish("blockpool:pruned") } func (bp *BlockPool) AddAcceptedBlock(block *types.Block) error { - return bp.acceptedBlocks.Store(hex.EncodeToString(block.Header.Hash), block) + err := bp.acceptedBlocks.Store(hex.EncodeToString(block.Header.Hash), block) + if err != nil { + return err + } + bp.bus.Publish("blockpool:acceptedBlockAdded", block) + return nil } func (bp *BlockPool) GetAllAcceptedBlocks() []*types.Block { diff --git a/blockchain/pool/mempool.go b/blockchain/pool/mempool.go index c94a99c..ce1753d 100644 --- a/blockchain/pool/mempool.go +++ b/blockchain/pool/mempool.go @@ -6,6 +6,8 @@ import ( "sort" "time" + "github.com/asaskevich/EventBus" + "github.com/sirupsen/logrus" types2 "github.com/Secured-Finance/dione/blockchain/types" @@ -26,11 +28,13 @@ var ( type Mempool struct { cache cache.Cache + bus EventBus.Bus } -func NewMempool() (*Mempool, error) { +func NewMempool(bus EventBus.Bus) (*Mempool, error) { mp := &Mempool{ cache: cache.NewInMemoryCache(), // here we need to use separate cache + bus: bus, } return mp, nil @@ -40,13 +44,21 @@ func (mp *Mempool) StoreTx(tx *types2.Transaction) error { hashStr := hex.EncodeToString(tx.Hash) err := mp.cache.StoreWithTTL(DefaultTxPrefix+hashStr, tx, DefaultTxTTL) logrus.Infof("Submitted new transaction in mempool with hash %x", tx.Hash) + mp.bus.Publish("mempool:transactionAdded", tx) return err } -func (mp *Mempool) DeleteTx(txHash []byte) { +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.Debugf("Deleted transaction from mempool %x", txHash) + mp.bus.Publish("mempool:transactionRemoved", tx) + return nil } func (mp *Mempool) GetTransactionsForNewBlock() []*types2.Transaction { diff --git a/consensus/consensus.go b/consensus/consensus.go index 86cbb02..a1c9b5b 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -1,6 +1,7 @@ package consensus import ( + "encoding/hex" "errors" "math/big" "sync" @@ -15,8 +16,6 @@ import ( "github.com/Secured-Finance/dione/blockchain" - "github.com/Arceliar/phony" - types3 "github.com/Secured-Finance/dione/blockchain/types" "github.com/libp2p/go-libp2p-core/crypto" @@ -47,7 +46,6 @@ const ( ) type PBFTConsensusManager struct { - phony.Inbox bus EventBus.Bus psb *pubsub.PubSubRouter minApprovals int // FIXME @@ -102,21 +100,11 @@ func NewPBFTConsensusManager( pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare) pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare) pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit) - //bus.SubscribeOnce("sync:initialSyncCompleted", func() { - // pcm.state.ready = true - //}) + bus.SubscribeAsync("beacon:newEntry", func(entry types2.BeaconEntry) { + pcm.onNewBeaconEntry(entry) + }, true) height, _ := pcm.blockchain.GetLatestBlockHeight() pcm.state.blockHeight = height + 1 - go func() { - for { - select { - case e := <-b.Beacon.NewEntries(): - { - pcm.NewDrandRound(nil, e) - } - } - } - }() return pcm } @@ -263,94 +251,105 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { } } -func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, entry types2.BeaconEntry) { - pcm.Act(from, func() { - pcm.state.mutex.Lock() - defer pcm.state.mutex.Unlock() - block, err := pcm.commitAcceptedBlocks() +func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { + block, err := pcm.commitAcceptedBlocks() + if err != nil { + if errors.Is(err, ErrNoAcceptedBlocks) { + logrus.WithFields(logrus.Fields{ + "round": pcm.state.blockHeight, + }).Infof("No accepted blocks in the current consensus round") + } else { + logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) + return + } + } + + if block != nil { + // broadcast new block + var newBlockMessage pubsub.PubSubMessage + newBlockMessage.Type = pubsub.NewBlockMessageType + blockSerialized, err := cbor.Marshal(block) if err != nil { - if errors.Is(err, ErrNoAcceptedBlocks) { - logrus.Infof("No accepted blocks for consensus round %d", pcm.state.blockHeight) - } else { - logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) - return - } + logrus.Errorf("Failed to serialize block %x for broadcasting!", block.Header.Hash) + } else { + newBlockMessage.Payload = blockSerialized + pcm.psb.BroadcastToServiceTopic(&newBlockMessage) } - if block != nil { - // broadcast new block - var newBlockMessage pubsub.PubSubMessage - newBlockMessage.Type = pubsub.NewBlockMessageType - blockSerialized, err := cbor.Marshal(block) - if err != nil { - logrus.Errorf("Failed to serialize block %x for broadcasting!", block.Header.Hash) - } else { - newBlockMessage.Payload = blockSerialized - pcm.psb.BroadcastToServiceTopic(&newBlockMessage) - } - - // if we are miner for this block - // then post dione tasks to target chains (currently, only Ethereum) - if *block.Header.Proposer == pcm.miner.address { - for _, v := range block.Data { - var task types2.DioneTask - err := cbor.Unmarshal(v.Data, &task) - if err != nil { - logrus.Errorf("Failed to unmarshal transaction %x payload: %s", v.Hash, err.Error()) - continue // FIXME - } - reqIDNumber, ok := big.NewInt(0).SetString(task.RequestID, 10) - if !ok { - logrus.Errorf("Failed to parse request id number in task of tx %x", v.Hash) - continue // FIXME - } - - err = pcm.ethereumClient.SubmitRequestAnswer(reqIDNumber, task.Payload) - if err != nil { - logrus.Errorf("Failed to submit task in tx %x: %s", v.Hash, err.Error()) - continue // FIXME - } + // if we are miner of this block + // then post dione tasks to target chains (currently, only Ethereum) + if block.Header.Proposer.String() == pcm.miner.address.String() { + 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 } - } - pcm.state.blockHeight = pcm.state.blockHeight + 1 - } - - // get latest block - height, err := pcm.blockchain.GetLatestBlockHeight() - if err != nil { - logrus.Error(err) - return - } - blockHeader, err := pcm.blockchain.FetchBlockHeaderByHeight(height) - if err != nil { - logrus.Error(err) - return - } - - pcm.state.drandRound = entry.Round - pcm.state.randomness = entry.Data - - minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round, blockHeader) - if err != nil { - if errors.Is(err, ErrNoTxForBlock) { - logrus.Info("Skipping consensus round, because we don't have transactions in mempool for including into block") - } else { - logrus.Errorf("Failed to mine the block: %s", err.Error()) - } - return - } - - // if we are round winner - if minedBlock != nil { - logrus.Infof("We are elected in consensus round %d", pcm.state.blockHeight) - err = pcm.propose(minedBlock) - if err != nil { - logrus.Errorf("Failed to propose the block: %s", err.Error()) - return + 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)") } } - }) + + pcm.state.blockHeight = pcm.state.blockHeight + 1 + } + + // get latest block + height, err := pcm.blockchain.GetLatestBlockHeight() + if err != nil { + logrus.Error(err) + return + } + blockHeader, err := pcm.blockchain.FetchBlockHeaderByHeight(height) + if err != nil { + logrus.Error(err) + return + } + + pcm.state.drandRound = entry.Round + pcm.state.randomness = entry.Data + + minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round, blockHeader) + if err != nil { + if errors.Is(err, ErrNoTxForBlock) { + logrus.Info("Sealing skipped, no transactions in mempool") + } else { + logrus.Errorf("Failed to mine the block: %s", err.Error()) + } + return + } + + // if we are round winner + if minedBlock != nil { + logrus.WithField("round", pcm.state.blockHeight).Infof("We are elected in consensus round") + err = pcm.propose(minedBlock) + if err != nil { + logrus.Errorf("Failed to propose the block: %s", err.Error()) + return + } + } } func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { @@ -374,10 +373,21 @@ func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { maxStake = stake selectedBlock = v } - logrus.Infof("Committed block %x with height %d of miner %s", selectedBlock.Header.Hash, selectedBlock.Header.Height, selectedBlock.Header.Proposer.String()) + logrus.WithFields(logrus.Fields{ + "hash": hex.EncodeToString(selectedBlock.Header.Hash), + "height": selectedBlock.Header.Height, + "miner": selectedBlock.Header.Proposer.String(), + }).Info("Committed new block") pcm.blockPool.PruneAcceptedBlocks(selectedBlock) for _, v := range selectedBlock.Data { - pcm.mempool.DeleteTx(v.Hash) + err := pcm.mempool.DeleteTx(v.Hash) + if err != nil { + logrus.WithFields(logrus.Fields{ + "err": err.Error(), + "tx": hex.EncodeToString(v.Hash), + }).Errorf("Failed to delete committed tx from mempool") + continue + } } return selectedBlock, pcm.blockchain.StoreBlock(selectedBlock) } diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 90145e7..42bf214 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -166,7 +166,10 @@ func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain, b beacon.Bea return } } else { - logrus.Debugf("Origin chain [%v]/request type[%v] doesn't have any payload validation!", task.OriginChain, task.RequestType) + logrus.WithFields(logrus.Fields{ + "originChain": task.OriginChain, + "requestType": task.RequestType, + }).Debug("This origin chain/request type doesn't have any payload validation!") } }(v, result) } diff --git a/consensus/miner.go b/consensus/miner.go index 5b6891f..ebbb982 100644 --- a/consensus/miner.go +++ b/consensus/miner.go @@ -90,7 +90,7 @@ func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, error) { } func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) { - logrus.Debug("attempting to mine the block at epoch: ", lastBlockHeader.Height+1) + logrus.WithField("height", lastBlockHeader.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) diff --git a/node/node.go b/node/node.go index 212b4c2..747053d 100644 --- a/node/node.go +++ b/node/node.go @@ -93,7 +93,13 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Fatal(err) } n.Host = lhost - logrus.Info("Libp2p host has been successfully initialized!") + logrus.WithField( + "multiaddress", + fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", + n.Config.ListenAddr, + n.Config.ListenPort, + n.Host.ID().Pretty(), + )).Info("Libp2p host has been initialized!") // initialize ethereum client ethClient, err := provideEthereumClient(n.Config) @@ -101,14 +107,14 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Fatal(err) } n.Ethereum = ethClient - logrus.Info("Ethereum client has been successfully initialized!") + logrus.WithField("ethAddress", ethClient.GetEthAddress().Hex()).Info("Ethereum client has been initialized!") // initialize blockchain rpc clients err = n.setupRPCClients() if err != nil { logrus.Fatal(err) } - logrus.Info("RPC clients has been successfully configured!") + logrus.Info("Foreign Blockchain RPC clients has been successfully configured!") // initialize pubsub subsystem psb := providePubsubRouter(lhost, n.Config) @@ -137,7 +143,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim // == initialize blockchain modules // initialize blockpool database - bc, err := provideBlockChain(n.Config) + bc, err := provideBlockChain(n.Config, bus) if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) } @@ -145,14 +151,14 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Block pool database has been successfully initialized!") // initialize mempool - mp, err := provideMemPool() + mp, err := provideMemPool(bus) if err != nil { logrus.Fatalf("Failed to initialize mempool: %s", err.Error()) } n.MemPool = mp logrus.Info("Mempool has been successfully initialized!") - bp, err := provideBlockPool(mp) + bp, err := provideBlockPool(mp, bus) if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) } @@ -166,7 +172,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim if err != nil { logrus.Fatal(err) } - logrus.Info("Node p2p RPC network service has been successfully initialized!") + logrus.Info("Direct RPC has been successfully initialized!") // initialize libp2p-gorpc client r := provideP2PRPCClient(lhost) @@ -184,7 +190,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Fatal(err) } n.SyncManager = sm - logrus.Info("Blockchain synchronization subsystem has been successfully initialized!") + logrus.Info("Blockchain sync subsystem has been successfully initialized!") // initialize mining subsystem miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Ethereum, prvKey, mp) @@ -192,7 +198,7 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim logrus.Info("Mining subsystem has been initialized!") // initialize random beacon network subsystem - randomBeaconNetwork, err := provideBeacon(psb.Pubsub) + randomBeaconNetwork, err := provideBeacon(psb.Pubsub, bus) if err != nil { logrus.Fatal(err) } @@ -238,8 +244,6 @@ func (n *Node) Run(ctx context.Context) error { } 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())) - logrus.Info("Announcing ourselves...") _, err := n.PeerDiscovery.Advertise(context.TODO(), n.Config.Rendezvous) if err != nil { @@ -267,12 +271,15 @@ func (n *Node) runLibp2pAsync(ctx context.Context) error { if newPeer.ID.String() == n.Host.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) + 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") } } } @@ -348,6 +355,7 @@ func (n *Node) setupRPCClients() error { func Start() { 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) diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 124b97d..9a066c7 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -64,8 +64,8 @@ func provideMiner(peerID peer.ID, ethAddress common.Address, ethClient *ethclien return consensus.NewMiner(peerID, ethAddress, ethClient, privateKey, mempool) } -func provideBeacon(ps *pubsub2.PubSub) (beacon.BeaconNetwork, error) { - bc, err := drand2.NewDrandBeacon(ps) +func provideBeacon(ps *pubsub2.PubSub, bus EventBus.Bus) (beacon.BeaconNetwork, error) { + bc, err := drand2.NewDrandBeacon(ps, bus) if err != nil { return beacon.BeaconNetwork{}, fmt.Errorf("failed to setup drand beacon: %w", err) } @@ -176,12 +176,12 @@ func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host, pexDiscover return pexDiscovery, nil } -func provideBlockChain(config *config.Config) (*blockchain.BlockChain, error) { - return blockchain.NewBlockChain(config.Blockchain.DatabasePath) +func provideBlockChain(config *config.Config, bus EventBus.Bus) (*blockchain.BlockChain, error) { + return blockchain.NewBlockChain(config.Blockchain.DatabasePath, bus) } -func provideMemPool() (*pool.Mempool, error) { - return pool.NewMempool() +func provideMemPool(bus EventBus.Bus) (*pool.Mempool, error) { + return pool.NewMempool(bus) } func provideSyncManager(bus EventBus.Bus, bp *blockchain.BlockChain, mp *pool.Mempool, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { @@ -205,6 +205,6 @@ func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *Network return NewNetworkService(bp, mp) } -func provideBlockPool(mp *pool.Mempool) (*pool.BlockPool, error) { - return pool.NewBlockPool(mp) +func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) (*pool.BlockPool, error) { + return pool.NewBlockPool(mp, bus) } From bb3bf032d5f7b4e193820250aa7490f4dd84f8a0 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 15 Jul 2021 23:55:06 +0300 Subject: [PATCH 41/59] Update go-libp2p-pex dependency to v1.1.2 --- consensus/consensus.go | 9 +++-- go.mod | 28 ++++++--------- go.sum | 77 ++++++++++-------------------------------- 3 files changed, 33 insertions(+), 81 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index a1c9b5b..69a4a9d 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -256,10 +256,13 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { if err != nil { if errors.Is(err, ErrNoAcceptedBlocks) { logrus.WithFields(logrus.Fields{ - "round": pcm.state.blockHeight, + "height": pcm.state.blockHeight, }).Infof("No accepted blocks in the current consensus round") } else { - logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error()) + logrus.WithFields(logrus.Fields{ + "height": pcm.state.blockHeight, + "err": err.Error(), + }).Errorf("Failed to select the block in the current consensus round") return } } @@ -343,7 +346,7 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { // if we are round winner if minedBlock != nil { - logrus.WithField("round", pcm.state.blockHeight).Infof("We are elected in consensus round") + logrus.WithField("height", pcm.state.blockHeight).Infof("We have been elected in the current consensus round") err = pcm.propose(minedBlock) if err != nil { logrus.Errorf("Failed to propose the block: %s", err.Error()) diff --git a/go.mod b/go.mod index 2a52f26..1b53929 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,13 @@ module github.com/Secured-Finance/dione -go 1.14 +go 1.16 require ( - github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 // indirect - 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/aristanetworks/goarista v0.0.0-20210308203447-b196d8410f1d // indirect - github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // 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 @@ -20,10 +19,9 @@ require ( 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 @@ -32,20 +30,17 @@ require ( github.com/huin/goupnp v1.0.1-0.20200620063722-49508fba0031 // indirect github.com/ipfs/go-log v1.0.4 github.com/ipfs/go-log/v2 v2.1.3 // indirect - github.com/jmoiron/sqlx v1.2.0 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect - github.com/klauspost/cpuid/v2 v2.0.6 // indirect + 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.2 + 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 @@ -57,20 +52,17 @@ 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/multierr v1.7.0 // indirect go.uber.org/zap v1.17.0 - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a + 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-20210601080250-7ecdf8ef093b // 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 3907208..57fe94b 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,6 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= -github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= @@ -59,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= @@ -111,8 +109,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 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/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/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= @@ -137,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= @@ -183,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= @@ -386,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= @@ -397,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= @@ -418,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= @@ -762,7 +750,6 @@ 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= @@ -843,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= @@ -859,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= @@ -869,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= @@ -895,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= @@ -917,10 +900,7 @@ 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/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 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 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= @@ -1021,7 +1001,6 @@ github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX 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/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.0 h1:5K3mT+64qDTKbV3yTdbMCzJ7O6wbNsavAEb8iqBvBcI= 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= @@ -1038,8 +1017,6 @@ 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.2 h1:jHL0F79uDVPNsflS9byf8Wk23MQ0G+r5nUnLChoUn8A= -github.com/libp2p/go-libp2p-gorpc v0.1.2/go.mod h1:ulZShaJCp3JHlBMHiA20efUmiqDECza+JvGFNXJyKdI= 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= @@ -1290,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= @@ -1334,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= @@ -1358,10 +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.2 h1:vapLUGlFpvvkaemMvKGGxjruOzaIzQbn41J6R9vxeyU= -github.com/multiformats/go-multiaddr v0.3.2/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= +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= @@ -1675,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= @@ -1713,13 +1683,11 @@ github.com/uber/jaeger-lib v1.5.1-0.20181102163054-1fc5c315e03c/go.mod h1:ComeND 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/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q= 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/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4= 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= @@ -1816,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= @@ -1861,7 +1828,6 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ 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= @@ -1870,12 +1836,10 @@ 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= @@ -1919,10 +1883,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-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +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= @@ -2014,8 +1976,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= @@ -2119,10 +2081,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-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk= -golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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= @@ -2171,7 +2132,6 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn 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= @@ -2321,7 +2281,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= @@ -2330,8 +2289,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= From f59aaa6cf2bf6eefca7a1db857de1141a1656dbf Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Mon, 19 Jul 2021 21:41:42 +0300 Subject: [PATCH 42/59] Fix issue when node creates tx with different hash but with the same content (because of slightly diffeent timestamp) --- blockchain/types/transaction.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockchain/types/transaction.go b/blockchain/types/transaction.go index a3b8ebb..96481bf 100644 --- a/blockchain/types/transaction.go +++ b/blockchain/types/transaction.go @@ -21,7 +21,7 @@ type Transaction struct { func CreateTransaction(data []byte) *Transaction { timestamp := time.Now() encodedData := hex.EncodeToString(data) - hash := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", timestamp.Unix(), encodedData))) + hash := crypto.Keccak256([]byte(fmt.Sprintf("%s", encodedData))) return &Transaction{ Hash: hash, Timestamp: timestamp, @@ -31,6 +31,6 @@ func CreateTransaction(data []byte) *Transaction { func (tx *Transaction) ValidateHash() bool { encodedData := hex.EncodeToString(tx.Data) - h := crypto.Keccak256([]byte(fmt.Sprintf("%d_%s", tx.Timestamp.Unix(), encodedData))) + h := crypto.Keccak256([]byte(fmt.Sprintf("%s", encodedData))) return bytes.Equal(h, tx.Hash) } From a6cf757fcf05756848093e4b84f8d7973da1567e Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Mon, 19 Jul 2021 23:19:06 +0300 Subject: [PATCH 43/59] Implement handler for NewBlock message in SyncManager, refactor block validation architecture --- beacon/domain_separation_tag.go | 8 + beacon/utils.go | 29 ++++ beacon/vrf.go | 28 ++++ blockchain/blockchain.go | 235 ++++++++++++++++++++++++----- {consensus => blockchain}/miner.go | 36 ++++- blockchain/pool/mempool.go | 4 +- blockchain/sync/sync_mgr.go | 29 +++- blockchain/types/block.go | 12 +- blockchain/utils/verification.go | 2 +- consensus/consensus.go | 133 ++++++++-------- consensus/consensus_validator.go | 222 ++++----------------------- consensus/utils.go | 80 ---------- node/node.go | 52 +++---- node/node_dep_providers.go | 14 +- 14 files changed, 468 insertions(+), 416 deletions(-) create mode 100644 beacon/domain_separation_tag.go create mode 100644 beacon/utils.go create mode 100644 beacon/vrf.go rename {consensus => blockchain}/miner.go (73%) 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/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 index 6d5874c..e92f01c 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -1,10 +1,22 @@ package blockchain import ( + "bytes" + "context" "encoding/binary" "encoding/hex" "errors" + "fmt" "os" + "sync" + + "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" @@ -29,16 +41,22 @@ var ( ) type BlockChain struct { + // db-related dbEnv *lmdb.Env db lmdb.DBI metadataIndex *utils.Index heightIndex *utils.Index - bus EventBus.Bus + + bus EventBus.Bus + miner *Miner + b beacon.BeaconAPI } -func NewBlockChain(path string, bus EventBus.Bus) (*BlockChain, error) { +func NewBlockChain(path string, bus EventBus.Bus, miner *Miner, b beacon.BeaconAPI) (*BlockChain, error) { chain := &BlockChain{ - bus: bus, + bus: bus, + miner: miner, + b: b, } // configure lmdb env @@ -88,16 +106,16 @@ func NewBlockChain(path string, bus EventBus.Bus) (*BlockChain, error) { return chain, nil } -func (bp *BlockChain) setLatestBlockHeight(height uint64) error { - err := bp.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height) +func (bc *BlockChain) setLatestBlockHeight(height uint64) error { + err := bc.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height) if err != nil { return err } return nil } -func (bp *BlockChain) GetLatestBlockHeight() (uint64, error) { - height, err := bp.metadataIndex.GetUint64([]byte(LatestBlockHeightKey)) +func (bc *BlockChain) GetLatestBlockHeight() (uint64, error) { + height, err := bc.metadataIndex.GetUint64([]byte(LatestBlockHeightKey)) if err != nil { if err == utils.ErrIndexKeyNotFound { return 0, ErrLatestHeightNil @@ -107,8 +125,20 @@ func (bp *BlockChain) GetLatestBlockHeight() (uint64, error) { return height, nil } -func (bp *BlockChain) StoreBlock(block *types2.Block) error { - err := bp.dbEnv.Update(func(txn *lmdb.Txn) error { +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 + } + + 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 @@ -118,11 +148,11 @@ func (bp *BlockChain) StoreBlock(block *types2.Block) error { return err } blockHash := hex.EncodeToString(block.Header.Hash) - err = txn.Put(bp.db, []byte(DefaultBlockDataPrefix+blockHash), data, 0) + err = txn.Put(bc.db, []byte(DefaultBlockDataPrefix+blockHash), data, 0) if err != nil { return err } - err = txn.Put(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching + err = txn.Put(bc.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching return err }) if err != nil { @@ -132,36 +162,36 @@ func (bp *BlockChain) StoreBlock(block *types2.Block) error { // update index "height -> block hash" heightBytes := make([]byte, 8) binary.LittleEndian.PutUint64(heightBytes, block.Header.Height) - err = bp.heightIndex.PutBytes(heightBytes, block.Header.Hash) + err = bc.heightIndex.PutBytes(heightBytes, block.Header.Hash) if err != nil { return err } // update latest block height - height, err := bp.GetLatestBlockHeight() + height, err := bc.GetLatestBlockHeight() if err != nil && err != ErrLatestHeightNil { return err } if err == ErrLatestHeightNil || block.Header.Height > height { - if err = bp.setLatestBlockHeight(block.Header.Height); err != nil { + if err = bc.setLatestBlockHeight(block.Header.Height); err != nil { return err } } else if block.Header.Height > height { - if err = bp.setLatestBlockHeight(block.Header.Height); err != nil { + if err = bc.setLatestBlockHeight(block.Header.Height); err != nil { return err } - bp.bus.Publish("blockchain:latestBlockHeightUpdated", block) + bc.bus.Publish("blockchain:latestBlockHeightUpdated", block) } - bp.bus.Publish("blockchain:blockCommitted", block) + bc.bus.Publish("blockchain:blockCommitted", block) return nil } -func (bp *BlockChain) HasBlock(blockHash []byte) (bool, error) { +func (bc *BlockChain) HasBlock(blockHash []byte) (bool, error) { var blockExists bool - err := bp.dbEnv.View(func(txn *lmdb.Txn) error { + err := bc.dbEnv.View(func(txn *lmdb.Txn) error { h := hex.EncodeToString(blockHash) - _, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+h)) // try to fetch block header + _, err := txn.Get(bc.db, []byte(DefaultBlockHeaderPrefix+h)) // try to fetch block header if err != nil { if lmdb.IsNotFound(err) { blockExists = false @@ -178,11 +208,11 @@ func (bp *BlockChain) HasBlock(blockHash []byte) (bool, error) { return blockExists, nil } -func (bp *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) { +func (bc *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) { var data []*types2.Transaction - err := bp.dbEnv.View(func(txn *lmdb.Txn) error { + err := bc.dbEnv.View(func(txn *lmdb.Txn) error { h := hex.EncodeToString(blockHash) - blockData, err := txn.Get(bp.db, []byte(DefaultBlockDataPrefix+h)) + blockData, err := txn.Get(bc.db, []byte(DefaultBlockDataPrefix+h)) if err != nil { if lmdb.IsNotFound(err) { return ErrBlockNotFound @@ -199,11 +229,11 @@ func (bp *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, e return data, nil } -func (bp *BlockChain) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) { +func (bc *BlockChain) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) { var blockHeader types2.BlockHeader - err := bp.dbEnv.View(func(txn *lmdb.Txn) error { + err := bc.dbEnv.View(func(txn *lmdb.Txn) error { h := hex.EncodeToString(blockHash) - data, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+h)) + data, err := txn.Get(bc.db, []byte(DefaultBlockHeaderPrefix+h)) if err != nil { if lmdb.IsNotFound(err) { return ErrBlockNotFound @@ -219,15 +249,15 @@ func (bp *BlockChain) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, e return &blockHeader, nil } -func (bp *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) { +func (bc *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) { var block types2.Block - header, err := bp.FetchBlockHeader(blockHash) + header, err := bc.FetchBlockHeader(blockHash) if err != nil { return nil, err } block.Header = header - data, err := bp.FetchBlockData(blockHash) + data, err := bc.FetchBlockData(blockHash) if err != nil { return nil, err } @@ -236,34 +266,169 @@ func (bp *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) { return &block, nil } -func (bp *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) { +func (bc *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) { var heightBytes = make([]byte, 8) binary.LittleEndian.PutUint64(heightBytes, height) - blockHash, err := bp.heightIndex.GetBytes(heightBytes) + blockHash, err := bc.heightIndex.GetBytes(heightBytes) if err != nil { if err == utils.ErrIndexKeyNotFound { return nil, ErrBlockNotFound } } - block, err := bp.FetchBlock(blockHash) + block, err := bc.FetchBlock(blockHash) if err != nil { return nil, err } return block, nil } -func (bp *BlockChain) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) { +func (bc *BlockChain) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) { var heightBytes = make([]byte, 8) binary.LittleEndian.PutUint64(heightBytes, height) - blockHash, err := bp.heightIndex.GetBytes(heightBytes) + blockHash, err := bc.heightIndex.GetBytes(heightBytes) if err != nil { if err == utils.ErrIndexKeyNotFound { return nil, ErrBlockNotFound } } - blockHeader, err := bp.FetchBlockHeader(blockHash) + 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.b.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/consensus/miner.go b/blockchain/miner.go similarity index 73% rename from consensus/miner.go rename to blockchain/miner.go index ebbb982..46b63bb 100644 --- a/consensus/miner.go +++ b/blockchain/miner.go @@ -1,4 +1,4 @@ -package consensus +package blockchain import ( "errors" @@ -6,6 +6,9 @@ import ( "math/big" "sync" + "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" @@ -96,7 +99,7 @@ func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHe return nil, fmt.Errorf("failed to update miner stake: %w", err) } - winner, err := IsRoundWinner( + winner, err := isRoundWinner( lastBlockHeader.Height+1, m.address, randomness, @@ -110,6 +113,7 @@ func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHe } if winner == nil { + logrus.WithField("height", lastBlockHeader.Height+1).Debug("Block is not mined because we are not leader in consensus round") return nil, nil } @@ -136,3 +140,31 @@ func (m *Miner) IsMinerEligibleToProposeBlock(ethAddress common.Address) error { } 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 index ce1753d..0a64d55 100644 --- a/blockchain/pool/mempool.go +++ b/blockchain/pool/mempool.go @@ -43,7 +43,7 @@ func NewMempool(bus EventBus.Bus) (*Mempool, error) { func (mp *Mempool) StoreTx(tx *types2.Transaction) error { hashStr := hex.EncodeToString(tx.Hash) err := mp.cache.StoreWithTTL(DefaultTxPrefix+hashStr, tx, DefaultTxTTL) - logrus.Infof("Submitted new transaction in mempool with hash %x", tx.Hash) + logrus.WithField("txHash", hex.EncodeToString(tx.Hash)).Info("Submitted new transaction in mempool") mp.bus.Publish("mempool:transactionAdded", tx) return err } @@ -56,7 +56,7 @@ func (mp *Mempool) DeleteTx(txHash []byte) error { return err } mp.cache.Delete(DefaultTxPrefix + hashStr) - logrus.Debugf("Deleted transaction from mempool %x", txHash) + logrus.WithField("txHash", hex.EncodeToString(txHash)).Debugf("Deleted transaction from mempool") mp.bus.Publish("mempool:transactionRemoved", tx) return nil } diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 70ce135..f18a73f 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -3,6 +3,7 @@ package sync import ( "bytes" "context" + "encoding/hex" "errors" "fmt" "strings" @@ -51,11 +52,11 @@ type syncManager struct { bus EventBus.Bus } -func NewSyncManager(bus EventBus.Bus, bp *blockchain.BlockChain, mp *pool.Mempool, p2pRPCClient *gorpc.Client, bootstrapPeer peer.ID, psb *pubsub.PubSubRouter) SyncManager { +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: bp, + blockpool: bc, mempool: mp, ctx: ctx, ctxCancelFunc: cancelFunc, @@ -66,6 +67,7 @@ func NewSyncManager(bus EventBus.Bus, bp *blockchain.BlockChain, mp *pool.Mempoo } psb.Hook(pubsub.NewTxMessageType, sm.onNewTransaction) + psb.Hook(pubsub.NewBlockMessageType, sm.onNewBlock) go func() { if err := sm.initialSync(); err != nil { @@ -217,7 +219,7 @@ func (sm *syncManager) processReceivedBlock(block types2.Block) error { } 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: %s", err.Error()) + 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") @@ -246,13 +248,30 @@ func (sm *syncManager) onNewTransaction(message *pubsub.PubSubMessage) { return } + // TODO add more checks on tx if !tx.ValidateHash() { - logrus.Warn("failed to validate tx hash, rejecting it") + logrus.WithField("txHash", hex.EncodeToString(tx.Hash)).Warn("failed to validate transaction hash, rejecting it") return - } // TODO add more checks on tx + } 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.WithField("err", err.Error()).Error("failed to store block from NewBlock message") + return + } +} diff --git a/blockchain/types/block.go b/blockchain/types/block.go index 262599d..bb0d6ad 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -1,6 +1,7 @@ package types import ( + "encoding/binary" "time" "github.com/Secured-Finance/dione/types" @@ -48,13 +49,16 @@ func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth comm timestamp := time.Now().Unix() // extract hashes from transactions - var txHashes [][]byte + var merkleHashes [][]byte for _, tx := range txs { - txHashes = append(txHashes, tx.Hash) + merkleHashes = append(merkleHashes, tx.Hash) } - txHashes = append(txHashes, lastBlockHeader.Hash) + merkleHashes = append(merkleHashes, lastBlockHeader.Hash) + timestampBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(timestampBytes, uint64(timestamp)) + merkleHashes = append(merkleHashes, timestampBytes) - tree, err := merkletree.NewUsing(txHashes, keccak256.New(), false) + tree, err := merkletree.NewUsing(merkleHashes, keccak256.New(), true) if err != nil { return nil, err } diff --git a/blockchain/utils/verification.go b/blockchain/utils/verification.go index 964c32d..21e9742 100644 --- a/blockchain/utils/verification.go +++ b/blockchain/utils/verification.go @@ -12,7 +12,7 @@ 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, false, tx.MerkleProof, [][]byte{blockHeader.Hash}, keccak256.New()) + 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()) } diff --git a/consensus/consensus.go b/consensus/consensus.go index 69a4a9d..1eeac6f 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -6,6 +6,8 @@ import ( "math/big" "sync" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/Secured-Finance/dione/beacon" "github.com/fxamacker/cbor/v2" @@ -53,11 +55,12 @@ type PBFTConsensusManager struct { msgLog *ConsensusMessageLog validator *ConsensusValidator ethereumClient *ethclient.EthereumClient - miner *Miner + miner *blockchain.Miner blockPool *pool.BlockPool mempool *pool.Mempool blockchain *blockchain.BlockChain state *State + address peer.ID } type State struct { @@ -75,28 +78,32 @@ func NewPBFTConsensusManager( minApprovals int, privKey crypto.PrivKey, ethereumClient *ethclient.EthereumClient, - miner *Miner, + miner *blockchain.Miner, bc *blockchain.BlockChain, bp *pool.BlockPool, b beacon.BeaconNetwork, mempool *pool.Mempool, + address peer.ID, ) *PBFTConsensusManager { - pcm := &PBFTConsensusManager{} - pcm.psb = psb - pcm.miner = miner - pcm.validator = NewConsensusValidator(miner, bc, b) - pcm.msgLog = NewConsensusMessageLog() - pcm.minApprovals = minApprovals - pcm.privKey = privKey - pcm.ethereumClient = ethereumClient - pcm.state = &State{ - ready: false, - status: StateStatusUnknown, + pcm := &PBFTConsensusManager{ + psb: psb, + miner: miner, + validator: NewConsensusValidator(miner, bc, b), + msgLog: NewConsensusMessageLog(), + minApprovals: minApprovals, + privKey: privKey, + ethereumClient: ethereumClient, + state: &State{ + ready: false, + status: StateStatusUnknown, + }, + bus: bus, + blockPool: bp, + mempool: mempool, + blockchain: bc, + address: address, } - pcm.bus = bus - pcm.blockPool = bp - pcm.mempool = mempool - pcm.blockchain = bc + pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare) pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare) pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit) @@ -120,8 +127,8 @@ func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { } func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) { - pcm.state.mutex.Lock() - defer pcm.state.mutex.Unlock() + //pcm.state.mutex.Lock() + //defer pcm.state.mutex.Unlock() var prePrepare types.PrePrepareMessage err := cbor.Unmarshal(message.Payload, &prePrepare) if err != nil { @@ -129,7 +136,7 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) return } - if *prePrepare.Block.Header.Proposer == pcm.miner.address { + if *prePrepare.Block.Header.Proposer == pcm.address { return } @@ -144,7 +151,7 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) logrus.Tracef("received existing pre_prepare msg for block %x", cmsg.Block.Header.Hash) return } - if !pcm.validator.Valid(cmsg, map[string]interface{}{"randomness": pcm.state.randomness}) { + if !pcm.validator.Valid(cmsg) { logrus.Warnf("received invalid pre_prepare msg for block %x", cmsg.Block.Header.Hash) return } @@ -163,8 +170,8 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) } func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { - pcm.state.mutex.Lock() - defer pcm.state.mutex.Unlock() + //pcm.state.mutex.Lock() + //defer pcm.state.mutex.Unlock() var prepare types.PrepareMessage err := cbor.Unmarshal(message.Payload, &prepare) if err != nil { @@ -189,7 +196,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { return } - if !pcm.validator.Valid(cmsg, nil) { + if !pcm.validator.Valid(cmsg) { logrus.Warnf("received invalid prepare msg for block %x", cmsg.Blockhash) return } @@ -208,8 +215,8 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { } func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { - pcm.state.mutex.Lock() - defer pcm.state.mutex.Unlock() + //pcm.state.mutex.Lock() + //defer pcm.state.mutex.Unlock() var commit types.CommitMessage err := cbor.Unmarshal(message.Payload, &commit) if err != nil { @@ -233,7 +240,7 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { logrus.Tracef("received existing commit msg for block %x", cmsg.Blockhash) return } - if !pcm.validator.Valid(cmsg, nil) { + if !pcm.validator.Valid(cmsg) { logrus.Warnf("received invalid commit msg for block %x", cmsg.Blockhash) return } @@ -281,39 +288,8 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { // if we are miner of this block // then post dione tasks to target chains (currently, only Ethereum) - if block.Header.Proposer.String() == pcm.miner.address.String() { - 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)") - } + if block.Header.Proposer.String() == pcm.address.String() { + pcm.submitTasksFromBlock(block) } pcm.state.blockHeight = pcm.state.blockHeight + 1 @@ -336,7 +312,7 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round, blockHeader) if err != nil { - if errors.Is(err, ErrNoTxForBlock) { + 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()) @@ -355,6 +331,41 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { } } +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.blockPool.GetAllAcceptedBlocks() if blocks == nil { diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 42bf214..a0cf8da 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -1,223 +1,61 @@ package consensus import ( - "bytes" - "context" - "fmt" - "sync" + "encoding/hex" "github.com/Secured-Finance/dione/beacon" - - types3 "github.com/Secured-Finance/dione/blockchain/types" + "github.com/sirupsen/logrus" "github.com/Secured-Finance/dione/blockchain" - "github.com/Secured-Finance/dione/blockchain/utils" types2 "github.com/Secured-Finance/dione/consensus/types" - "github.com/Secured-Finance/dione/consensus/validation" - "github.com/Secured-Finance/dione/types" - "github.com/filecoin-project/go-state-types/crypto" - "github.com/fxamacker/cbor/v2" - "github.com/sirupsen/logrus" - "github.com/wealdtech/go-merkletree" - "github.com/wealdtech/go-merkletree/keccak256" ) type ConsensusValidator struct { - validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool - miner *Miner + validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool + miner *blockchain.Miner beacon beacon.BeaconNetwork blockchain *blockchain.BlockChain } -func NewConsensusValidator(miner *Miner, bc *blockchain.BlockChain, b beacon.BeaconNetwork) *ConsensusValidator { +func NewConsensusValidator(miner *blockchain.Miner, bc *blockchain.BlockChain, b beacon.BeaconNetwork) *ConsensusValidator { cv := &ConsensusValidator{ miner: miner, blockchain: bc, beacon: b, } - cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool{ - // FIXME it all - types2.ConsensusMessageTypePrePrepare: func(msg types2.ConsensusMessage, metadata map[string]interface{}) bool { - // === verify block signature === - pubkey, err := msg.Block.Header.Proposer.ExtractPublicKey() - if err != nil { - logrus.Errorf("unable to extract public key from block proposer's peer id: %s", err.Error()) + 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 } - - ok, err := pubkey.Verify(msg.Block.Header.Hash, msg.Block.Header.Signature) - if err != nil { - logrus.Errorf("failed to verify block signature: %s", err.Error()) - return false - } - if !ok { - logrus.Errorf("signature of block %x is invalid", msg.Block.Header.Hash) - return false - } - ///////////////////////////////// - - // === check last hash merkle proof === - latestHeight, err := cv.blockchain.GetLatestBlockHeight() - if err != nil { - logrus.Error(err) - return false - } - previousBlockHeader, err := cv.blockchain.FetchBlockHeaderByHeight(latestHeight) - if err != nil { - logrus.Error(err) - return false - } - if bytes.Compare(msg.Block.Header.LastHash, previousBlockHeader.Hash) != 0 { - logrus.Errorf("block header has invalid last block hash (expected: %x, actual %x)", previousBlockHeader.Hash, msg.Block.Header.LastHash) - return false - } - - verified, err := merkletree.VerifyProofUsing(previousBlockHeader.Hash, false, msg.Block.Header.LastHashProof, [][]byte{msg.Block.Header.Hash}, keccak256.New()) - if err != nil { - logrus.Error("failed to verify last block hash merkle proof: %s", err.Error()) - return false - } - if !verified { - logrus.Error("merkle hash of current block doesn't contain hash of previous block: %s", err.Error()) - return false - } - ///////////////////////////////// - - // === verify election proof wincount preliminarily === - if msg.Block.Header.ElectionProof.WinCount < 1 { - logrus.Error("miner isn't a winner!") - return false - } - ///////////////////////////////// - - // === verify miner's eligibility to propose this task === - err = cv.miner.IsMinerEligibleToProposeBlock(msg.Block.Header.ProposerEth) - if err != nil { - logrus.Errorf("miner is not eligible to propose block: %v", err) - return false - } - ///////////////////////////////// - - // === verify election proof vrf === - proposerBuf, err := msg.Block.Header.Proposer.MarshalBinary() - if err != nil { - logrus.Error(err) - return false - } - - res, err := b.Beacon.Entry(context.TODO(), msg.Block.Header.ElectionProof.RandomnessRound) - if err != nil { - logrus.Error(err) - return false - } - eproofRandomness, err := DrawRandomness( - res.Data, - crypto.DomainSeparationTag_ElectionProofProduction, - msg.Block.Header.Height, - proposerBuf, - ) - if err != nil { - logrus.Errorf("failed to draw ElectionProof randomness: %s", err.Error()) - return false - } - err = VerifyVRF(*msg.Block.Header.Proposer, eproofRandomness, msg.Block.Header.ElectionProof.VRFProof) - if err != nil { - logrus.Errorf("failed to verify election proof vrf: %v", err) - return false - } - ////////////////////////////////////// - - // === compute wincount locally and verify values === - mStake, nStake, err := cv.miner.GetStakeInfo(msg.Block.Header.ProposerEth) - if err != nil { - logrus.Errorf("failed to get miner stake: %v", err) - return false - } - actualWinCount := msg.Block.Header.ElectionProof.ComputeWinCount(mStake, nStake) - if msg.Block.Header.ElectionProof.WinCount != actualWinCount { - logrus.Errorf("locally computed wincount of block %x isn't matching received value!", msg.Block.Header.Hash) - return false - } - ////////////////////////////////////// - - // === validate block transactions === - result := make(chan error) - var wg sync.WaitGroup - for _, v := range msg.Block.Data { - wg.Add(1) - go func(v *types3.Transaction, c chan error) { - defer wg.Done() - if err := utils.VerifyTx(msg.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 { - logrus.Error(err) - return false - } - } - ///////////////////////////////// - return true }, - types2.ConsensusMessageTypePrepare: func(msg types2.ConsensusMessage, metadata map[string]interface{}) 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 - }, - types2.ConsensusMessageTypeCommit: func(msg types2.ConsensusMessage, metadata map[string]interface{}) 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 - }, + types2.ConsensusMessageTypePrepare: checkSignatureForBlockhash, + types2.ConsensusMessageTypeCommit: checkSignatureForBlockhash, } return cv } -func (cv *ConsensusValidator) Valid(msg types2.ConsensusMessage, metadata map[string]interface{}) bool { - return cv.validationFuncMap[msg.Type](msg, metadata) +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/utils.go b/consensus/utils.go index 7d62f77..37e7e85 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -1,9 +1,7 @@ package consensus import ( - "encoding/binary" "fmt" - "math/big" "github.com/fxamacker/cbor/v2" @@ -11,87 +9,9 @@ import ( types2 "github.com/Secured-Finance/dione/consensus/types" - "github.com/minio/blake2b-simd" - - "github.com/libp2p/go-libp2p-core/peer" - - "github.com/Secured-Finance/dione/types" - crypto2 "github.com/filecoin-project/go-state-types/crypto" "github.com/libp2p/go-libp2p-core/crypto" - "golang.org/x/xerrors" ) -type SignFunc func(peer.ID, []byte) (*types.Signature, error) - -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 -} - -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 := DrawRandomness(randomness, crypto2.DomainSeparationTag_ElectionProofProduction, round, buf) - if err != nil { - return nil, fmt.Errorf("failed to draw randomness: %w", err) - } - - vrfout, err := 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 -} - -func DrawRandomness(rbase []byte, pers crypto2.DomainSeparationTag, round uint64, 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 NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, privKey crypto.PrivKey) (*pubsub.PubSubMessage, error) { var message pubsub.PubSubMessage switch typ { diff --git a/node/node.go b/node/node.go index 747053d..786ba34 100644 --- a/node/node.go +++ b/node/node.go @@ -65,7 +65,7 @@ type Node struct { Config *config.Config Ethereum *ethclient.EthereumClient ConsensusManager *consensus.PBFTConsensusManager - Miner *consensus.Miner + Miner *blockchain.Miner Beacon beacon.BeaconNetwork DisputeManager *consensus.DisputeManager BlockPool *pool.BlockPool @@ -104,9 +104,10 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim // initialize ethereum client ethClient, err := provideEthereumClient(n.Config) if err != nil { - logrus.Fatal(err) + logrus.WithField("err", err.Error()).Fatal("Failed to initialize Ethereum client") } n.Ethereum = ethClient + //goland:noinspection ALL logrus.WithField("ethAddress", ethClient.GetEthAddress().Hex()).Info("Ethereum client has been initialized!") // initialize blockchain rpc clients @@ -135,21 +136,16 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim n.PeerDiscovery = peerDiscovery logrus.Info("Peer discovery subsystem has been initialized!") - // initialize event log cache subsystem - //c := provideCache(config) - //n.Cache = c - //logrus.Info("Event cache subsystem has initialized!") + // initialize random beacon network subsystem + randomBeaconNetwork, err := provideBeacon(psb.Pubsub, bus) + if err != nil { + logrus.Fatal(err) + } + n.Beacon = randomBeaconNetwork + logrus.Info("Random beacon subsystem has been initialized!") // == initialize blockchain modules - // initialize blockpool database - bc, err := provideBlockChain(n.Config, bus) - if err != nil { - logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) - } - n.BlockChain = bc - logrus.Info("Block pool database has been successfully initialized!") - // initialize mempool mp, err := provideMemPool(bus) if err != nil { @@ -158,6 +154,19 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim n.MemPool = mp logrus.Info("Mempool has been successfully initialized!") + // initialize mining subsystem + miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Ethereum, prvKey, mp) + n.Miner = miner + logrus.Info("Mining subsystem has been initialized!") + + // initialize blockpool database + bc, err := provideBlockChain(n.Config, bus, miner, randomBeaconNetwork.Beacon) + if err != nil { + logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) + } + n.BlockChain = bc + logrus.Info("Block pool database has been successfully initialized!") + bp, err := provideBlockPool(mp, bus) if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) @@ -192,21 +201,8 @@ func NewNode(config *config.Config, prvKey crypto.PrivKey, pexDiscoveryUpdateTim n.SyncManager = sm logrus.Info("Blockchain sync subsystem has been successfully initialized!") - // initialize mining subsystem - miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Ethereum, prvKey, mp) - n.Miner = miner - logrus.Info("Mining subsystem has been initialized!") - - // initialize random beacon network subsystem - randomBeaconNetwork, err := provideBeacon(psb.Pubsub, bus) - if err != nil { - logrus.Fatal(err) - } - n.Beacon = randomBeaconNetwork - logrus.Info("Random beacon subsystem has been initialized!") - // initialize consensus subsystem - consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals, bp, randomBeaconNetwork, mp) + consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals, bp, randomBeaconNetwork, mp, n.Host.ID()) n.ConsensusManager = consensusManager logrus.Info("Consensus subsystem has been initialized!") diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 9a066c7..e221b28 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -60,8 +60,8 @@ func provideDisputeManager(ctx context.Context, ethClient *ethclient.EthereumCli return consensus.NewDisputeManager(ctx, ethClient, pcm, cfg.Ethereum.DisputeVoteWindow, bc) } -func provideMiner(peerID peer.ID, ethAddress common.Address, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *consensus.Miner { - return consensus.NewMiner(peerID, ethAddress, ethClient, privateKey, mempool) +func provideMiner(peerID peer.ID, ethAddress common.Address, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *blockchain.Miner { + return blockchain.NewMiner(peerID, ethAddress, ethClient, privateKey, mempool) } func provideBeacon(ps *pubsub2.PubSub, bus EventBus.Bus) (beacon.BeaconNetwork, error) { @@ -94,7 +94,7 @@ func provideEthereumClient(config *config.Config) (*ethclient.EthereumClient, er ethereum := ethclient.NewEthereumClient() err := ethereum.Initialize(&config.Ethereum) if err != nil { - return nil, xerrors.Errorf("failed to initialize ethereum client: %v", err) + return nil, err } return ethereum, nil } @@ -106,7 +106,7 @@ func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubR func provideConsensusManager( bus EventBus.Bus, psb *pubsub.PubSubRouter, - miner *consensus.Miner, + miner *blockchain.Miner, bc *blockchain.BlockChain, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, @@ -114,6 +114,7 @@ func provideConsensusManager( bp *pool.BlockPool, b beacon.BeaconNetwork, mp *pool.Mempool, + address peer.ID, ) *consensus.PBFTConsensusManager { return consensus.NewPBFTConsensusManager( bus, @@ -126,6 +127,7 @@ func provideConsensusManager( bp, b, mp, + address, ) } @@ -176,8 +178,8 @@ func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host, pexDiscover return pexDiscovery, nil } -func provideBlockChain(config *config.Config, bus EventBus.Bus) (*blockchain.BlockChain, error) { - return blockchain.NewBlockChain(config.Blockchain.DatabasePath, bus) +func provideBlockChain(config *config.Config, bus EventBus.Bus, miner *blockchain.Miner, b beacon.BeaconAPI) (*blockchain.BlockChain, error) { + return blockchain.NewBlockChain(config.Blockchain.DatabasePath, bus, miner, b) } func provideMemPool(bus EventBus.Bus) (*pool.Mempool, error) { From ed9fa658ed27a382e34047679d3d06202ee88a68 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 20 Jul 2021 01:38:55 +0300 Subject: [PATCH 44/59] Make block hash more random --- blockchain/types/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain/types/block.go b/blockchain/types/block.go index bb0d6ad..ce99ec2 100644 --- a/blockchain/types/block.go +++ b/blockchain/types/block.go @@ -46,7 +46,7 @@ func GenesisBlock() *Block { } func CreateBlock(lastBlockHeader *BlockHeader, txs []*Transaction, minerEth common.Address, privateKey crypto.PrivKey, eproof *types.ElectionProof) (*Block, error) { - timestamp := time.Now().Unix() + timestamp := time.Now().UnixNano() // extract hashes from transactions var merkleHashes [][]byte From cbce24fdce8baa249274c2a79e7b35da4d97f81b Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 20 Jul 2021 01:39:14 +0300 Subject: [PATCH 45/59] Ignore block validation for genesis block --- blockchain/blockchain.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index e92f01c..bcd4a4a 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -133,12 +133,14 @@ func (bc *BlockChain) StoreBlock(block *types2.Block) error { return nil } - err := bc.ValidateBlock(block) - if err != nil { - return fmt.Errorf("failed to store block: %w", err) + 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 { + err := bc.dbEnv.Update(func(txn *lmdb.Txn) error { data, err := cbor.Marshal(block.Data) if err != nil { return err From 550d69fb26eed5eb4275ef53fb77bdfe624f80da Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 20 Jul 2021 01:40:49 +0300 Subject: [PATCH 46/59] Fix approve count for PREPARE state in consensus --- blockchain/pool/blockpool.go | 2 ++ blockchain/sync/sync_mgr.go | 5 ++++- config/win_config.go | 2 +- consensus/consensus.go | 22 ++++++++++++++++++++-- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go index 23246ce..e8882da 100644 --- a/blockchain/pool/blockpool.go +++ b/blockchain/pool/blockpool.go @@ -3,6 +3,7 @@ package pool import ( "bytes" "encoding/hex" + "fmt" "time" "github.com/asaskevich/EventBus" @@ -37,6 +38,7 @@ func (bp *BlockPool) AddBlock(block *types.Block) error { if err != nil { return err } + logrus.WithField("hash", fmt.Sprintf("%x", block.Header.Hash)).Debug("New block discovered") bp.bus.Publish("blockpool:knownBlockAdded", block) return nil } diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index f18a73f..43d89c3 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -271,7 +271,10 @@ func (sm *syncManager) onNewBlock(message *pubsub.PubSubMessage) { err = sm.blockpool.StoreBlock(&block) if err != nil { - logrus.WithField("err", err.Error()).Error("failed to store block from NewBlock message") + 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/config/win_config.go b/config/win_config.go index 4a624b2..1cf9295 100644 --- a/config/win_config.go +++ b/config/win_config.go @@ -2,4 +2,4 @@ package config import "math/big" -var ExpectedLeadersPerEpoch = big.NewInt(5) +var ExpectedLeadersPerEpoch = big.NewInt(1) diff --git a/consensus/consensus.go b/consensus/consensus.go index 1eeac6f..b6df23a 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -3,6 +3,7 @@ package consensus import ( "encoding/hex" "errors" + "fmt" "math/big" "sync" @@ -122,6 +123,7 @@ func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { } pcm.psb.BroadcastToServiceTopic(prePrepareMsg) pcm.blockPool.AddBlock(blk) + logrus.WithField("blockHash", fmt.Sprintf("%x", blk.Header.Hash)).Debugf("Entered into PREPREPARED state") pcm.state.status = StateStatusPrePrepared return nil } @@ -157,6 +159,10 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) } pcm.msgLog.AddMessage(cmsg) + logrus.WithFields(logrus.Fields{ + "blockHash": fmt.Sprintf("%x", cmsg.Block.Header.Hash), + "from": message.From.String(), + }).Debug("Received PREPREPARE message") pcm.blockPool.AddBlock(cmsg.Block) prepareMsg, err := NewMessage(cmsg, types.ConsensusMessageTypePrepare, pcm.privKey) @@ -165,6 +171,7 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) return } + logrus.WithField("blockHash", fmt.Sprintf("%x", prePrepare.Block.Header.Hash)).Debugf("Entered into PREPREPARED state") pcm.psb.BroadcastToServiceTopic(prepareMsg) pcm.state.status = StateStatusPrePrepared } @@ -187,7 +194,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { } if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { - logrus.Warnf("received unknown block %x", cmsg.Blockhash) + logrus.WithField("blockHash", cmsg.Blockhash).Warnf("received unknown block %x", cmsg.Blockhash) return } @@ -202,14 +209,19 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { } pcm.msgLog.AddMessage(cmsg) + logrus.WithFields(logrus.Fields{ + "blockHash": fmt.Sprintf("%x", cmsg.Blockhash), + "from": message.From.String(), + }).Debug("Received PREPARE message") - if len(pcm.msgLog.Get(types.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= pcm.minApprovals { + if len(pcm.msgLog.Get(types.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= pcm.minApprovals-1 { commitMsg, err := NewMessage(cmsg, types.ConsensusMessageTypeCommit, pcm.privKey) if err != nil { logrus.Errorf("failed to create commit message: %v", err) return } pcm.psb.BroadcastToServiceTopic(commitMsg) + logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into PREPARED state") pcm.state.status = StateStatusPrepared } } @@ -247,6 +259,11 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { pcm.msgLog.AddMessage(cmsg) + logrus.WithFields(logrus.Fields{ + "blockHash": fmt.Sprintf("%x", cmsg.Blockhash), + "from": message.From.String(), + }).Debug("Received COMMIT message") + if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals { block, err := pcm.blockPool.GetBlock(cmsg.Blockhash) if err != nil { @@ -254,6 +271,7 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { return } pcm.blockPool.AddAcceptedBlock(block) + logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into COMMIT state") pcm.state.status = StateStatusCommited } } From 5e0c7f02fa887176912590d2c1a33b105110f299 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 22 Jul 2021 00:56:58 +0300 Subject: [PATCH 47/59] Integrate Uber's Fx DI framework, refactor node init code massively --- beacon/drand/drand.go | 22 +- blockchain/blockchain.go | 18 +- blockchain/sync/sync_mgr.go | 14 +- config/config.go | 1 + consensus/consensus.go | 17 +- consensus/consensus_validator.go | 9 +- consensus/dispute_manager.go | 44 ++-- go.mod | 2 +- go.sum | 5 + node/flags.go | 6 + node/node.go | 393 +++++++------------------------ node/node_dep_providers.go | 290 ++++++++++++++++++----- pubsub/pubsub_router.go | 18 +- 13 files changed, 426 insertions(+), 413 deletions(-) create mode 100644 node/flags.go diff --git a/beacon/drand/drand.go b/beacon/drand/drand.go index b555ed6..00e1ef6 100644 --- a/beacon/drand/drand.go +++ b/beacon/drand/drand.go @@ -83,20 +83,23 @@ func NewDrandBeacon(ps *pubsub.PubSub, bus EventBus.Bus) (*DrandBeacon, error) { DrandClient: drandClient, localCache: make(map[uint64]types.BeaconEntry), bus: bus, + PublicKey: drandChain.PublicKey, } - db.PublicKey = drandChain.PublicKey - - db.drandResultChannel = db.DrandClient.Watch(context.TODO()) - err = db.getLatestDrandResult() - if err != nil { - return nil, err - } - go db.loop(context.TODO()) - return db, nil } +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 { @@ -113,6 +116,7 @@ func (db *DrandBeacon) loop(ctx context.Context) { select { case <-ctx.Done(): { + logrus.Debug("Stopping watching new DRAND entries...") return } case res := <-db.drandResultChannel: diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index bcd4a4a..a634a5a 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -10,6 +10,8 @@ import ( "os" "sync" + drand2 "github.com/Secured-Finance/dione/beacon/drand" + "github.com/Secured-Finance/dione/beacon" "github.com/Secured-Finance/dione/consensus/validation" @@ -47,16 +49,16 @@ type BlockChain struct { metadataIndex *utils.Index heightIndex *utils.Index - bus EventBus.Bus - miner *Miner - b beacon.BeaconAPI + bus EventBus.Bus + miner *Miner + drandBeacon *drand2.DrandBeacon } -func NewBlockChain(path string, bus EventBus.Bus, miner *Miner, b beacon.BeaconAPI) (*BlockChain, error) { +func NewBlockChain(path string, bus EventBus.Bus, miner *Miner, db *drand2.DrandBeacon) (*BlockChain, error) { chain := &BlockChain{ - bus: bus, - miner: miner, - b: b, + bus: bus, + miner: miner, + drandBeacon: db, } // configure lmdb env @@ -357,7 +359,7 @@ func (bc *BlockChain) ValidateBlock(block *types2.Block) error { return err } - res, err := bc.b.Entry(context.TODO(), block.Header.ElectionProof.RandomnessRound) + res, err := bc.drandBeacon.Entry(context.TODO(), block.Header.ElectionProof.RandomnessRound) if err != nil { return err } diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index 43d89c3..da9ef4e 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -37,7 +37,9 @@ import ( gorpc "github.com/libp2p/go-libp2p-gorpc" ) -type SyncManager interface{} +type SyncManager interface { + Run() +} type syncManager struct { blockpool *blockchain.BlockChain @@ -66,16 +68,18 @@ func NewSyncManager(bus EventBus.Bus, bc *blockchain.BlockChain, mp *pool.Mempoo psb: psb, } - psb.Hook(pubsub.NewTxMessageType, sm.onNewTransaction) - psb.Hook(pubsub.NewBlockMessageType, sm.onNewBlock) + 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) } }() - - return sm } func (sm *syncManager) initialSync() error { diff --git a/config/config.go b/config/config.go index 8f51f52..337a6b7 100644 --- a/config/config.go +++ b/config/config.go @@ -20,6 +20,7 @@ type Config struct { Redis RedisConfig `mapstructure:"redis"` CacheType string `mapstructure:"cache_type"` Blockchain BlockchainConfig `mapstructure:"blockchain"` + PrivateKeyPath string `mapstructure:"private_key_path"` } type EthereumConfig struct { diff --git a/consensus/consensus.go b/consensus/consensus.go index b6df23a..0e2909f 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -7,9 +7,9 @@ import ( "math/big" "sync" - "github.com/libp2p/go-libp2p-core/peer" + drand2 "github.com/Secured-Finance/dione/beacon/drand" - "github.com/Secured-Finance/dione/beacon" + "github.com/libp2p/go-libp2p-core/peer" "github.com/fxamacker/cbor/v2" @@ -82,14 +82,14 @@ func NewPBFTConsensusManager( miner *blockchain.Miner, bc *blockchain.BlockChain, bp *pool.BlockPool, - b beacon.BeaconNetwork, + db *drand2.DrandBeacon, mempool *pool.Mempool, address peer.ID, ) *PBFTConsensusManager { pcm := &PBFTConsensusManager{ psb: psb, miner: miner, - validator: NewConsensusValidator(miner, bc, b), + validator: NewConsensusValidator(miner, bc, db), msgLog: NewConsensusMessageLog(), minApprovals: minApprovals, privKey: privKey, @@ -105,15 +105,18 @@ func NewPBFTConsensusManager( address: address, } + return pcm +} + +func (pcm *PBFTConsensusManager) Run() { pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare) pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare) pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit) - bus.SubscribeAsync("beacon:newEntry", func(entry types2.BeaconEntry) { + pcm.bus.SubscribeAsync("beacon:newEntry", func(entry types2.BeaconEntry) { pcm.onNewBeaconEntry(entry) }, true) height, _ := pcm.blockchain.GetLatestBlockHeight() pcm.state.blockHeight = height + 1 - return pcm } func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { @@ -194,7 +197,7 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { } if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { - logrus.WithField("blockHash", cmsg.Blockhash).Warnf("received unknown block %x", cmsg.Blockhash) + logrus.WithField("blockHash", hex.EncodeToString(cmsg.Blockhash)).Warnf("received unknown block %x", cmsg.Blockhash) return } diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index a0cf8da..7bf49cb 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -3,7 +3,8 @@ package consensus import ( "encoding/hex" - "github.com/Secured-Finance/dione/beacon" + drand2 "github.com/Secured-Finance/dione/beacon/drand" + "github.com/sirupsen/logrus" "github.com/Secured-Finance/dione/blockchain" @@ -13,15 +14,15 @@ import ( type ConsensusValidator struct { validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool miner *blockchain.Miner - beacon beacon.BeaconNetwork + beacon *drand2.DrandBeacon blockchain *blockchain.BlockChain } -func NewConsensusValidator(miner *blockchain.Miner, bc *blockchain.BlockChain, b beacon.BeaconNetwork) *ConsensusValidator { +func NewConsensusValidator(miner *blockchain.Miner, bc *blockchain.BlockChain, db *drand2.DrandBeacon) *ConsensusValidator { cv := &ConsensusValidator{ miner: miner, blockchain: bc, - beacon: b, + beacon: db, } cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool{ diff --git a/consensus/dispute_manager.go b/consensus/dispute_manager.go index 672ca9c..28e3ba6 100644 --- a/consensus/dispute_manager.go +++ b/consensus/dispute_manager.go @@ -5,6 +5,8 @@ import ( "encoding/hex" "time" + "github.com/ethereum/go-ethereum/event" + types2 "github.com/Secured-Finance/dione/blockchain/types" "github.com/Secured-Finance/dione/types" @@ -28,51 +30,63 @@ type DisputeManager struct { 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, bc *blockchain.BlockChain) (*DisputeManager, error) { - newSubmittionsChan, submSubscription, err := ethClient.SubscribeOnNewSubmittions(ctx) + 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, - blockchain: bc, + 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(submission *dioneOracle.DioneOracleSubmittedOracleRequest) { diff --git a/go.mod b/go.mod index 1b53929..4b954af 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ 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.3.0 @@ -57,6 +56,7 @@ require ( 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 + 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 diff --git a/go.sum b/go.sum index 57fe94b..bf32893 100644 --- a/go.sum +++ b/go.sum @@ -1820,8 +1820,12 @@ 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= @@ -2129,6 +2133,7 @@ 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= 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/node.go b/node/node.go index 786ba34..615c86b 100644 --- a/node/node.go +++ b/node/node.go @@ -2,20 +2,17 @@ package node import ( "context" - "crypto/rand" - "flag" - "fmt" - "io/ioutil" - "os" - "path" - "runtime" "time" - "github.com/Secured-Finance/dione/blockchain" + drand2 "github.com/Secured-Finance/dione/beacon/drand" - "github.com/multiformats/go-multiaddr" + "github.com/Secured-Finance/dione/pubsub" - "github.com/asaskevich/EventBus" + "github.com/Secured-Finance/dione/consensus" + + "github.com/Secured-Finance/dione/blockchain/sync" + + "go.uber.org/fx" "github.com/fxamacker/cbor/v2" @@ -23,31 +20,16 @@ import ( types2 "github.com/Secured-Finance/dione/blockchain/types" - gorpc "github.com/libp2p/go-libp2p-gorpc" - "github.com/Secured-Finance/dione/blockchain/pool" - "github.com/Secured-Finance/dione/blockchain/sync" - - "github.com/Secured-Finance/dione/consensus" - pubsub2 "github.com/Secured-Finance/dione/pubsub" - "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" "golang.org/x/xerrors" - "github.com/Secured-Finance/dione/beacon" - "github.com/Secured-Finance/dione/config" "github.com/Secured-Finance/dione/ethclient" - "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/host" "github.com/sirupsen/logrus" ) @@ -56,192 +38,61 @@ 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 *blockchain.Miner - Beacon beacon.BeaconNetwork - DisputeManager *consensus.DisputeManager - BlockPool *pool.BlockPool - MemPool *pool.Mempool - BlockChain *blockchain.BlockChain - SyncManager sync.SyncManager - NetworkService *NetworkService - NetworkRPCHost *gorpc.Server - Bus EventBus.Bus - //Cache cache.Cache - //Wallet *wallet.LocalWallet -} +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 + } - bus := EventBus.New() - n.Bus = bus + // Run pubsub router + pubSubRouter.Run() - // initialize libp2p host - lhost, err := provideLibp2pHost(n.Config, prvKey) - if err != nil { - logrus.Fatal(err) - } - n.Host = lhost - logrus.WithField( - "multiaddress", - fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", - n.Config.ListenAddr, - n.Config.ListenPort, - n.Host.ID().Pretty(), - )).Info("Libp2p host has been initialized!") + // Subscribe on new requests event channel from Ethereum + err = subscribeOnEthContractsAsync(context.TODO(), ethClient, mp) + if err != nil { + return err + } - // initialize ethereum client - ethClient, err := provideEthereumClient(n.Config) - if err != nil { - logrus.WithField("err", err.Error()).Fatal("Failed to initialize Ethereum client") - } - n.Ethereum = ethClient - //goland:noinspection ALL - logrus.WithField("ethAddress", ethClient.GetEthAddress().Hex()).Info("Ethereum client has been initialized!") + // Run blockchain sync manager + syncManager.Run() - // initialize blockchain rpc clients - err = n.setupRPCClients() - if err != nil { - logrus.Fatal(err) - } - logrus.Info("Foreign Blockchain RPC clients has been successfully configured!") + // Run consensus manager + consensusManager.Run() - // initialize pubsub subsystem - psb := providePubsubRouter(lhost, n.Config) - n.PubSubRouter = psb - logrus.Info("PubSub subsystem has been initialized!") + // Run dispute manager + disputeManager.Run(context.TODO()) - // get list of bootstrap multiaddresses - baddrs, err := provideBootstrapAddrs(n.Config) - if err != nil { - logrus.Fatal(err) - } - - // initialize peer discovery - peerDiscovery, err := providePeerDiscovery(baddrs, lhost, pexDiscoveryUpdateTime) - if err != nil { - logrus.Fatal(err) - } - n.PeerDiscovery = peerDiscovery - logrus.Info("Peer discovery subsystem has been initialized!") - - // initialize random beacon network subsystem - randomBeaconNetwork, err := provideBeacon(psb.Pubsub, bus) - if err != nil { - logrus.Fatal(err) - } - n.Beacon = randomBeaconNetwork - logrus.Info("Random beacon subsystem has been initialized!") - - // == initialize blockchain modules - - // initialize mempool - mp, err := provideMemPool(bus) - if err != nil { - logrus.Fatalf("Failed to initialize mempool: %s", err.Error()) - } - n.MemPool = mp - logrus.Info("Mempool has been successfully initialized!") - - // initialize mining subsystem - miner := provideMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Ethereum, prvKey, mp) - n.Miner = miner - logrus.Info("Mining subsystem has been initialized!") - - // initialize blockpool database - bc, err := provideBlockChain(n.Config, bus, miner, randomBeaconNetwork.Beacon) - if err != nil { - logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) - } - n.BlockChain = bc - logrus.Info("Block pool database has been successfully initialized!") - - bp, err := provideBlockPool(mp, bus) - if err != nil { - logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) - } - n.BlockPool = bp - logrus.Info("Blockpool has been successfully initialized!") - - ns := provideNetworkService(bc, mp) - n.NetworkService = ns - rpcHost := provideNetworkRPCHost(lhost) - err = rpcHost.Register(ns) - if err != nil { - logrus.Fatal(err) - } - logrus.Info("Direct RPC has been successfully initialized!") - - // initialize libp2p-gorpc client - r := provideP2PRPCClient(lhost) - - // initialize sync manager - - var baddr multiaddr.Multiaddr - if len(baddrs) == 0 { - baddr = nil - } else { - baddr = baddrs[0] - } - sm, err := provideSyncManager(bus, bc, mp, r, baddr, psb) // FIXME here we just pick up first bootstrap in list - if err != nil { - logrus.Fatal(err) - } - n.SyncManager = sm - logrus.Info("Blockchain sync subsystem has been successfully initialized!") - - // initialize consensus subsystem - consensusManager := provideConsensusManager(bus, psb, miner, bc, ethClient, prvKey, n.Config.ConsensusMinApprovals, bp, randomBeaconNetwork, mp, n.Host.ID()) - n.ConsensusManager = consensusManager - logrus.Info("Consensus subsystem has been initialized!") - - // initialize dispute subsystem - disputeManager, err := provideDisputeManager(context.TODO(), ethClient, consensusManager, config, bc) - if err != nil { - logrus.Fatal(err) - } - n.DisputeManager = disputeManager - logrus.Info("Dispute subsystem has been initialized!") - - // initialize internal eth wallet - //w, err := provideWallet(n.Host.ID(), rawPrivKey) - //if err != nil { - // logrus.Fatal(err) - //} - //n.Wallet = w - - return n, nil -} - -func (n *Node) Run(ctx context.Context) error { - err := n.runLibp2pAsync(ctx) - if err != nil { - return err - } - n.subscribeOnEthContractsAsync(ctx) - - for { - select { - case <-ctx.Done(): return nil - } - } + }, + OnStop: func(ctx context.Context) error { + // TODO + return nil + }, + }) } -func (n *Node) runLibp2pAsync(ctx context.Context) error { +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) } @@ -249,7 +100,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) } @@ -264,12 +115,12 @@ 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.WithField("peer", newPeer.ID).Info("Discovered new peer, connecting...") // Connect to the peer - if err := n.Host.Connect(ctx, newPeer); err != nil { + if err := h.Connect(ctx, newPeer); err != nil { logrus.WithFields(logrus.Fields{ "peer": newPeer.ID, "err": err.Error(), @@ -283,10 +134,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() { @@ -318,7 +169,7 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { continue } tx := types2.CreateTransaction(data) - err = n.MemPool.StoreTx(tx) + err = mp.StoreTx(tx) if err != nil { logrus.Errorf("Failed to store tx in mempool: %s", err.Error()) continue @@ -326,107 +177,45 @@ func (n *Node) subscribeOnEthContractsAsync(ctx context.Context) { } 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 (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 Start() { - 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) - }, - }) - - 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 { - // FIXME just a little hack - 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() - _, err = f.Write(r) - if err != nil { - logrus.Fatal(err) - } - } 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, + provideMiner, + provideBlockChain, + provideBlockPool, + provideSyncManager, + provideNetworkRPCHost, + provideNetworkService, + provideDirectRPCClient, + provideConsensusManager, + provideDisputeManager, + ), + fx.Invoke( + configureLogger, + configureDirectRPC, + configureForeignBlockchainRPC, + runNode, + ), + fx.NopLogger, + ).Run() } diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index e221b28..779ffe7 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -2,8 +2,20 @@ package node import ( "context" + "crypto/rand" + "flag" "fmt" - "time" + "io/ioutil" + "os" + "path" + "runtime" + + "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" @@ -19,7 +31,6 @@ import ( "github.com/Secured-Finance/dione/blockchain/pool" - "github.com/Secured-Finance/dione/beacon" "github.com/Secured-Finance/dione/cache" "github.com/Secured-Finance/dione/config" "github.com/Secured-Finance/dione/consensus" @@ -28,13 +39,11 @@ import ( "github.com/Secured-Finance/dione/types" "github.com/Secured-Finance/dione/wallet" pex "github.com/Secured-Finance/go-libp2p-pex" - "github.com/ethereum/go-ethereum/common" "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" - pubsub2 "github.com/libp2p/go-libp2p-pubsub" "github.com/multiformats/go-multiaddr" "golang.org/x/xerrors" ) @@ -56,21 +65,30 @@ func provideCache(config *config.Config) cache.Cache { return backend } -func provideDisputeManager(ctx context.Context, ethClient *ethclient.EthereumClient, pcm *consensus.PBFTConsensusManager, cfg *config.Config, bc *blockchain.BlockChain) (*consensus.DisputeManager, error) { - return consensus.NewDisputeManager(ctx, ethClient, pcm, cfg.Ethereum.DisputeVoteWindow, bc) -} - -func provideMiner(peerID peer.ID, ethAddress common.Address, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *blockchain.Miner { - return blockchain.NewMiner(peerID, ethAddress, ethClient, privateKey, mempool) -} - -func provideBeacon(ps *pubsub2.PubSub, bus EventBus.Bus) (beacon.BeaconNetwork, error) { - bc, err := drand2.NewDrandBeacon(ps, bus) +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 { - return beacon.BeaconNetwork{}, fmt.Errorf("failed to setup drand beacon: %w", err) + logrus.Fatal(err) } - // NOTE: currently we use only one network - return beacon.BeaconNetwork{Start: config.DrandChainGenesisTime, Beacon: bc}, nil + + logrus.Info("Dispute subsystem has been initialized!") + + return dm +} + +func provideMiner(h host.Host, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *blockchain.Miner { + miner := blockchain.NewMiner(h.ID(), *ethClient.GetEthAddress(), ethClient, privateKey, mempool) + logrus.Info("Mining subsystem has been initialized!") + return miner +} + +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? @@ -90,123 +108,287 @@ func provideWallet(peerID peer.ID, privKey []byte) (*wallet.LocalWallet, error) return w, nil } -func provideEthereumClient(config *config.Config) (*ethclient.EthereumClient, error) { +func provideEthereumClient(config *config.Config) *ethclient.EthereumClient { ethereum := ethclient.NewEthereumClient() err := ethereum.Initialize(&config.Ethereum) if err != nil { - return nil, err + logrus.Fatal(err) } - return ethereum, nil + + logrus.WithField("ethAddress", ethereum.GetEthAddress().Hex()).Info("Ethereum client has been initialized!") + + return ethereum } func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubRouter { - return pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap) + psb := pubsub.NewPubSubRouter(lhost, config.PubSub.ServiceTopicName, config.IsBootstrap) + logrus.Info("PubSub subsystem has been initialized!") + return psb } func provideConsensusManager( + h host.Host, + cfg *config.Config, bus EventBus.Bus, psb *pubsub.PubSubRouter, miner *blockchain.Miner, bc *blockchain.BlockChain, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, - minApprovals int, bp *pool.BlockPool, - b beacon.BeaconNetwork, + db *drand2.DrandBeacon, mp *pool.Mempool, - address peer.ID, ) *consensus.PBFTConsensusManager { - return consensus.NewPBFTConsensusManager( + c := consensus.NewPBFTConsensusManager( bus, psb, - minApprovals, + cfg.ConsensusMinApprovals, privateKey, ethClient, miner, bc, bp, - b, + db, mp, - address, + h.ID(), ) + logrus.Info("Consensus subsystem has been initialized!") + return c } -func provideLibp2pHost(config *config.Config, privateKey crypto.PrivKey) (host.Host, error) { +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 { - return nil, xerrors.Errorf("failed to parse multiaddress: %v", err) + logrus.Fatalf("Failed to parse multiaddress: %s", err.Error()) } - host, err := libp2p.New( + libp2pHost, 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) + logrus.Fatal(err) } - return host, nil + 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, error) { +func provideBootstrapAddrs(c *config.Config) []multiaddr.Multiaddr { if c.IsBootstrap { - return nil, nil + return nil } var bootstrapMaddrs []multiaddr.Multiaddr for _, a := range c.BootstrapNodes { maddr, err := multiaddr.NewMultiaddr(a) if err != nil { - return nil, xerrors.Errorf("invalid multiaddress of bootstrap node: %v", err) + logrus.Fatalf("Invalid multiaddress of bootstrap node: %v", err) } bootstrapMaddrs = append(bootstrapMaddrs, maddr) } - return bootstrapMaddrs, nil + return bootstrapMaddrs } -func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host, pexDiscoveryUpdateTime time.Duration) (discovery.Discovery, error) { - pexDiscovery, err := pex.NewPEXDiscovery(h, baddrs, pexDiscoveryUpdateTime) +func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host) discovery.Discovery { + pexDiscovery, err := pex.NewPEXDiscovery(h, baddrs, DefaultPEXUpdateTime) if err != nil { - return nil, xerrors.Errorf("failed to setup pex pexDiscovery: %v", err) + logrus.Fatalf("Failed to setup libp2p PEX discovery: %s", err.Error()) } - return pexDiscovery, nil + logrus.Info("Peer discovery subsystem has been initialized!") + + return pexDiscovery } -func provideBlockChain(config *config.Config, bus EventBus.Bus, miner *blockchain.Miner, b beacon.BeaconAPI) (*blockchain.BlockChain, error) { - return blockchain.NewBlockChain(config.Blockchain.DatabasePath, bus, miner, b) +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, error) { - return pool.NewMempool(bus) +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, r *gorpc.Client, bootstrap multiaddr.Multiaddr, psb *pubsub.PubSubRouter) (sync.SyncManager, error) { +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 bootstrap != nil { - addr, err := peer.AddrInfoFromP2pAddr(bootstrap) + + if bootstrapAddresses != nil { + addr, err := peer.AddrInfoFromP2pAddr(bootstrapAddresses[0]) // FIXME if err != nil { - return nil, err + logrus.Fatal(err) } bootstrapPeerID = addr.ID } - return sync.NewSyncManager(bus, bp, mp, r, bootstrapPeerID, psb), nil + sm := sync.NewSyncManager(bus, bp, mp, c, bootstrapPeerID, psb) + logrus.Info("Blockchain sync subsystem has been successfully initialized!") + + return sm } -func provideP2PRPCClient(h host.Host) *gorpc.Client { +func provideDirectRPCClient(h host.Host) *gorpc.Client { return gorpc.NewClient(h, DioneProtocolID) } func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *NetworkService { - return NewNetworkService(bp, mp) + ns := NewNetworkService(bp, mp) + logrus.Info("Direct RPC has been successfully initialized!") + return ns } -func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) (*pool.BlockPool, error) { - return pool.NewBlockPool(mp, bus) +func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) *pool.BlockPool { + bp, err := pool.NewBlockPool(mp, bus) + if err != nil { + logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) + } + logrus.Info("Blockpool 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) + } + + 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!") } diff --git a/pubsub/pubsub_router.go b/pubsub/pubsub_router.go index 4653583..0d547ad 100644 --- a/pubsub/pubsub_router.go +++ b/pubsub/pubsub_router.go @@ -29,10 +29,11 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR ctx, ctxCancel := context.WithCancel(context.Background()) psr := &PubSubRouter{ - node: h, - context: ctx, - contextCancel: ctxCancel, - handlers: make(map[PubSubMessageType][]Handler), + node: h, + context: ctx, + contextCancel: ctxCancel, + handlers: make(map[PubSubMessageType][]Handler), + oracleTopicName: oracleTopic, } var pbOptions []pubsub.Option @@ -61,7 +62,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 +72,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 +83,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 +92,6 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR } } }() - - return psr } func (psr *PubSubRouter) handleMessage(p *pubsub.Message) { From 430a994a76c1038a7d7cc932c71b353974092bee Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 22 Jul 2021 01:02:42 +0300 Subject: [PATCH 48/59] Return reward to stake balance instead of node's token balance --- eth-contracts/contracts/DioneOracle.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From d179ffcd76c8d67f9a9bf9a0b387498427a26df2 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 27 Jul 2021 00:18:16 +0300 Subject: [PATCH 49/59] Implement consensus state change watching to fix stage race conditions --- blockchain/blockchain.go | 4 - blockchain/miner.go | 69 +++++++--- blockchain/pool/blockpool.go | 101 --------------- blockchain/sync/sync_mgr.go | 11 +- cache/cache.go | 1 + cache/inmemory_cache.go | 5 + cache/redis_cache.go | 11 ++ config/win_config.go | 2 +- consensus/consensus.go | 205 +++++++++++++++--------------- consensus/consensus_round_pool.go | 119 +++++++++++++++++ node/node.go | 9 +- node/node_dep_providers.go | 30 +++-- types/electionproof.go | 2 +- 13 files changed, 322 insertions(+), 247 deletions(-) delete mode 100644 blockchain/pool/blockpool.go create mode 100644 consensus/consensus_round_pool.go diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index a634a5a..86dbc3d 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -181,10 +181,6 @@ func (bc *BlockChain) StoreBlock(block *types2.Block) error { if err = bc.setLatestBlockHeight(block.Header.Height); err != nil { return err } - } else if 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) diff --git a/blockchain/miner.go b/blockchain/miner.go index 46b63bb..3f5c97d 100644 --- a/blockchain/miner.go +++ b/blockchain/miner.go @@ -4,7 +4,10 @@ import ( "errors" "fmt" "math/big" - "sync" + + "github.com/libp2p/go-libp2p-core/host" + + "github.com/asaskevich/EventBus" "github.com/Secured-Finance/dione/beacon" "github.com/Secured-Finance/dione/types" @@ -27,34 +30,54 @@ var ( ) type Miner struct { - address peer.ID - ethAddress common.Address - mutex sync.Mutex - ethClient *ethclient.EthereumClient - minerStake *big.Int - networkStake *big.Int - privateKey crypto.PrivKey - mempool *pool.Mempool + 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( - address peer.ID, - ethAddress common.Address, + h host.Host, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool, + bus EventBus.Bus, ) *Miner { - return &Miner{ - address: address, - ethAddress: ethAddress, + 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.ethAddress) + mStake, err := m.ethClient.GetMinerStake(*m.ethClient.GetEthAddress()) if err != nil { logrus.Warn("Can't get miner stake", err) @@ -92,15 +115,19 @@ func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, error) { return mStake, nStake, nil } -func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) { - logrus.WithField("height", lastBlockHeader.Height+1).Debug("Trying to mine new block...") +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( - lastBlockHeader.Height+1, + m.latestBlockHeader.Height+1, m.address, randomness, randomnessRound, @@ -113,16 +140,18 @@ func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHe } if winner == nil { - logrus.WithField("height", lastBlockHeader.Height+1).Debug("Block is not mined because we are not leader in consensus round") + 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(lastBlockHeader, txs, m.ethAddress, m.privateKey, winner) + 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) } diff --git a/blockchain/pool/blockpool.go b/blockchain/pool/blockpool.go deleted file mode 100644 index e8882da..0000000 --- a/blockchain/pool/blockpool.go +++ /dev/null @@ -1,101 +0,0 @@ -package pool - -import ( - "bytes" - "encoding/hex" - "fmt" - "time" - - "github.com/asaskevich/EventBus" - - "github.com/sirupsen/logrus" - - "github.com/Secured-Finance/dione/blockchain/types" - "github.com/Secured-Finance/dione/cache" -) - -// BlockPool is pool for blocks that isn't not validated or committed yet -type BlockPool struct { - mempool *Mempool - knownBlocks cache.Cache - acceptedBlocks cache.Cache - bus EventBus.Bus -} - -func NewBlockPool(mp *Mempool, bus EventBus.Bus) (*BlockPool, error) { - bp := &BlockPool{ - acceptedBlocks: cache.NewInMemoryCache(), // here we need to use separate cache - knownBlocks: cache.NewInMemoryCache(), - mempool: mp, - bus: bus, - } - - return bp, nil -} - -func (bp *BlockPool) AddBlock(block *types.Block) error { - err := bp.knownBlocks.StoreWithTTL(hex.EncodeToString(block.Header.Hash), block, 10*time.Minute) - if err != nil { - return err - } - logrus.WithField("hash", fmt.Sprintf("%x", block.Header.Hash)).Debug("New block discovered") - bp.bus.Publish("blockpool:knownBlockAdded", block) - return nil -} - -func (bp *BlockPool) GetBlock(blockhash []byte) (*types.Block, error) { - var block types.Block - err := bp.knownBlocks.Get(hex.EncodeToString(blockhash), &block) - return &block, err -} - -// PruneBlocks cleans known blocks list. It is called when new consensus round starts. -func (bp *BlockPool) PruneBlocks() { - for k := range bp.knownBlocks.Items() { - bp.knownBlocks.Delete(k) - } - bp.bus.Publish("blockpool:pruned") -} - -func (bp *BlockPool) AddAcceptedBlock(block *types.Block) error { - err := bp.acceptedBlocks.Store(hex.EncodeToString(block.Header.Hash), block) - if err != nil { - return err - } - bp.bus.Publish("blockpool:acceptedBlockAdded", block) - return nil -} - -func (bp *BlockPool) GetAllAcceptedBlocks() []*types.Block { - var blocks []*types.Block - for _, v := range bp.acceptedBlocks.Items() { - blocks = append(blocks, v.(*types.Block)) - } - return blocks -} - -// PruneAcceptedBlocks cleans accepted blocks list. It is called when new consensus round starts. -func (bp *BlockPool) PruneAcceptedBlocks(committedBlock *types.Block) { - for k, v := range bp.acceptedBlocks.Items() { - block := v.(*types.Block) - for _, v := range block.Data { - if !containsTx(committedBlock.Data, v) { - v.MerkleProof = nil - err := bp.mempool.StoreTx(v) // return transactions back to mempool - if err != nil { - logrus.Error(err) - } - } - } - bp.acceptedBlocks.Delete(k) - } -} - -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/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index da9ef4e..cab9b7f 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -98,21 +98,14 @@ func (sm *syncManager) doInitialBlockPoolSync() error { return nil } - ourLastHeight, err := sm.blockpool.GetLatestBlockHeight() - if err == blockchain.ErrLatestHeightNil { - gBlock := types2.GenesisBlock() - err = sm.blockpool.StoreBlock(gBlock) // commit genesis block - if err != nil { - return err - } - } + ourLastHeight, _ := sm.blockpool.GetLatestBlockHeight() if sm.bootstrapPeer == "" { return nil // FIXME } var reply wire.LastBlockHeightReply - err = sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "LastBlockHeight", nil, &reply) + err := sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "LastBlockHeight", nil, &reply) if err != nil { return err } diff --git a/cache/cache.go b/cache/cache.go index 5af78e3..96f37fe 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -13,4 +13,5 @@ type Cache interface { Get(key string, value interface{}) error Delete(key string) Items() map[string]interface{} + Exists(key string) bool } diff --git a/cache/inmemory_cache.go b/cache/inmemory_cache.go index 902180a..0acef75 100644 --- a/cache/inmemory_cache.go +++ b/cache/inmemory_cache.go @@ -62,3 +62,8 @@ func (imc *InMemoryCache) Items() map[string]interface{} { } 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 index 3f7ace3..9eacdab 100644 --- a/cache/redis_cache.go +++ b/cache/redis_cache.go @@ -85,3 +85,14 @@ func (rc *RedisCache) Delete(key string) { 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/win_config.go b/config/win_config.go index 1cf9295..10a76c0 100644 --- a/config/win_config.go +++ b/config/win_config.go @@ -2,4 +2,4 @@ package config import "math/big" -var ExpectedLeadersPerEpoch = big.NewInt(1) +var ExpectedLeadersPerEpoch = big.NewInt(2) diff --git a/consensus/consensus.go b/consensus/consensus.go index 0e2909f..64a12e8 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math/big" - "sync" drand2 "github.com/Secured-Finance/dione/beacon/drand" @@ -38,39 +37,20 @@ var ( ErrNoAcceptedBlocks = errors.New("there is no accepted blocks") ) -type StateStatus uint8 - -const ( - StateStatusUnknown = iota - - StateStatusPrePrepared - StateStatusPrepared - StateStatusCommited -) - type PBFTConsensusManager struct { - bus EventBus.Bus - psb *pubsub.PubSubRouter - minApprovals int // FIXME - privKey crypto.PrivKey - msgLog *ConsensusMessageLog - validator *ConsensusValidator - ethereumClient *ethclient.EthereumClient - miner *blockchain.Miner - blockPool *pool.BlockPool - mempool *pool.Mempool - blockchain *blockchain.BlockChain - state *State - address peer.ID -} - -type State struct { - mutex sync.Mutex - drandRound uint64 - randomness []byte - blockHeight uint64 - status StateStatus - ready bool + bus EventBus.Bus + psb *pubsub.PubSubRouter + minApprovals int // FIXME + privKey crypto.PrivKey + msgLog *ConsensusMessageLog + validator *ConsensusValidator + ethereumClient *ethclient.EthereumClient + miner *blockchain.Miner + consensusRoundPool *ConsensusRoundPool + mempool *pool.Mempool + blockchain *blockchain.BlockChain + address peer.ID + stateChangeChannels map[string]map[State][]chan bool } func NewPBFTConsensusManager( @@ -81,28 +61,25 @@ func NewPBFTConsensusManager( ethereumClient *ethclient.EthereumClient, miner *blockchain.Miner, bc *blockchain.BlockChain, - bp *pool.BlockPool, + bp *ConsensusRoundPool, db *drand2.DrandBeacon, mempool *pool.Mempool, address peer.ID, ) *PBFTConsensusManager { pcm := &PBFTConsensusManager{ - psb: psb, - miner: miner, - validator: NewConsensusValidator(miner, bc, db), - msgLog: NewConsensusMessageLog(), - minApprovals: minApprovals, - privKey: privKey, - ethereumClient: ethereumClient, - state: &State{ - ready: false, - status: StateStatusUnknown, - }, - bus: bus, - blockPool: bp, - mempool: mempool, - blockchain: bc, - address: address, + psb: psb, + miner: miner, + validator: NewConsensusValidator(miner, bc, db), + msgLog: NewConsensusMessageLog(), + minApprovals: minApprovals, + privKey: privKey, + ethereumClient: ethereumClient, + bus: bus, + consensusRoundPool: bp, + mempool: mempool, + blockchain: bc, + address: address, + stateChangeChannels: map[string]map[State][]chan bool{}, } return pcm @@ -115,8 +92,6 @@ func (pcm *PBFTConsensusManager) Run() { pcm.bus.SubscribeAsync("beacon:newEntry", func(entry types2.BeaconEntry) { pcm.onNewBeaconEntry(entry) }, true) - height, _ := pcm.blockchain.GetLatestBlockHeight() - pcm.state.blockHeight = height + 1 } func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { @@ -125,15 +100,12 @@ func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { return err } pcm.psb.BroadcastToServiceTopic(prePrepareMsg) - pcm.blockPool.AddBlock(blk) + pcm.consensusRoundPool.AddConsensusInfo(blk) logrus.WithField("blockHash", fmt.Sprintf("%x", blk.Header.Hash)).Debugf("Entered into PREPREPARED state") - pcm.state.status = StateStatusPrePrepared return nil } func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) { - //pcm.state.mutex.Lock() - //defer pcm.state.mutex.Unlock() var prePrepare types.PrePrepareMessage err := cbor.Unmarshal(message.Payload, &prePrepare) if err != nil { @@ -166,7 +138,18 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) "blockHash": fmt.Sprintf("%x", cmsg.Block.Header.Hash), "from": message.From.String(), }).Debug("Received PREPREPARE message") - pcm.blockPool.AddBlock(cmsg.Block) + pcm.consensusRoundPool.AddConsensusInfo(cmsg.Block) + + encodedHash := hex.EncodeToString(cmsg.Blockhash) + if m, ok := pcm.stateChangeChannels[encodedHash]; ok { + if channels, ok := m[StateStatusPrePrepared]; ok { + for _, v := range channels { + v <- true + close(v) + delete(pcm.stateChangeChannels, encodedHash) + } + } + } prepareMsg, err := NewMessage(cmsg, types.ConsensusMessageTypePrepare, pcm.privKey) if err != nil { @@ -176,12 +159,9 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) logrus.WithField("blockHash", fmt.Sprintf("%x", prePrepare.Block.Header.Hash)).Debugf("Entered into PREPREPARED state") pcm.psb.BroadcastToServiceTopic(prepareMsg) - pcm.state.status = StateStatusPrePrepared } func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { - //pcm.state.mutex.Lock() - //defer pcm.state.mutex.Unlock() var prepare types.PrepareMessage err := cbor.Unmarshal(message.Payload, &prepare) if err != nil { @@ -196,9 +176,18 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { Signature: prepare.Signature, } - if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { - logrus.WithField("blockHash", hex.EncodeToString(cmsg.Blockhash)).Warnf("received unknown block %x", cmsg.Blockhash) - return + if _, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { + encodedHash := hex.EncodeToString(cmsg.Blockhash) + logrus.WithField("blockHash", encodedHash).Warn("received PREPARE for unknown block") + waitingCh := make(chan bool) + if _, ok := pcm.stateChangeChannels[encodedHash]; !ok { + pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{} + } + pcm.stateChangeChannels[encodedHash][StateStatusPrePrepared] = append(pcm.stateChangeChannels[encodedHash][StateStatusPrePrepared], waitingCh) + result := <-waitingCh + if !result { + return + } } if pcm.msgLog.Exists(cmsg) { @@ -225,13 +214,23 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { } pcm.psb.BroadcastToServiceTopic(commitMsg) logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into PREPARED state") - pcm.state.status = StateStatusPrepared + pcm.consensusRoundPool.UpdateConsensusState(cmsg.Blockhash, StateStatusPrepared) + + // pull watchers + encodedHash := hex.EncodeToString(cmsg.Blockhash) + if m, ok := pcm.stateChangeChannels[encodedHash]; ok { + if channels, ok := m[StateStatusPrepared]; ok { + for _, v := range channels { + v <- true + close(v) + delete(pcm.stateChangeChannels, encodedHash) + } + } + } } } func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { - //pcm.state.mutex.Lock() - //defer pcm.state.mutex.Unlock() var commit types.CommitMessage err := cbor.Unmarshal(message.Payload, &commit) if err != nil { @@ -246,11 +245,27 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { Signature: commit.Signature, } - if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { - logrus.Warnf("received unknown block %x", cmsg.Blockhash) + ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash) + + if errors.Is(err, cache.ErrNotFound) { + logrus.WithField("blockHash", hex.EncodeToString(cmsg.Blockhash)).Warnf("received COMMIT for unknown block") return } + if ci.State < StateStatusPrepared { + encodedHash := hex.EncodeToString(cmsg.Blockhash) + logrus.WithField("blockHash", encodedHash).Warnf("incorrect state of block consensus") + waitingCh := make(chan bool) + if _, ok := pcm.stateChangeChannels[encodedHash]; !ok { + pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{} + } + pcm.stateChangeChannels[encodedHash][StateStatusPrepared] = append(pcm.stateChangeChannels[encodedHash][StateStatusPrepared], waitingCh) + result := <-waitingCh + if !result { + return + } + } + if pcm.msgLog.Exists(cmsg) { logrus.Tracef("received existing commit msg for block %x", cmsg.Blockhash) return @@ -268,27 +283,22 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { }).Debug("Received COMMIT message") if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals { - block, err := pcm.blockPool.GetBlock(cmsg.Blockhash) - if err != nil { - logrus.Error(err) - return - } - pcm.blockPool.AddAcceptedBlock(block) logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into COMMIT state") - pcm.state.status = StateStatusCommited + pcm.consensusRoundPool.UpdateConsensusState(cmsg.Blockhash, StateStatusCommited) } } 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": pcm.state.blockHeight, + "height": height + 1, }).Infof("No accepted blocks in the current consensus round") } else { logrus.WithFields(logrus.Fields{ - "height": pcm.state.blockHeight, + "height": height + 1, "err": err.Error(), }).Errorf("Failed to select the block in the current consensus round") return @@ -312,26 +322,20 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { if block.Header.Proposer.String() == pcm.address.String() { pcm.submitTasksFromBlock(block) } - - pcm.state.blockHeight = pcm.state.blockHeight + 1 } - // get latest block - height, err := pcm.blockchain.GetLatestBlockHeight() - if err != nil { - logrus.Error(err) - return - } - blockHeader, err := pcm.blockchain.FetchBlockHeaderByHeight(height) - if err != nil { - logrus.Error(err) - return + 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) } - pcm.state.drandRound = entry.Round - pcm.state.randomness = entry.Data - - minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round, blockHeader) + 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") @@ -343,7 +347,6 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { // if we are round winner if minedBlock != nil { - logrus.WithField("height", pcm.state.blockHeight).Infof("We have been elected in the current consensus round") err = pcm.propose(minedBlock) if err != nil { logrus.Errorf("Failed to propose the block: %s", err.Error()) @@ -388,32 +391,34 @@ func (pcm *PBFTConsensusManager) submitTasksFromBlock(block *types3.Block) { } func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { - blocks := pcm.blockPool.GetAllAcceptedBlocks() + blocks := pcm.consensusRoundPool.GetAllBlocksWithCommit() if blocks == nil { return nil, ErrNoAcceptedBlocks } var maxStake *big.Int + var maxWinCount int64 = -1 var selectedBlock *types3.Block for _, v := range blocks { - stake, err := pcm.ethereumClient.GetMinerStake(v.Header.ProposerEth) + stake, err := pcm.ethereumClient.GetMinerStake(v.Block.Header.ProposerEth) if err != nil { return nil, err } - if maxStake != nil { - if stake.Cmp(maxStake) == -1 { + if maxStake != nil && maxWinCount != -1 { + if stake.Cmp(maxStake) == -1 || v.Block.Header.ElectionProof.WinCount < maxWinCount { continue } } maxStake = stake - selectedBlock = v + maxWinCount = v.Block.Header.ElectionProof.WinCount + selectedBlock = v.Block } logrus.WithFields(logrus.Fields{ "hash": hex.EncodeToString(selectedBlock.Header.Hash), "height": selectedBlock.Header.Height, "miner": selectedBlock.Header.Proposer.String(), }).Info("Committed new block") - pcm.blockPool.PruneAcceptedBlocks(selectedBlock) + pcm.consensusRoundPool.Prune() for _, v := range selectedBlock.Data { err := pcm.mempool.DeleteTx(v.Hash) if err != nil { diff --git a/consensus/consensus_round_pool.go b/consensus/consensus_round_pool.go new file mode 100644 index 0000000..902e066 --- /dev/null +++ b/consensus/consensus_round_pool.go @@ -0,0 +1,119 @@ +package consensus + +import ( + "bytes" + "encoding/hex" + "fmt" + "time" + + "github.com/Secured-Finance/dione/blockchain/pool" + + "github.com/asaskevich/EventBus" + + "github.com/sirupsen/logrus" + + "github.com/Secured-Finance/dione/blockchain/types" + "github.com/Secured-Finance/dione/cache" +) + +type State uint8 + +const ( + StateStatusUnknown = iota + + StateStatusPrePrepared + StateStatusPrepared + StateStatusCommited +) + +// ConsensusRoundPool is pool for blocks that isn't not validated or committed yet +type ConsensusRoundPool struct { + mempool *pool.Mempool + consensusInfoStorage cache.Cache + bus EventBus.Bus +} + +func NewConsensusRoundPool(mp *pool.Mempool, bus EventBus.Bus) (*ConsensusRoundPool, error) { + bp := &ConsensusRoundPool{ + consensusInfoStorage: cache.NewInMemoryCache(), + mempool: mp, + bus: bus, + } + + return bp, nil +} + +type ConsensusInfo struct { + Block *types.Block + State State +} + +func (crp *ConsensusRoundPool) AddConsensusInfo(block *types.Block) error { + encodedHash := hex.EncodeToString(block.Header.Hash) + + if crp.consensusInfoStorage.Exists(encodedHash) { + return nil + } + + err := crp.consensusInfoStorage.StoreWithTTL(encodedHash, &ConsensusInfo{ + Block: block, + State: StateStatusPrePrepared, + }, 10*time.Minute) + if err != nil { + return err + } + logrus.WithField("hash", fmt.Sprintf("%x", block.Header.Hash)).Debug("New block discovered") + crp.bus.Publish("blockpool:knownBlockAdded", block) + return nil +} + +func (crp *ConsensusRoundPool) UpdateConsensusState(blockhash []byte, newState State) error { + encodedHash := hex.EncodeToString(blockhash) + + var consensusInfo ConsensusInfo + err := crp.consensusInfoStorage.Get(encodedHash, &consensusInfo) + if err != nil { + return err + } + + if newState < consensusInfo.State { + return fmt.Errorf("attempt to set incorrect state") + } + consensusInfo.State = newState + crp.bus.Publish("blockpool:newConsensusState", blockhash, newState) + return crp.consensusInfoStorage.StoreWithTTL(encodedHash, &consensusInfo, 10*time.Minute) +} + +func (crp *ConsensusRoundPool) GetConsensusInfo(blockhash []byte) (*ConsensusInfo, error) { + var consensusInfo ConsensusInfo + err := crp.consensusInfoStorage.Get(hex.EncodeToString(blockhash), &consensusInfo) + return &consensusInfo, err +} + +// Prune cleans known blocks list. It is called when new consensus round starts. +func (crp *ConsensusRoundPool) Prune() { + for k := range crp.consensusInfoStorage.Items() { + crp.consensusInfoStorage.Delete(k) + } + crp.bus.Publish("blockpool:pruned") +} + +func (crp *ConsensusRoundPool) GetAllBlocksWithCommit() []*ConsensusInfo { + var consensusInfos []*ConsensusInfo + for _, v := range crp.consensusInfoStorage.Items() { + ci := v.(*ConsensusInfo) + if ci.State == StateStatusCommited { + consensusInfos = append(consensusInfos, ci) + } + } + 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/node/node.go b/node/node.go index 615c86b..b1b847b 100644 --- a/node/node.go +++ b/node/node.go @@ -4,6 +4,8 @@ import ( "context" "time" + "github.com/Secured-Finance/dione/blockchain" + drand2 "github.com/Secured-Finance/dione/beacon/drand" "github.com/Secured-Finance/dione/pubsub" @@ -50,6 +52,7 @@ func runNode( pubSubRouter *pubsub.PubSubRouter, disputeManager *consensus.DisputeManager, db *drand2.DrandBeacon, + bc *blockchain.BlockChain, ) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { @@ -200,7 +203,7 @@ func Start() { providePeerDiscovery, provideDrandBeacon, provideMempool, - provideMiner, + blockchain.NewMiner, provideBlockChain, provideBlockPool, provideSyncManager, @@ -214,8 +217,10 @@ func Start() { configureLogger, configureDirectRPC, configureForeignBlockchainRPC, + initializeBlockchain, + configureMiner, runNode, ), - fx.NopLogger, + //fx.NopLogger, ).Run() } diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 779ffe7..93f9b0e 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -10,6 +10,8 @@ import ( "path" "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" @@ -76,12 +78,6 @@ func provideDisputeManager(ethClient *ethclient.EthereumClient, pcm *consensus.P return dm } -func provideMiner(h host.Host, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *blockchain.Miner { - miner := blockchain.NewMiner(h.ID(), *ethClient.GetEthAddress(), ethClient, privateKey, mempool) - logrus.Info("Mining subsystem has been initialized!") - return miner -} - func provideDrandBeacon(ps *pubsub.PubSubRouter, bus EventBus.Bus) *drand2.DrandBeacon { db, err := drand2.NewDrandBeacon(ps.Pubsub, bus) if err != nil { @@ -135,7 +131,7 @@ func provideConsensusManager( bc *blockchain.BlockChain, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, - bp *pool.BlockPool, + bp *consensus.ConsensusRoundPool, db *drand2.DrandBeacon, mp *pool.Mempool, ) *consensus.PBFTConsensusManager { @@ -268,8 +264,8 @@ func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *Network return ns } -func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) *pool.BlockPool { - bp, err := pool.NewBlockPool(mp, bus) +func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) *consensus.ConsensusRoundPool { + bp, err := consensus.NewConsensusRoundPool(mp, bus) if err != nil { logrus.Fatalf("Failed to initialize blockpool: %s", err.Error()) } @@ -392,3 +388,19 @@ func configureForeignBlockchainRPC() { 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/types/electionproof.go b/types/electionproof.go index 5350dde..e95b2eb 100644 --- a/types/electionproof.go +++ b/types/electionproof.go @@ -106,7 +106,7 @@ func lambda(power, totalPower *big.Int) *big.Int { return lam } -var MaxWinCount = 3 * config.ExpectedLeadersPerEpoch.Int64() +var MaxWinCount = 10 * config.ExpectedLeadersPerEpoch.Int64() type poiss struct { lam *big.Int From 225c84e8a85fd8f5ff4b070ae8726845c4c866ac Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 27 Jul 2021 02:03:12 +0300 Subject: [PATCH 50/59] Implement creating necessary directories when creating private key file --- node/node_dep_providers.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 93f9b0e..25616be 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "path" + "path/filepath" "runtime" types2 "github.com/Secured-Finance/dione/blockchain/types" @@ -311,6 +312,14 @@ func providePrivateKey(cfg *config.Config) crypto.PrivKey { 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) From 7e8e4ccc41aa5e386c38dc48230173516f6bc9f0 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 28 Jul 2021 21:13:30 +0300 Subject: [PATCH 51/59] Separate secrets to another file, add some Dione-related tasks to hardhat project --- .gitignore | 3 ++- eth-contracts/hardhat.config.ts | 26 ++++++++++--------------- eth-contracts/hardhat.tasks.ts | 34 +++++++++++++++++++++++++++++++++ eth-contracts/tsconfig.json | 3 ++- 4 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 eth-contracts/hardhat.tasks.ts diff --git a/.gitignore b/.gitignore index 03dfc01..45d3320 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ eth-contracts/node_modules /.dione-config-4.toml .bootstrap_privkey eth-contracts/build -/.db +.db +eth-contracts/secrets.json \ No newline at end of file diff --git a/eth-contracts/hardhat.config.ts b/eth-contracts/hardhat.config.ts index 66a5258..3d6f9c2 100644 --- a/eth-contracts/hardhat.config.ts +++ b/eth-contracts/hardhat.config.ts @@ -1,30 +1,24 @@ -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"; 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 } }; \ 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..b188691 --- /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(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/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"] From b9c39ac7ba66d6916dc3b141b886ffcf14a73c07 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 28 Jul 2021 21:13:43 +0300 Subject: [PATCH 52/59] Fix DioneOracle unit test --- eth-contracts/test/DioneOracle.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 () { From 1670a6b2878929121271336002eff09fc75e94a2 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 28 Jul 2021 21:52:10 +0300 Subject: [PATCH 53/59] Add Etherscan plugin for Hardhat --- eth-contracts/hardhat.config.ts | 4 + eth-contracts/hardhat.tasks.ts | 2 +- eth-contracts/package-lock.json | 696 +++++++++++++++++++++++++++++++- eth-contracts/package.json | 3 +- 4 files changed, 701 insertions(+), 4 deletions(-) diff --git a/eth-contracts/hardhat.config.ts b/eth-contracts/hardhat.config.ts index 3d6f9c2..49d745b 100644 --- a/eth-contracts/hardhat.config.ts +++ b/eth-contracts/hardhat.config.ts @@ -5,6 +5,7 @@ import "solidity-coverage"; import "hardhat-gas-reporter"; import * as secrets from "./secrets.json"; import "./hardhat.tasks"; +import "@nomiclabs/hardhat-etherscan"; export default { solidity: { @@ -20,5 +21,8 @@ export default { gasReporter: { currency: 'USD', 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 index b188691..7b4b0ff 100644 --- a/eth-contracts/hardhat.tasks.ts +++ b/eth-contracts/hardhat.tasks.ts @@ -27,7 +27,7 @@ task("oracleRequest", "Makes oracle request to Mediator contract") .setAction(async (args, hre) => { const Mediator = await hre.ethers.getContractFactory("Mediator"); const contract = Mediator.attach(args.contract); - const res = await contract.request(args.chainid, args.method, args.param) + 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) 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", From cd671e6165cde854d5c4f94c3b2f77b1cca20e41 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 29 Jul 2021 00:47:09 +0300 Subject: [PATCH 54/59] Change temporarily pubsub engine to FloodSub --- consensus/validation/filecoin/filecoin.go | 5 +++++ pubsub/pubsub_router.go | 21 ++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/consensus/validation/filecoin/filecoin.go b/consensus/validation/filecoin/filecoin.go index fb5c867..06393cf 100644 --- a/consensus/validation/filecoin/filecoin.go +++ b/consensus/validation/filecoin/filecoin.go @@ -28,8 +28,13 @@ func ValidateGetTransaction(task *types.DioneTask) error { return validation.VerifyExactMatching(task) } +func ValidateGetBlock(task *types.DioneTask) error { + return validation.VerifyExactMatching(task) +} + func init() { validation.RegisterValidation(rtypes.RPCTypeFilecoin, map[string]func(*types.DioneTask) error{ "getTransaction": ValidateGetTransaction, + "getBlock": ValidateGetBlock, }) } diff --git a/pubsub/pubsub_router.go b/pubsub/pubsub_router.go index 0d547ad..2ff7fda 100644 --- a/pubsub/pubsub_router.go +++ b/pubsub/pubsub_router.go @@ -2,7 +2,6 @@ package pubsub import ( "context" - "time" "github.com/fxamacker/cbor/v2" @@ -40,19 +39,19 @@ func NewPubSubRouter(h host.Host, oracleTopic string, isBootstrap bool) *PubSubR 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..., From e8b220455e1d31c09ee6d04003b0369f38ce38c5 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 29 Jul 2021 00:48:01 +0300 Subject: [PATCH 55/59] Fix sync/race issues in consensus, refactor accepted blocks sorting --- consensus/consensus.go | 82 ++++++++++++++++++++++++++++++------------ node/node.go | 3 +- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 64a12e8..b099469 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" "math/big" + "sort" + "time" drand2 "github.com/Secured-Finance/dione/beacon/drand" @@ -99,6 +101,7 @@ func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { if err != nil { return err } + time.Sleep(1 * time.Second) // wait until all nodes will commit previous blocks pcm.psb.BroadcastToServiceTopic(prePrepareMsg) pcm.consensusRoundPool.AddConsensusInfo(blk) logrus.WithField("blockHash", fmt.Sprintf("%x", blk.Header.Hash)).Debugf("Entered into PREPREPARED state") @@ -178,8 +181,8 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { if _, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { encodedHash := hex.EncodeToString(cmsg.Blockhash) - logrus.WithField("blockHash", encodedHash).Warn("received PREPARE for unknown block") - waitingCh := make(chan bool) + logrus.WithField("blockHash", encodedHash).Warn("Received PREPARE before PREPREPARED state, waiting...") + waitingCh := make(chan bool, 1) if _, ok := pcm.stateChangeChannels[encodedHash]; !ok { pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{} } @@ -207,6 +210,14 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { }).Debug("Received PREPARE message") if len(pcm.msgLog.Get(types.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= pcm.minApprovals-1 { + ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash) + if err != nil { + logrus.Fatalf("This should never happen: %s", err.Error()) + } + if ci.State >= StateStatusPrepared { + return + } + commitMsg, err := NewMessage(cmsg, types.ConsensusMessageTypeCommit, pcm.privKey) if err != nil { logrus.Errorf("failed to create commit message: %v", err) @@ -247,15 +258,14 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash) - if errors.Is(err, cache.ErrNotFound) { - logrus.WithField("blockHash", hex.EncodeToString(cmsg.Blockhash)).Warnf("received COMMIT for unknown block") - return - } + encodedHash := hex.EncodeToString(cmsg.Blockhash) - if ci.State < StateStatusPrepared { - encodedHash := hex.EncodeToString(cmsg.Blockhash) - logrus.WithField("blockHash", encodedHash).Warnf("incorrect state of block consensus") - waitingCh := make(chan bool) + if errors.Is(err, cache.ErrNotFound) || ci.State < StateStatusPrepared { + logrus.WithFields(logrus.Fields{ + "blockHash": encodedHash, + "from": cmsg.From, + }).Warnf("Received COMMIT message before PREPARED state, waiting...") + waitingCh := make(chan bool, 1) if _, ok := pcm.stateChangeChannels[encodedHash]; !ok { pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{} } @@ -283,6 +293,14 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { }).Debug("Received COMMIT message") if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals { + ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash) + if err != nil { + logrus.Fatalf("This should never happen: %s", err.Error()) + } + if ci.State == StateStatusCommited { + return + } + logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into COMMIT state") pcm.consensusRoundPool.UpdateConsensusState(cmsg.Blockhash, StateStatusCommited) } @@ -395,24 +413,44 @@ func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { if blocks == nil { return nil, ErrNoAcceptedBlocks } - var maxStake *big.Int - var maxWinCount int64 = -1 var selectedBlock *types3.Block - for _, v := range blocks { - stake, err := pcm.ethereumClient.GetMinerStake(v.Block.Header.ProposerEth) + + sort.Slice(blocks, func(i, j int) bool { + iStake, err := pcm.ethereumClient.GetMinerStake(blocks[i].Block.Header.ProposerEth) if err != nil { - return nil, err + logrus.Error(err) + return false } - if maxStake != nil && maxWinCount != -1 { - if stake.Cmp(maxStake) == -1 || v.Block.Header.ElectionProof.WinCount < maxWinCount { - continue - } + jStake, err := pcm.ethereumClient.GetMinerStake(blocks[j].Block.Header.ProposerEth) + if err != nil { + logrus.Error(err) + return false } - maxStake = stake - maxWinCount = v.Block.Header.ElectionProof.WinCount - selectedBlock = v.Block + + if iStake.Cmp(jStake) == -1 { + return false + } + if iStake.Cmp(jStake) == 1 { + return true + } + + return blocks[i].Block.Header.ElectionProof.WinCount > blocks[i].Block.Header.ElectionProof.WinCount + }) + + 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, diff --git a/node/node.go b/node/node.go index b1b847b..ec72329 100644 --- a/node/node.go +++ b/node/node.go @@ -52,7 +52,6 @@ func runNode( pubSubRouter *pubsub.PubSubRouter, disputeManager *consensus.DisputeManager, db *drand2.DrandBeacon, - bc *blockchain.BlockChain, ) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { @@ -221,6 +220,6 @@ func Start() { configureMiner, runNode, ), - //fx.NopLogger, + fx.NopLogger, ).Run() } From 445fbf44f0a2809476f43ab637d2988df6a09d09 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 30 Jul 2021 01:53:48 +0300 Subject: [PATCH 56/59] Fix infinity loop when syncing mempool --- blockchain/sync/sync_mgr.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockchain/sync/sync_mgr.go b/blockchain/sync/sync_mgr.go index cab9b7f..ce9f51e 100644 --- a/blockchain/sync/sync_mgr.go +++ b/blockchain/sync/sync_mgr.go @@ -183,6 +183,7 @@ func (sm *syncManager) doInitialMempoolSync() error { txsToRetrieve = txsToRetrieve[policy.MaxTransactionCountForRetrieving:] } else { txHashes = txsToRetrieve + txsToRetrieve = nil } getMempoolTxArg := wire.GetMempoolTxsArg{ From 65afa056bb923de928eaf8a4870d7118ab04ff32 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 30 Jul 2021 02:04:27 +0300 Subject: [PATCH 57/59] FINALLY fix consensus desync issue!!! WOOHOOOOOOOO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --- consensus/consensus.go | 224 ++++++++++++------------------ consensus/consensus_round_pool.go | 129 +++++++++-------- consensus/consensus_validator.go | 10 +- consensus/msg_log.go | 16 +-- consensus/utils.go | 14 +- node/node.go | 3 - node/node_dep_providers.go | 10 +- 7 files changed, 180 insertions(+), 226 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index b099469..e47c808 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -14,8 +14,6 @@ import ( "github.com/fxamacker/cbor/v2" - "github.com/Secured-Finance/dione/cache" - "github.com/asaskevich/EventBus" "github.com/Secured-Finance/dione/blockchain" @@ -42,13 +40,11 @@ var ( type PBFTConsensusManager struct { bus EventBus.Bus psb *pubsub.PubSubRouter - minApprovals int // FIXME privKey crypto.PrivKey - msgLog *ConsensusMessageLog validator *ConsensusValidator ethereumClient *ethclient.EthereumClient miner *blockchain.Miner - consensusRoundPool *ConsensusRoundPool + consensusRoundPool *ConsensusStatePool mempool *pool.Mempool blockchain *blockchain.BlockChain address peer.ID @@ -58,12 +54,11 @@ type PBFTConsensusManager struct { func NewPBFTConsensusManager( bus EventBus.Bus, psb *pubsub.PubSubRouter, - minApprovals int, privKey crypto.PrivKey, ethereumClient *ethclient.EthereumClient, miner *blockchain.Miner, bc *blockchain.BlockChain, - bp *ConsensusRoundPool, + bp *ConsensusStatePool, db *drand2.DrandBeacon, mempool *pool.Mempool, address peer.ID, @@ -72,8 +67,6 @@ func NewPBFTConsensusManager( psb: psb, miner: miner, validator: NewConsensusValidator(miner, bc, db), - msgLog: NewConsensusMessageLog(), - minApprovals: minApprovals, privKey: privKey, ethereumClient: ethereumClient, bus: bus, @@ -84,26 +77,85 @@ func NewPBFTConsensusManager( stateChangeChannels: map[string]map[State][]chan bool{}, } - return pcm -} - -func (pcm *PBFTConsensusManager) Run() { pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare) pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare) pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit) + pcm.bus.SubscribeAsync("beacon:newEntry", func(entry types2.BeaconEntry) { pcm.onNewBeaconEntry(entry) }, true) + + pcm.bus.SubscribeAsync("consensus:newState", func(block *types3.Block, newStateNumber int) { + newState := State(newStateNumber) // hacky, because reflection panics if we pass int to a handler which has type-alias for int + + consensusMessageType := types.ConsensusMessageTypeUnknown + + switch newState { + case StateStatusPrePrepared: + { + logrus.WithField("blockHash", fmt.Sprintf("%x", block.Header.Hash)).Debugf("Entered into PREPREPARED state") + if *block.Header.Proposer == pcm.address { + return + } + consensusMessageType = types.ConsensusMessageTypePrepare + break + } + case StateStatusPrepared: + { + consensusMessageType = types.ConsensusMessageTypeCommit + logrus.WithField("blockHash", fmt.Sprintf("%x", block.Header.Hash)).Debugf("Entered into PREPARED state") + break + } + case StateStatusCommited: + { + logrus.WithField("blockHash", fmt.Sprintf("%x", block.Header.Hash)).Debugf("Entered into COMMITTED state") + break + } + } + + if consensusMessageType == types.ConsensusMessageTypeUnknown { + return + } + + message, err := NewMessage(&types.ConsensusMessage{ + Type: consensusMessageType, + Blockhash: block.Header.Hash, + }, pcm.privKey) + if err != nil { + logrus.Errorf("Failed to create consensus message: %v", err) + return + } + if err = pcm.psb.BroadcastToServiceTopic(message); err != nil { + logrus.Errorf("Failed to send consensus message: %s", err.Error()) + return + } + }, true) + + return pcm } func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error { - prePrepareMsg, err := NewMessage(types.ConsensusMessage{Block: blk}, types.ConsensusMessageTypePrePrepare, pcm.privKey) + 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 } + time.Sleep(1 * time.Second) // wait until all nodes will commit previous blocks - pcm.psb.BroadcastToServiceTopic(prePrepareMsg) - pcm.consensusRoundPool.AddConsensusInfo(blk) + + 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 } @@ -120,48 +172,28 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) return } - cmsg := types.ConsensusMessage{ + cmsg := &types.ConsensusMessage{ Type: types.ConsensusMessageTypePrePrepare, From: message.From, Block: prePrepare.Block, Blockhash: prePrepare.Block.Header.Hash, } - if pcm.msgLog.Exists(cmsg) { - logrus.Tracef("received existing pre_prepare msg for block %x", cmsg.Block.Header.Hash) - return - } if !pcm.validator.Valid(cmsg) { - logrus.Warnf("received invalid pre_prepare msg for block %x", cmsg.Block.Header.Hash) + 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 } - pcm.msgLog.AddMessage(cmsg) logrus.WithFields(logrus.Fields{ "blockHash": fmt.Sprintf("%x", cmsg.Block.Header.Hash), "from": message.From.String(), }).Debug("Received PREPREPARE message") - pcm.consensusRoundPool.AddConsensusInfo(cmsg.Block) - - encodedHash := hex.EncodeToString(cmsg.Blockhash) - if m, ok := pcm.stateChangeChannels[encodedHash]; ok { - if channels, ok := m[StateStatusPrePrepared]; ok { - for _, v := range channels { - v <- true - close(v) - delete(pcm.stateChangeChannels, encodedHash) - } - } - } - - prepareMsg, err := NewMessage(cmsg, types.ConsensusMessageTypePrepare, pcm.privKey) - if err != nil { - logrus.Errorf("failed to create prepare message: %v", err) - return - } - - logrus.WithField("blockHash", fmt.Sprintf("%x", prePrepare.Block.Header.Hash)).Debugf("Entered into PREPREPARED state") - pcm.psb.BroadcastToServiceTopic(prepareMsg) } func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { @@ -172,73 +204,28 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) { return } - cmsg := types.ConsensusMessage{ + cmsg := &types.ConsensusMessage{ Type: types.ConsensusMessageTypePrepare, From: message.From, Blockhash: prepare.Blockhash, Signature: prepare.Signature, } - if _, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) { - encodedHash := hex.EncodeToString(cmsg.Blockhash) - logrus.WithField("blockHash", encodedHash).Warn("Received PREPARE before PREPREPARED state, waiting...") - waitingCh := make(chan bool, 1) - if _, ok := pcm.stateChangeChannels[encodedHash]; !ok { - pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{} - } - pcm.stateChangeChannels[encodedHash][StateStatusPrePrepared] = append(pcm.stateChangeChannels[encodedHash][StateStatusPrePrepared], waitingCh) - result := <-waitingCh - if !result { - return - } - } - - if pcm.msgLog.Exists(cmsg) { - logrus.Tracef("received existing prepare msg for block %x", cmsg.Blockhash) - return - } - if !pcm.validator.Valid(cmsg) { logrus.Warnf("received invalid prepare msg for block %x", cmsg.Blockhash) return } - pcm.msgLog.AddMessage(cmsg) + 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") - - if len(pcm.msgLog.Get(types.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= pcm.minApprovals-1 { - ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash) - if err != nil { - logrus.Fatalf("This should never happen: %s", err.Error()) - } - if ci.State >= StateStatusPrepared { - return - } - - commitMsg, err := NewMessage(cmsg, types.ConsensusMessageTypeCommit, pcm.privKey) - if err != nil { - logrus.Errorf("failed to create commit message: %v", err) - return - } - pcm.psb.BroadcastToServiceTopic(commitMsg) - logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into PREPARED state") - pcm.consensusRoundPool.UpdateConsensusState(cmsg.Blockhash, StateStatusPrepared) - - // pull watchers - encodedHash := hex.EncodeToString(cmsg.Blockhash) - if m, ok := pcm.stateChangeChannels[encodedHash]; ok { - if channels, ok := m[StateStatusPrepared]; ok { - for _, v := range channels { - v <- true - close(v) - delete(pcm.stateChangeChannels, encodedHash) - } - } - } - } } func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { @@ -249,61 +236,28 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) { return } - cmsg := types.ConsensusMessage{ + cmsg := &types.ConsensusMessage{ Type: types.ConsensusMessageTypeCommit, From: message.From, Blockhash: commit.Blockhash, Signature: commit.Signature, } - ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash) - - encodedHash := hex.EncodeToString(cmsg.Blockhash) - - if errors.Is(err, cache.ErrNotFound) || ci.State < StateStatusPrepared { - logrus.WithFields(logrus.Fields{ - "blockHash": encodedHash, - "from": cmsg.From, - }).Warnf("Received COMMIT message before PREPARED state, waiting...") - waitingCh := make(chan bool, 1) - if _, ok := pcm.stateChangeChannels[encodedHash]; !ok { - pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{} - } - pcm.stateChangeChannels[encodedHash][StateStatusPrepared] = append(pcm.stateChangeChannels[encodedHash][StateStatusPrepared], waitingCh) - result := <-waitingCh - if !result { - return - } - } - - if pcm.msgLog.Exists(cmsg) { - logrus.Tracef("received existing commit msg for block %x", cmsg.Blockhash) - return - } if !pcm.validator.Valid(cmsg) { logrus.Warnf("received invalid commit msg for block %x", cmsg.Blockhash) return } - pcm.msgLog.AddMessage(cmsg) + 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") - - if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals { - ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash) - if err != nil { - logrus.Fatalf("This should never happen: %s", err.Error()) - } - if ci.State == StateStatusCommited { - return - } - - logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into COMMIT state") - pcm.consensusRoundPool.UpdateConsensusState(cmsg.Blockhash, StateStatusCommited) - } } func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) { diff --git a/consensus/consensus_round_pool.go b/consensus/consensus_round_pool.go index 902e066..54e172c 100644 --- a/consensus/consensus_round_pool.go +++ b/consensus/consensus_round_pool.go @@ -1,10 +1,11 @@ package consensus import ( - "bytes" "encoding/hex" "fmt" - "time" + "sync" + + types2 "github.com/Secured-Finance/dione/consensus/types" "github.com/Secured-Finance/dione/blockchain/pool" @@ -13,7 +14,6 @@ import ( "github.com/sirupsen/logrus" "github.com/Secured-Finance/dione/blockchain/types" - "github.com/Secured-Finance/dione/cache" ) type State uint8 @@ -26,94 +26,101 @@ const ( StateStatusCommited ) -// ConsensusRoundPool is pool for blocks that isn't not validated or committed yet -type ConsensusRoundPool struct { - mempool *pool.Mempool - consensusInfoStorage cache.Cache - bus EventBus.Bus +// 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) (*ConsensusRoundPool, error) { - bp := &ConsensusRoundPool{ - consensusInfoStorage: cache.NewInMemoryCache(), - mempool: mp, - bus: bus, +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 { - Block *types.Block - State State + Blockhash []byte + Block *types.Block + State State + MessageLog *ConsensusMessageLog } -func (crp *ConsensusRoundPool) AddConsensusInfo(block *types.Block) error { - encodedHash := hex.EncodeToString(block.Header.Hash) - - if crp.consensusInfoStorage.Exists(encodedHash) { - return nil +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 } - err := crp.consensusInfoStorage.StoreWithTTL(encodedHash, &ConsensusInfo{ - Block: block, - State: StateStatusPrePrepared, - }, 10*time.Minute) - if err != nil { - return err + added := consensusInfo.MessageLog.AddMessage(cmsg) + if !added { + return fmt.Errorf("consensus message already exists in message log") } - logrus.WithField("hash", fmt.Sprintf("%x", block.Header.Hash)).Debug("New block discovered") - crp.bus.Publish("blockpool:knownBlockAdded", block) + + crp.maybeUpdateConsensusState(consensusInfo, cmsg) + return nil } -func (crp *ConsensusRoundPool) UpdateConsensusState(blockhash []byte, newState State) error { - encodedHash := hex.EncodeToString(blockhash) - - var consensusInfo ConsensusInfo - err := crp.consensusInfoStorage.Get(encodedHash, &consensusInfo) - if err != nil { - return err +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 newState < consensusInfo.State { - return fmt.Errorf("attempt to set incorrect state") + 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) } - consensusInfo.State = newState - crp.bus.Publish("blockpool:newConsensusState", blockhash, newState) - return crp.consensusInfoStorage.StoreWithTTL(encodedHash, &consensusInfo, 10*time.Minute) -} -func (crp *ConsensusRoundPool) GetConsensusInfo(blockhash []byte) (*ConsensusInfo, error) { - var consensusInfo ConsensusInfo - err := crp.consensusInfoStorage.Get(hex.EncodeToString(blockhash), &consensusInfo) - return &consensusInfo, err + 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 *ConsensusRoundPool) Prune() { - for k := range crp.consensusInfoStorage.Items() { - crp.consensusInfoStorage.Delete(k) +func (crp *ConsensusStatePool) Prune() { + for k := range crp.consensusInfoMap { + delete(crp.consensusInfoMap, k) } crp.bus.Publish("blockpool:pruned") } -func (crp *ConsensusRoundPool) GetAllBlocksWithCommit() []*ConsensusInfo { +func (crp *ConsensusStatePool) GetAllBlocksWithCommit() []*ConsensusInfo { + crp.mapMutex.Lock() + defer crp.mapMutex.Unlock() var consensusInfos []*ConsensusInfo - for _, v := range crp.consensusInfoStorage.Items() { - ci := v.(*ConsensusInfo) - if ci.State == StateStatusCommited { - consensusInfos = append(consensusInfos, ci) + 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 -} +//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 7bf49cb..24e84fc 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -12,7 +12,7 @@ import ( ) type ConsensusValidator struct { - validationFuncMap map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool + validationFuncMap map[types2.ConsensusMessageType]func(msg *types2.ConsensusMessage) bool miner *blockchain.Miner beacon *drand2.DrandBeacon blockchain *blockchain.BlockChain @@ -25,8 +25,8 @@ func NewConsensusValidator(miner *blockchain.Miner, bc *blockchain.BlockChain, d beacon: db, } - cv.validationFuncMap = map[types2.ConsensusMessageType]func(msg types2.ConsensusMessage) bool{ - types2.ConsensusMessageTypePrePrepare: func(msg types2.ConsensusMessage) bool { + 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), @@ -43,11 +43,11 @@ func NewConsensusValidator(miner *blockchain.Miner, bc *blockchain.BlockChain, d return cv } -func (cv *ConsensusValidator) Valid(msg types2.ConsensusMessage) bool { +func (cv *ConsensusValidator) Valid(msg *types2.ConsensusMessage) bool { return cv.validationFuncMap[msg.Type](msg) } -func checkSignatureForBlockhash(msg types2.ConsensusMessage) bool { +func checkSignatureForBlockhash(msg *types2.ConsensusMessage) bool { pubKey, err := msg.From.ExtractPublicKey() if err != nil { // TODO logging diff --git a/consensus/msg_log.go b/consensus/msg_log.go index 77a57aa..3276a0f 100644 --- a/consensus/msg_log.go +++ b/consensus/msg_log.go @@ -8,24 +8,22 @@ import ( ) type ConsensusMessageLog struct { - messages mapset.Set - maxLogSize int + messages mapset.Set } func NewConsensusMessageLog() *ConsensusMessageLog { msgLog := &ConsensusMessageLog{ - messages: mapset.NewSet(), - maxLogSize: 0, // TODO + messages: mapset.NewSet(), } return msgLog } -func (ml *ConsensusMessageLog) AddMessage(msg types2.ConsensusMessage) { - ml.messages.Add(msg) +func (ml *ConsensusMessageLog) AddMessage(msg *types2.ConsensusMessage) bool { + return ml.messages.Add(msg) } -func (ml *ConsensusMessageLog) Exists(msg types2.ConsensusMessage) bool { +func (ml *ConsensusMessageLog) Exists(msg *types2.ConsensusMessage) bool { return ml.messages.Contains(msg) } @@ -33,7 +31,7 @@ func (ml *ConsensusMessageLog) Get(typ types2.ConsensusMessageType, blockhash [] var result []*types2.ConsensusMessage for v := range ml.messages.Iter() { - msg := v.(types2.ConsensusMessage) + msg := v.(*types2.ConsensusMessage) if msg.Block != nil { } @@ -45,7 +43,7 @@ func (ml *ConsensusMessageLog) Get(typ types2.ConsensusMessageType, blockhash [] msgBlockHash = msg.Blockhash } if bytes.Compare(msgBlockHash, blockhash) == 0 { - result = append(result, &msg) + result = append(result, msg) } } } diff --git a/consensus/utils.go b/consensus/utils.go index 37e7e85..88a1ded 100644 --- a/consensus/utils.go +++ b/consensus/utils.go @@ -12,9 +12,9 @@ import ( "github.com/libp2p/go-libp2p-core/crypto" ) -func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, privKey crypto.PrivKey) (*pubsub.PubSubMessage, error) { +func NewMessage(cmsg *types2.ConsensusMessage, privKey crypto.PrivKey) (*pubsub.PubSubMessage, error) { var message pubsub.PubSubMessage - switch typ { + switch cmsg.Type { case types2.ConsensusMessageTypePrePrepare: { message.Type = pubsub.PrePrepareMessageType @@ -36,7 +36,7 @@ func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, p return nil, fmt.Errorf("failed to create signature: %v", err) } pm := types2.PrepareMessage{ - Blockhash: cmsg.Block.Header.Hash, + Blockhash: cmsg.Blockhash, Signature: signature, } data, err := cbor.Marshal(pm) @@ -49,14 +49,14 @@ func NewMessage(cmsg types2.ConsensusMessage, typ types2.ConsensusMessageType, p case types2.ConsensusMessageTypeCommit: { message.Type = pubsub.CommitMessageType - pm := types2.CommitMessage{ - Blockhash: cmsg.Blockhash, - } signature, err := privKey.Sign(cmsg.Blockhash) if err != nil { return nil, fmt.Errorf("failed to create signature: %v", err) } - pm.Signature = signature + 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()) diff --git a/node/node.go b/node/node.go index ec72329..8526b2a 100644 --- a/node/node.go +++ b/node/node.go @@ -77,9 +77,6 @@ func runNode( // Run blockchain sync manager syncManager.Run() - // Run consensus manager - consensusManager.Run() - // Run dispute manager disputeManager.Run(context.TODO()) diff --git a/node/node_dep_providers.go b/node/node_dep_providers.go index 25616be..73ce43c 100644 --- a/node/node_dep_providers.go +++ b/node/node_dep_providers.go @@ -125,21 +125,19 @@ func providePubsubRouter(lhost host.Host, config *config.Config) *pubsub.PubSubR func provideConsensusManager( h host.Host, - cfg *config.Config, bus EventBus.Bus, psb *pubsub.PubSubRouter, miner *blockchain.Miner, bc *blockchain.BlockChain, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, - bp *consensus.ConsensusRoundPool, + bp *consensus.ConsensusStatePool, db *drand2.DrandBeacon, mp *pool.Mempool, ) *consensus.PBFTConsensusManager { c := consensus.NewPBFTConsensusManager( bus, psb, - cfg.ConsensusMinApprovals, privateKey, ethClient, miner, @@ -265,12 +263,12 @@ func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *Network return ns } -func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) *consensus.ConsensusRoundPool { - bp, err := consensus.NewConsensusRoundPool(mp, bus) +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("Blockpool has been successfully initialized!") + logrus.Info("Consensus state pool has been successfully initialized!") return bp } From 7e80654c875a4ab3ffa1f4a07d7f049a3276586b Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Mon, 2 Aug 2021 20:28:44 +0300 Subject: [PATCH 58/59] Compare by block timestamp if stake is equal in block selection mechanism --- consensus/consensus.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index e47c808..d9879b4 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -384,12 +384,11 @@ func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { if iStake.Cmp(jStake) == -1 { return false - } - if iStake.Cmp(jStake) == 1 { + } else if iStake.Cmp(jStake) == 1 { return true + } else { + return blocks[i].Block.Header.Timestamp > blocks[i].Block.Header.Timestamp } - - return blocks[i].Block.Header.ElectionProof.WinCount > blocks[i].Block.Header.ElectionProof.WinCount }) selectedBlock = blocks[0].Block From 46716ed52d1b89ff01c99912dcfc652c6826b3f0 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Mon, 2 Aug 2021 20:40:25 +0300 Subject: [PATCH 59/59] fix small typo --- consensus/consensus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index d9879b4..1490604 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -387,7 +387,7 @@ func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) { } else if iStake.Cmp(jStake) == 1 { return true } else { - return blocks[i].Block.Header.Timestamp > blocks[i].Block.Header.Timestamp + return blocks[i].Block.Header.Timestamp > blocks[j].Block.Header.Timestamp } })