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
/dione
/.dione-config.toml
/.dione-config-2.toml
/.dione-config-3.toml
eth-contracts/build

View File

@ -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 {

View File

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

View File

@ -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,6 +38,7 @@ func NewMiner(
api WalletAPI,
beacon beacon.BeaconNetworks,
ethClient *ethclient.EthereumClient,
solanaClient *solana.SolanaClient,
) *Miner {
return &Miner{
address: address,
@ -40,6 +46,7 @@ func NewMiner(
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,13 +101,30 @@ 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
Payload: response,
BlockHash: blockHash,
Signature: signature,
DrandRound: types.DrandRound(rbase.Round),
}, nil
}

View File

@ -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

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 {
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)

View File

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

View File

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

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)
)
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 {

View File

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

View File

@ -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