From 2526f65825cce175aeced03b23fcb69aec2818cc Mon Sep 17 00:00:00 2001 From: bahadylbekov <33404905+bahadylbekov@users.noreply.github.com> Date: Sun, 15 Nov 2020 08:59:46 +0300 Subject: [PATCH] add: fixed ed25519 cryptography, added ethereum subscription, solana client integration into node, miner and consensus changes --- .gitignore | 2 ++ config/config.go | 2 ++ consensus/consensus.go | 14 +++++------ consensus/miner.go | 42 ++++++++++++++++++++++++------- ethclient/ethereum.go | 28 ++++++++++++++++++--- node/ethereum.go | 49 +++++++++++++++++++++++++++++++++++++ node/node.go | 9 +++++-- sigs/ed25519/ed25519.go | 4 +-- solana/client.go | 2 +- solana/types/transaction.go | 45 ++++++++++++++++++++++++++++++++++ types/signature.go | 11 +++++++++ types/task.go | 25 +++++++++++++++++++ wallet/wallet.go | 3 +++ 13 files changed, 211 insertions(+), 25 deletions(-) create mode 100644 node/ethereum.go create mode 100644 solana/types/transaction.go diff --git a/.gitignore b/.gitignore index 3edbbbe..2c69fbb 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ eth-contracts/node_modules /dione /.dione-config.toml +/.dione-config-2.toml +/.dione-config-3.toml eth-contracts/build \ No newline at end of file diff --git a/config/config.go b/config/config.go index d2792cc..68138da 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ 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"` @@ -23,6 +24,7 @@ type EthereumConfig struct { PrivateKey string `mapstructure:"private_key"` OracleEmitterContractAddress string `mapstructure:"oracle_emitter_contract_address"` AggregatorContractAddress string `mapstructure:"aggregator_contract_address"` + DioneStakingContractAddress string `mapstructure:"dione_staking_address"` } type FilecoinConfig struct { diff --git a/consensus/consensus.go b/consensus/consensus.go index 219eaca..07e15e2 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -11,9 +11,9 @@ import ( type ConsensusState int const ( - consensusPrePrepared ConsensusState = 0x0 - consensusPrepared ConsensusState = 0x1 - consensusCommitted ConsensusState = 0x2 + ConsensusPrePrepared ConsensusState = 0x0 + ConsensusPrepared ConsensusState = 0x1 + ConsensusCommitted ConsensusState = 0x2 testValidData = "test" ) @@ -60,7 +60,7 @@ func (pcm *PBFTConsensusManager) NewTestConsensus(data string, consensusID strin msg.Payload["data"] = data pcm.psb.BroadcastToServiceTopic(&msg) - cData.State = consensusPrePrepared + cData.State = ConsensusPrePrepared logrus.Debug("started new consensus: " + consensusID) } @@ -99,7 +99,7 @@ func (pcm *PBFTConsensusManager) handlePreparedMessage(message *models.Message) logrus.Warn("Unable to send COMMIT message: " + err.Error()) return } - data.State = consensusPrepared + data.State = ConsensusPrepared } } @@ -115,7 +115,7 @@ func (pcm *PBFTConsensusManager) handleCommitMessage(message *models.Message) { data.mutex.Lock() defer data.mutex.Unlock() - if data.State == consensusCommitted { + if data.State == ConsensusCommitted { logrus.Debug("consensus already finished, dropping COMMIT message") return } @@ -125,7 +125,7 @@ func (pcm *PBFTConsensusManager) handleCommitMessage(message *models.Message) { data.commitCount++ if data.commitCount > 2*pcm.maxFaultNodes+1 { - data.State = consensusCommitted + data.State = ConsensusCommitted data.result = message.Payload["data"].(string) logrus.Debug("consensus successfully finished with result: " + data.result) data.onConsensusFinishCallback(data.result) diff --git a/consensus/miner.go b/consensus/miner.go index 612d1bc..606ac1c 100644 --- a/consensus/miner.go +++ b/consensus/miner.go @@ -2,9 +2,13 @@ package consensus import ( "context" + "encoding/json" "sync" "github.com/Secured-Finance/dione/beacon" + "github.com/Secured-Finance/dione/contracts/oracleEmitter" + "github.com/Secured-Finance/dione/solana" + solTypes "github.com/Secured-Finance/dione/solana/types" "github.com/libp2p/go-libp2p-core/peer" @@ -23,6 +27,7 @@ type Miner struct { mutex sync.Mutex beacon beacon.BeaconNetworks ethClient *ethclient.EthereumClient + solanaClient *solana.SolanaClient minerStake types.BigInt networkStake types.BigInt } @@ -33,13 +38,15 @@ func NewMiner( api WalletAPI, beacon beacon.BeaconNetworks, ethClient *ethclient.EthereumClient, + solanaClient *solana.SolanaClient, ) *Miner { return &Miner{ - address: address, - ethAddress: ethAddress, - api: api, - beacon: beacon, - ethClient: ethClient, + address: address, + ethAddress: ethAddress, + api: api, + beacon: beacon, + ethClient: ethClient, + solanaClient: solanaClient, } } @@ -68,7 +75,7 @@ func (m *Miner) UpdateCurrentStakeInfo() error { return nil } -func (m *Miner) MineTask(ctx context.Context, payload []byte) (*types.DioneTask, error) { +func (m *Miner) MineTask(ctx context.Context, event *oracleEmitter.OracleEmitterNewOracleRequest, sign SignFunc) (*types.DioneTask, error) { bvals, err := beacon.BeaconEntriesForTask(ctx, m.beacon) if err != nil { return nil, xerrors.Errorf("failed to get beacon entries: %w", err) @@ -94,14 +101,31 @@ func (m *Miner) MineTask(ctx context.Context, payload []byte) (*types.DioneTask, if winner == nil { return nil, nil } + + res, err := m.solanaClient.GetTransaction(event.RequestParams) + if err != nil { + return nil, xerrors.Errorf("Couldn't get solana request: %w", err) + } + response := res.Body() + var txRes solTypes.TxResponse + if err = json.Unmarshal(response, &txRes); err != nil { + return nil, xerrors.Errorf("Couldn't unmarshal solana response: %w", err) + } + blockHash := txRes.Result.Transaction.Message.RecentBlockhash + signature, err := sign(ctx, m.address, response) + if err != nil { + return nil, xerrors.Errorf("Couldn't sign solana response: %w", err) + } + return &types.DioneTask{ Miner: m.address, Ticket: ticket, ElectionProof: winner, BeaconEntries: bvals, - Payload: payload, - // TODO: signature - DrandRound: types.DrandRound(rbase.Round), + Payload: response, + BlockHash: blockHash, + Signature: signature, + DrandRound: types.DrandRound(rbase.Round), }, nil } diff --git a/ethclient/ethereum.go b/ethclient/ethereum.go index 682a71d..3ce58b8 100644 --- a/ethclient/ethereum.go +++ b/ethclient/ethereum.go @@ -5,8 +5,9 @@ import ( "math/big" "github.com/Secured-Finance/dione/contracts/aggregator" + "github.com/Secured-Finance/dione/contracts/dioneStaking" stakingContract "github.com/Secured-Finance/dione/contracts/dioneStaking" - oracleEmitter "github.com/Secured-Finance/dione/contracts/oracleemitter" + oracleEmitter "github.com/Secured-Finance/dione/contracts/oracleEmitter" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -44,7 +45,7 @@ func NewEthereumClient() *EthereumClient { return ethereumClient } -func (c *EthereumClient) Initialize(ctx context.Context, url, privateKey, oracleEmitterContractAddress, aggregatorContractAddress string) error { +func (c *EthereumClient) Initialize(ctx context.Context, url, privateKey, oracleEmitterContractAddress, aggregatorContractAddress, dioneStakingAddress string) error { client, err := ethclient.Dial(url) if err != nil { return err @@ -66,6 +67,10 @@ func (c *EthereumClient) Initialize(ctx context.Context, url, privateKey, oracle if err != nil { return err } + stakingContract, err := dioneStaking.NewDioneStaking(common.HexToAddress(dioneStakingAddress), client) + if err != nil { + return err + } c.oracleEmitter = &oracleEmitter.OracleEmitterSession{ Contract: emitter, CallOpts: bind.CallOpts{ @@ -96,6 +101,21 @@ func (c *EthereumClient) Initialize(ctx context.Context, url, privateKey, oracle Context: context.Background(), }, } + c.dioneStaking = &dioneStaking.DioneStakingSession{ + Contract: stakingContract, + CallOpts: bind.CallOpts{ + Pending: true, + From: authTransactor.From, + Context: context.Background(), + }, + TransactOpts: bind.TransactOpts{ + From: authTransactor.From, + Signer: authTransactor.Signer, + GasLimit: 0, // 0 automatically estimates gas limit + GasPrice: nil, // nil automatically suggests gas price + Context: context.Background(), + }, + } return nil } @@ -103,12 +123,12 @@ func (c *EthereumClient) GetEthAddress() *common.Address { return c.ethAddress } -func (c *EthereumClient) SubscribeOnOracleEvents() (chan *oracleEmitter.OracleEmitterNewOracleRequest, event.Subscription, error) { +func (c *EthereumClient) SubscribeOnOracleEvents(ctx context.Context) (chan *oracleEmitter.OracleEmitterNewOracleRequest, event.Subscription, error) { resChan := make(chan *oracleEmitter.OracleEmitterNewOracleRequest) requestsFilter := c.oracleEmitter.Contract.OracleEmitterFilterer subscription, err := requestsFilter.WatchNewOracleRequest(&bind.WatchOpts{ Start: nil, //last block - Context: nil, + Context: ctx, }, resChan) if err != nil { return nil, nil, err diff --git a/node/ethereum.go b/node/ethereum.go new file mode 100644 index 0000000..819f09b --- /dev/null +++ b/node/ethereum.go @@ -0,0 +1,49 @@ +package node + +import ( + "context" + + "github.com/Secured-Finance/dione/consensus" + "github.com/sirupsen/logrus" +) + +func (n *Node) subscribeOnEthContracts(ctx context.Context) { + eventChan, subscription, err := n.Ethereum.SubscribeOnOracleEvents(ctx) + if err != nil { + logrus.Fatal("Can't subscribe on ethereum contracts, exiting... ", err) + } + + go func() { + EventLoop: + for { + select { + case event := <-eventChan: + { + task, err := n.Miner.MineTask(ctx, event, n.Wallet.WalletSign) + if err != nil { + logrus.Fatal("Error with mining algorithm, exiting... ", err) + } + logrus.Info("BlockHash for Solana transaction: ", task.BlockHash) + logrus.Info("Started new consensus round with ID: ", task.BlockHash) + n.ConsensusManager.NewTestConsensus(string(task.BlockHash), task.BlockHash, func(finalData string) { + if finalData != string(task.BlockHash) { + logrus.Warn("Expected final data to be %s, not %s", finalData) + } + }) + + if n.ConsensusManager.Consensuses[task.BlockHash].State == consensus.ConsensusCommitted { + logrus.Info("Consensus ID: ", task.BlockHash, " was successfull") + logrus.Info("Submitting on-chain result: ", task.BlockHash, "for consensus ID: ", task.BlockHash) + if err := n.Ethereum.SubmitRequestAnswer(event.RequestID, task.BlockHash, event.CallbackAddress, event.CallbackMethodID); err != nil { + logrus.Warn("Can't submit request to ethereum chain: ", err) + } + } + } + case <-ctx.Done(): + break EventLoop + case <-subscription.Err(): + logrus.Fatal("Error with ethereum subscription, exiting... ", err) + } + } + }() +} diff --git a/node/node.go b/node/node.go index 2d08b6a..7f30ad4 100644 --- a/node/node.go +++ b/node/node.go @@ -70,6 +70,7 @@ func (n *Node) setupNode(ctx context.Context, prvKey crypto.PrivKey, pexDiscover if err != nil { logrus.Fatal(err) } + n.setupSolanaClient() n.setupPubsub() n.setupConsensusManager(n.Config.ConsensusMaxFaultNodes) err = n.setupBeacon() @@ -84,10 +85,11 @@ func (n *Node) setupNode(ctx context.Context, prvKey crypto.PrivKey, pexDiscover if err != nil { logrus.Fatal(err) } + n.subscribeOnEthContracts(ctx) } func (n *Node) setupMiner() error { - n.Miner = consensus.NewMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Wallet, n.Beacon, n.Ethereum) + n.Miner = consensus.NewMiner(n.Host.ID(), *n.Ethereum.GetEthAddress(), n.Wallet, n.Beacon, n.Ethereum, n.Solana) return nil } @@ -129,6 +131,7 @@ func (n *Node) setupEthereumClient() error { n.Config.Ethereum.PrivateKey, n.Config.Ethereum.OracleEmitterContractAddress, n.Config.Ethereum.AggregatorContractAddress, + n.Config.Ethereum.DioneStakingContractAddress, ) } @@ -177,7 +180,9 @@ func (n *Node) setupLibp2pHost(ctx context.Context, privateKey crypto.PrivKey, p } bootstrapMaddrs = append(bootstrapMaddrs, maddr) } - + if n.Config.IsBootstrap { + bootstrapMaddrs = nil + } discovery, err := pex.NewPEXDiscovery(host, bootstrapMaddrs, pexDiscoveryUpdateTime) if err != nil { logrus.Fatal("Can't set up PEX discovery protocol, exiting... ", err) diff --git a/sigs/ed25519/ed25519.go b/sigs/ed25519/ed25519.go index 0c12f3e..d4eeb84 100644 --- a/sigs/ed25519/ed25519.go +++ b/sigs/ed25519/ed25519.go @@ -22,13 +22,13 @@ func (ed25519Signer) GenPrivate() ([]byte, error) { } func (ed25519Signer) ToPublic(priv []byte) ([]byte, error) { - privKey := ed25519.NewKeyFromSeed(priv) + var privKey ed25519.PrivateKey = priv pubKey := privKey.Public().(ed25519.PublicKey) return pubKey, nil } func (ed25519Signer) Sign(p []byte, msg []byte) ([]byte, error) { - privKey := ed25519.NewKeyFromSeed(p) + var privKey ed25519.PrivateKey = p return ed25519.Sign(privKey, msg), nil } diff --git a/solana/client.go b/solana/client.go index 6bb9c87..0c44022 100644 --- a/solana/client.go +++ b/solana/client.go @@ -44,7 +44,7 @@ func (c *SolanaClient) GetTransaction(txHash string) (*fasthttp.Response, error) req.Header.SetMethod("POST") req.Header.SetContentType("application/json") requestBody := rpc.NewRequestBody("getConfirmedTransaction") - requestBody.Params = append(requestBody.Params, txHash, "base58") + requestBody.Params = append(requestBody.Params, txHash, "json") body, err := json.Marshal(requestBody) if err != nil { return nil, fmt.Errorf("Failed to marshal request body %v", err) diff --git a/solana/types/transaction.go b/solana/types/transaction.go new file mode 100644 index 0000000..d64c77b --- /dev/null +++ b/solana/types/transaction.go @@ -0,0 +1,45 @@ +package types + +type TxResponse struct { + Jsonrpc string `json:"jsonrpc"` + Result TxResult `json:"result"` + ID int `json:"id"` +} + +type TxStatus struct { + Ok interface{} `json:"Ok"` +} +type TxMeta struct { + Err interface{} `json:"err"` + Fee int `json:"fee"` + InnerInstructions []interface{} `json:"innerInstructions"` + LogMessages []interface{} `json:"logMessages"` + PostBalances []interface{} `json:"postBalances"` + PreBalances []interface{} `json:"preBalances"` + Status TxStatus `json:"status"` +} +type TxHeader struct { + NumReadonlySignedAccounts int `json:"numReadonlySignedAccounts"` + NumReadonlyUnsignedAccounts int `json:"numReadonlyUnsignedAccounts"` + NumRequiredSignatures int `json:"numRequiredSignatures"` +} +type TxInstructions struct { + Accounts []int `json:"accounts"` + Data string `json:"data"` + ProgramIDIndex int `json:"programIdIndex"` +} +type Message struct { + AccountKeys []string `json:"accountKeys"` + Header TxHeader `json:"header"` + Instructions []TxInstructions `json:"instructions"` + RecentBlockhash string `json:"recentBlockhash"` +} +type Transaction struct { + Message Message `json:"message"` + Signatures []string `json:"signatures"` +} +type TxResult struct { + Meta TxMeta `json:"meta"` + Slot int `json:"slot"` + Transaction Transaction `json:"transaction"` +} diff --git a/types/signature.go b/types/signature.go index f40c437..c6ea62a 100644 --- a/types/signature.go +++ b/types/signature.go @@ -17,6 +17,17 @@ const ( SigTypeEd25519 = SigType(iota) ) +func (t SigType) Name() (string, error) { + switch t { + case SigTypeUnknown: + return "unknown", nil + case SigTypeEd25519: + return "ed25519", nil + default: + return "", fmt.Errorf("invalid signature type: %d", t) + } +} + const SignatureMaxLength = 200 type Signature struct { diff --git a/types/task.go b/types/task.go index f419219..53b3a28 100644 --- a/types/task.go +++ b/types/task.go @@ -36,6 +36,31 @@ type DioneTask struct { Signature *Signature DrandRound DrandRound Payload []byte + BlockHash string +} + +func NewDioneTask( + t TaskType, + miner peer.ID, + ticket *Ticket, + electionProof *ElectionProof, + beacon []BeaconEntry, + sig *Signature, + drand DrandRound, + payload []byte, + blockHash string, +) *DioneTask { + return &DioneTask{ + Type: t, + Miner: miner, + Ticket: ticket, + ElectionProof: electionProof, + BeaconEntries: beacon, + Signature: sig, + DrandRound: drand, + Payload: payload, + BlockHash: blockHash, + } } var tasksPerEpoch = NewInt(config.TasksPerEpoch) diff --git a/wallet/wallet.go b/wallet/wallet.go index 7e0d78c..33f6ce7 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -7,6 +7,7 @@ import ( "github.com/libp2p/go-libp2p-core/peer" "github.com/Secured-Finance/dione/sigs" + _ "github.com/Secured-Finance/dione/sigs/ed25519" // enable ed25519 signatures "github.com/Secured-Finance/dione/types" "github.com/filecoin-project/go-address" "github.com/sirupsen/logrus" @@ -107,11 +108,13 @@ func (w *LocalWallet) tryFind(addr peer.ID) (types.KeyInfo, error) { if err != nil { return types.KeyInfo{}, err } + logrus.Info("tAddress: ", tAddress) ki, err = w.keystore.Get(KNamePrefix + tAddress) if err != nil { return types.KeyInfo{}, err } + logrus.Info("ki from tryFind: ", ki) // We found it with the testnet prefix // Add this KeyInfo with the mainnet prefix address string