add: fixed ed25519 cryptography, added ethereum subscription, solana client integration into node, miner and consensus changes

This commit is contained in:
bahadylbekov 2020-11-15 08:59:46 +03:00
parent 54ac2c6c8d
commit 2526f65825
13 changed files with 211 additions and 25 deletions

2
.gitignore vendored
View File

@ -20,4 +20,6 @@
eth-contracts/node_modules eth-contracts/node_modules
/dione /dione
/.dione-config.toml /.dione-config.toml
/.dione-config-2.toml
/.dione-config-3.toml
eth-contracts/build eth-contracts/build

View File

@ -9,6 +9,7 @@ import (
type Config struct { type Config struct {
ListenPort int `mapstructure:"listen_port"` ListenPort int `mapstructure:"listen_port"`
ListenAddr string `mapstructure:"listen_addr"` ListenAddr string `mapstructure:"listen_addr"`
IsBootstrap bool `mapstructure:"is_bootstrap"`
BootstrapNodes []string `mapstructure:"bootstrap_node_multiaddr"` BootstrapNodes []string `mapstructure:"bootstrap_node_multiaddr"`
Rendezvous string `mapstructure:"rendezvous"` Rendezvous string `mapstructure:"rendezvous"`
Ethereum EthereumConfig `mapstructure:"ethereum"` Ethereum EthereumConfig `mapstructure:"ethereum"`
@ -23,6 +24,7 @@ type EthereumConfig struct {
PrivateKey string `mapstructure:"private_key"` PrivateKey string `mapstructure:"private_key"`
OracleEmitterContractAddress string `mapstructure:"oracle_emitter_contract_address"` OracleEmitterContractAddress string `mapstructure:"oracle_emitter_contract_address"`
AggregatorContractAddress string `mapstructure:"aggregator_contract_address"` AggregatorContractAddress string `mapstructure:"aggregator_contract_address"`
DioneStakingContractAddress string `mapstructure:"dione_staking_address"`
} }
type FilecoinConfig struct { type FilecoinConfig struct {

View File

@ -11,9 +11,9 @@ import (
type ConsensusState int type ConsensusState int
const ( const (
consensusPrePrepared ConsensusState = 0x0 ConsensusPrePrepared ConsensusState = 0x0
consensusPrepared ConsensusState = 0x1 ConsensusPrepared ConsensusState = 0x1
consensusCommitted ConsensusState = 0x2 ConsensusCommitted ConsensusState = 0x2
testValidData = "test" testValidData = "test"
) )
@ -60,7 +60,7 @@ func (pcm *PBFTConsensusManager) NewTestConsensus(data string, consensusID strin
msg.Payload["data"] = data msg.Payload["data"] = data
pcm.psb.BroadcastToServiceTopic(&msg) pcm.psb.BroadcastToServiceTopic(&msg)
cData.State = consensusPrePrepared cData.State = ConsensusPrePrepared
logrus.Debug("started new consensus: " + consensusID) 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()) logrus.Warn("Unable to send COMMIT message: " + err.Error())
return return
} }
data.State = consensusPrepared data.State = ConsensusPrepared
} }
} }
@ -115,7 +115,7 @@ func (pcm *PBFTConsensusManager) handleCommitMessage(message *models.Message) {
data.mutex.Lock() data.mutex.Lock()
defer data.mutex.Unlock() defer data.mutex.Unlock()
if data.State == consensusCommitted { if data.State == ConsensusCommitted {
logrus.Debug("consensus already finished, dropping COMMIT message") logrus.Debug("consensus already finished, dropping COMMIT message")
return return
} }
@ -125,7 +125,7 @@ func (pcm *PBFTConsensusManager) handleCommitMessage(message *models.Message) {
data.commitCount++ data.commitCount++
if data.commitCount > 2*pcm.maxFaultNodes+1 { if data.commitCount > 2*pcm.maxFaultNodes+1 {
data.State = consensusCommitted data.State = ConsensusCommitted
data.result = message.Payload["data"].(string) data.result = message.Payload["data"].(string)
logrus.Debug("consensus successfully finished with result: " + data.result) logrus.Debug("consensus successfully finished with result: " + data.result)
data.onConsensusFinishCallback(data.result) data.onConsensusFinishCallback(data.result)

View File

@ -2,9 +2,13 @@ package consensus
import ( import (
"context" "context"
"encoding/json"
"sync" "sync"
"github.com/Secured-Finance/dione/beacon" "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" "github.com/libp2p/go-libp2p-core/peer"
@ -23,6 +27,7 @@ type Miner struct {
mutex sync.Mutex mutex sync.Mutex
beacon beacon.BeaconNetworks beacon beacon.BeaconNetworks
ethClient *ethclient.EthereumClient ethClient *ethclient.EthereumClient
solanaClient *solana.SolanaClient
minerStake types.BigInt minerStake types.BigInt
networkStake types.BigInt networkStake types.BigInt
} }
@ -33,13 +38,15 @@ func NewMiner(
api WalletAPI, api WalletAPI,
beacon beacon.BeaconNetworks, beacon beacon.BeaconNetworks,
ethClient *ethclient.EthereumClient, ethClient *ethclient.EthereumClient,
solanaClient *solana.SolanaClient,
) *Miner { ) *Miner {
return &Miner{ return &Miner{
address: address, address: address,
ethAddress: ethAddress, ethAddress: ethAddress,
api: api, api: api,
beacon: beacon, beacon: beacon,
ethClient: ethClient, ethClient: ethClient,
solanaClient: solanaClient,
} }
} }
@ -68,7 +75,7 @@ func (m *Miner) UpdateCurrentStakeInfo() error {
return nil 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) bvals, err := beacon.BeaconEntriesForTask(ctx, m.beacon)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to get beacon entries: %w", err) 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 { if winner == nil {
return nil, 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{ return &types.DioneTask{
Miner: m.address, Miner: m.address,
Ticket: ticket, Ticket: ticket,
ElectionProof: winner, ElectionProof: winner,
BeaconEntries: bvals, BeaconEntries: bvals,
Payload: payload, Payload: response,
// TODO: signature BlockHash: blockHash,
DrandRound: types.DrandRound(rbase.Round), Signature: signature,
DrandRound: types.DrandRound(rbase.Round),
}, nil }, nil
} }

View File

@ -5,8 +5,9 @@ import (
"math/big" "math/big"
"github.com/Secured-Finance/dione/contracts/aggregator" "github.com/Secured-Finance/dione/contracts/aggregator"
"github.com/Secured-Finance/dione/contracts/dioneStaking"
stakingContract "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/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -44,7 +45,7 @@ func NewEthereumClient() *EthereumClient {
return 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) client, err := ethclient.Dial(url)
if err != nil { if err != nil {
return err return err
@ -66,6 +67,10 @@ func (c *EthereumClient) Initialize(ctx context.Context, url, privateKey, oracle
if err != nil { if err != nil {
return err return err
} }
stakingContract, err := dioneStaking.NewDioneStaking(common.HexToAddress(dioneStakingAddress), client)
if err != nil {
return err
}
c.oracleEmitter = &oracleEmitter.OracleEmitterSession{ c.oracleEmitter = &oracleEmitter.OracleEmitterSession{
Contract: emitter, Contract: emitter,
CallOpts: bind.CallOpts{ CallOpts: bind.CallOpts{
@ -96,6 +101,21 @@ func (c *EthereumClient) Initialize(ctx context.Context, url, privateKey, oracle
Context: context.Background(), 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 return nil
} }
@ -103,12 +123,12 @@ func (c *EthereumClient) GetEthAddress() *common.Address {
return c.ethAddress 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) resChan := make(chan *oracleEmitter.OracleEmitterNewOracleRequest)
requestsFilter := c.oracleEmitter.Contract.OracleEmitterFilterer requestsFilter := c.oracleEmitter.Contract.OracleEmitterFilterer
subscription, err := requestsFilter.WatchNewOracleRequest(&bind.WatchOpts{ subscription, err := requestsFilter.WatchNewOracleRequest(&bind.WatchOpts{
Start: nil, //last block Start: nil, //last block
Context: nil, Context: ctx,
}, resChan) }, resChan)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

49
node/ethereum.go Normal file
View File

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

View File

@ -70,6 +70,7 @@ func (n *Node) setupNode(ctx context.Context, prvKey crypto.PrivKey, pexDiscover
if err != nil { if err != nil {
logrus.Fatal(err) logrus.Fatal(err)
} }
n.setupSolanaClient()
n.setupPubsub() n.setupPubsub()
n.setupConsensusManager(n.Config.ConsensusMaxFaultNodes) n.setupConsensusManager(n.Config.ConsensusMaxFaultNodes)
err = n.setupBeacon() err = n.setupBeacon()
@ -84,10 +85,11 @@ func (n *Node) setupNode(ctx context.Context, prvKey crypto.PrivKey, pexDiscover
if err != nil { if err != nil {
logrus.Fatal(err) logrus.Fatal(err)
} }
n.subscribeOnEthContracts(ctx)
} }
func (n *Node) setupMiner() error { 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 return nil
} }
@ -129,6 +131,7 @@ func (n *Node) setupEthereumClient() error {
n.Config.Ethereum.PrivateKey, n.Config.Ethereum.PrivateKey,
n.Config.Ethereum.OracleEmitterContractAddress, n.Config.Ethereum.OracleEmitterContractAddress,
n.Config.Ethereum.AggregatorContractAddress, 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) bootstrapMaddrs = append(bootstrapMaddrs, maddr)
} }
if n.Config.IsBootstrap {
bootstrapMaddrs = nil
}
discovery, err := pex.NewPEXDiscovery(host, bootstrapMaddrs, pexDiscoveryUpdateTime) discovery, err := pex.NewPEXDiscovery(host, bootstrapMaddrs, pexDiscoveryUpdateTime)
if err != nil { if err != nil {
logrus.Fatal("Can't set up PEX discovery protocol, exiting... ", err) logrus.Fatal("Can't set up PEX discovery protocol, exiting... ", err)

View File

@ -22,13 +22,13 @@ func (ed25519Signer) GenPrivate() ([]byte, error) {
} }
func (ed25519Signer) ToPublic(priv []byte) ([]byte, error) { func (ed25519Signer) ToPublic(priv []byte) ([]byte, error) {
privKey := ed25519.NewKeyFromSeed(priv) var privKey ed25519.PrivateKey = priv
pubKey := privKey.Public().(ed25519.PublicKey) pubKey := privKey.Public().(ed25519.PublicKey)
return pubKey, nil return pubKey, nil
} }
func (ed25519Signer) Sign(p []byte, msg []byte) ([]byte, error) { func (ed25519Signer) Sign(p []byte, msg []byte) ([]byte, error) {
privKey := ed25519.NewKeyFromSeed(p) var privKey ed25519.PrivateKey = p
return ed25519.Sign(privKey, msg), nil return ed25519.Sign(privKey, msg), nil
} }

View File

@ -44,7 +44,7 @@ func (c *SolanaClient) GetTransaction(txHash string) (*fasthttp.Response, error)
req.Header.SetMethod("POST") req.Header.SetMethod("POST")
req.Header.SetContentType("application/json") req.Header.SetContentType("application/json")
requestBody := rpc.NewRequestBody("getConfirmedTransaction") requestBody := rpc.NewRequestBody("getConfirmedTransaction")
requestBody.Params = append(requestBody.Params, txHash, "base58") requestBody.Params = append(requestBody.Params, txHash, "json")
body, err := json.Marshal(requestBody) body, err := json.Marshal(requestBody)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to marshal request body %v", err) return nil, fmt.Errorf("Failed to marshal request body %v", err)

View File

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

View File

@ -17,6 +17,17 @@ const (
SigTypeEd25519 = SigType(iota) 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 const SignatureMaxLength = 200
type Signature struct { type Signature struct {

View File

@ -36,6 +36,31 @@ type DioneTask struct {
Signature *Signature Signature *Signature
DrandRound DrandRound DrandRound DrandRound
Payload []byte 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) var tasksPerEpoch = NewInt(config.TasksPerEpoch)

View File

@ -7,6 +7,7 @@ import (
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/sigs" "github.com/Secured-Finance/dione/sigs"
_ "github.com/Secured-Finance/dione/sigs/ed25519" // enable ed25519 signatures
"github.com/Secured-Finance/dione/types" "github.com/Secured-Finance/dione/types"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -107,11 +108,13 @@ func (w *LocalWallet) tryFind(addr peer.ID) (types.KeyInfo, error) {
if err != nil { if err != nil {
return types.KeyInfo{}, err return types.KeyInfo{}, err
} }
logrus.Info("tAddress: ", tAddress)
ki, err = w.keystore.Get(KNamePrefix + tAddress) ki, err = w.keystore.Get(KNamePrefix + tAddress)
if err != nil { if err != nil {
return types.KeyInfo{}, err return types.KeyInfo{}, err
} }
logrus.Info("ki from tryFind: ", ki)
// We found it with the testnet prefix // We found it with the testnet prefix
// Add this KeyInfo with the mainnet prefix address string // Add this KeyInfo with the mainnet prefix address string