Add Type field to DioneTask, rename rpcclient to ethclient, rewrite whole signing system in Dione, add some comments to consensus process (w/ mining system)

This commit is contained in:
ChronosX88 2020-11-12 18:18:30 +04:00
parent 6636a279e7
commit 0b37896af9
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
20 changed files with 426 additions and 773 deletions

View File

@ -15,9 +15,9 @@ type Response struct {
Err error Err error
} }
type Schedule []BeaconPoint type Queue []BeaconPoint
func (bs Schedule) BeaconForEpoch(e types.TaskEpoch) RandomBeacon { func (bs Queue) BeaconForEpoch(e types.TaskEpoch) RandomBeacon {
for i := len(bs) - 1; i >= 0; i-- { for i := len(bs) - 1; i >= 0; i-- {
bp := bs[i] bp := bs[i]
if e >= bp.Start { if e >= bp.Start {
@ -42,10 +42,10 @@ type RandomBeacon interface {
MaxBeaconRoundForEpoch(types.TaskEpoch) uint64 MaxBeaconRoundForEpoch(types.TaskEpoch) uint64
} }
func ValidateTaskValues(bSchedule Schedule, t *types.DioneTask, parentEpoch types.TaskEpoch, prevEntry types.BeaconEntry) error { func ValidateTaskValues(bQueue Queue, t *types.DioneTask, parentEpoch types.TaskEpoch, prevEntry types.BeaconEntry) error {
{ {
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) parentBeacon := bQueue.BeaconForEpoch(parentEpoch)
currBeacon := bSchedule.BeaconForEpoch(t.Epoch) currBeacon := bQueue.BeaconForEpoch(t.Epoch)
if parentBeacon != currBeacon { if parentBeacon != currBeacon {
if len(t.BeaconEntries) != 2 { if len(t.BeaconEntries) != 2 {
return fmt.Errorf("expected two beacon entries at beacon fork, got %d", len(t.BeaconEntries)) return fmt.Errorf("expected two beacon entries at beacon fork, got %d", len(t.BeaconEntries))
@ -60,7 +60,7 @@ func ValidateTaskValues(bSchedule Schedule, t *types.DioneTask, parentEpoch type
} }
// TODO: fork logic // TODO: fork logic
b := bSchedule.BeaconForEpoch(t.Epoch) b := bQueue.BeaconForEpoch(t.Epoch)
maxRound := b.MaxBeaconRoundForEpoch(t.Epoch) maxRound := b.MaxBeaconRoundForEpoch(t.Epoch)
if maxRound == prevEntry.Round { if maxRound == prevEntry.Round {
if len(t.BeaconEntries) != 0 { if len(t.BeaconEntries) != 0 {
@ -88,7 +88,7 @@ func ValidateTaskValues(bSchedule Schedule, t *types.DioneTask, parentEpoch type
return nil return nil
} }
func BeaconEntriesForTask(ctx context.Context, bSchedule Schedule, epoch types.TaskEpoch, parentEpoch types.TaskEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) { func BeaconEntriesForTask(ctx context.Context, bSchedule Queue, epoch types.TaskEpoch, parentEpoch types.TaskEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
{ {
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
currBeacon := bSchedule.BeaconForEpoch(epoch) currBeacon := bSchedule.BeaconForEpoch(epoch)

View File

@ -7,15 +7,15 @@ 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"`
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"`
Filecoin FilecoinConfig `mapstructure:"filecoin"` Filecoin FilecoinConfig `mapstructure:"filecoin"`
PubSub PubSubConfig `mapstructure:"pubSub"` PubSub PubSubConfig `mapstructure:"pubSub"`
Store StoreConfig `mapstructure:"store"` Store StoreConfig `mapstructure:"store"`
ConsensusMaxFaultNodes int `mapstructure:"consensus_max_fault_nodes"` ConsensusMaxFaultNodes int `mapstructure:"consensus_max_fault_nodes"`
} }
type EthereumConfig struct { type EthereumConfig struct {

View File

@ -25,12 +25,12 @@ type PBFTConsensusManager struct {
} }
type ConsensusData struct { type ConsensusData struct {
preparedCount int preparedCount int
commitCount int commitCount int
State ConsensusState State ConsensusState
mutex sync.Mutex mutex sync.Mutex
result string result string
test bool test bool
onConsensusFinishCallback func(finalData string) onConsensusFinishCallback func(finalData string)
} }
@ -50,6 +50,8 @@ func (pcm *PBFTConsensusManager) NewTestConsensus(data string, consensusID strin
cData.onConsensusFinishCallback = onConsensusFinishCallback cData.onConsensusFinishCallback = onConsensusFinishCallback
pcm.Consensuses[consensusID] = cData pcm.Consensuses[consensusID] = cData
// here we will create DioneTask
msg := models.Message{} msg := models.Message{}
msg.Type = "prepared" msg.Type = "prepared"
msg.Payload = make(map[string]interface{}) msg.Payload = make(map[string]interface{})
@ -71,16 +73,19 @@ func (pcm *PBFTConsensusManager) handlePreparedMessage(message *models.Message)
logrus.Debug("received prepared msg") logrus.Debug("received prepared msg")
data := pcm.Consensuses[consensusID] data := pcm.Consensuses[consensusID]
// validate payload data //// validate payload data
if data.test { //if data.test {
rData := message.Payload["data"].(string) // rData := message.Payload["data"].(string)
if rData != testValidData { // if rData != testValidData {
logrus.Error("Incorrect data was received! Ignoring this message, because it was sent from fault node!") // logrus.Error("Incorrect data was received! Ignoring this message, because it was sent from fault node!")
return // return
} // }
} else { //} else {
// TODO // // TODO
} //}
// here we can validate miner which produced this task, is he winner, and so on
// we must to reconstruct transaction here for validating task itself
data.mutex.Lock() data.mutex.Lock()
data.preparedCount++ data.preparedCount++
@ -103,7 +108,7 @@ func (pcm *PBFTConsensusManager) handlePreparedMessage(message *models.Message)
func (pcm *PBFTConsensusManager) handleCommitMessage(message *models.Message) { func (pcm *PBFTConsensusManager) handleCommitMessage(message *models.Message) {
// TODO add check on view of the message // TODO add check on view of the message
// TODO add validation of data to this stage // TODO add validation of data by hash to this stage
consensusID := message.Payload["consensusID"].(string) consensusID := message.Payload["consensusID"].(string)
if _, ok := pcm.Consensuses[consensusID]; !ok { if _, ok := pcm.Consensuses[consensusID]; !ok {
logrus.Warn("Unknown consensus ID: " + consensusID) logrus.Warn("Unknown consensus ID: " + consensusID)

View File

@ -1,43 +1,39 @@
package consensus package consensus
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/types" "github.com/Secured-Finance/dione/types"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/lib/sigs"
"go.opencensus.io/trace"
"golang.org/x/xerrors" "golang.org/x/xerrors"
) )
type SignFunc func(context.Context, address.Address, []byte) (*crypto.Signature, error) type SignFunc func(context.Context, peer.ID, []byte) (*types.Signature, error)
func ComputeVRF(ctx context.Context, sign SignFunc, worker address.Address, sigInput []byte) ([]byte, error) { func ComputeVRF(ctx context.Context, sign SignFunc, worker peer.ID, sigInput []byte) ([]byte, error) {
sig, err := sign(ctx, worker, sigInput) sig, err := sign(ctx, worker, sigInput)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if sig.Type != crypto.SigTypeBLS { if sig.Type != types.SigTypeEd25519 {
return nil, fmt.Errorf("miner worker address was not a BLS key") return nil, fmt.Errorf("miner worker address was not a Ed25519 key")
} }
return sig.Data, nil return sig.Data, nil
} }
func VerifyVRF(ctx context.Context, worker address.Address, vrfBase, vrfproof []byte) error { func VerifyVRF(ctx context.Context, worker peer.ID, vrfBase, vrfproof []byte) error {
_, span := trace.StartSpan(ctx, "VerifyVRF") pKey, err := worker.ExtractPublicKey()
defer span.End() if err != nil {
return xerrors.Errorf("failed to extract public key from worker address: %w", err)
sig := &crypto.Signature{
Type: crypto.SigTypeBLS,
Data: vrfproof,
} }
if err := sigs.Verify(sig, worker, vrfBase); err != nil { valid, err := pKey.Verify(vrfBase, vrfproof)
if err != nil || !valid {
return xerrors.Errorf("vrf was invalid: %w", err) return xerrors.Errorf("vrf was invalid: %w", err)
} }
@ -45,14 +41,14 @@ func VerifyVRF(ctx context.Context, worker address.Address, vrfBase, vrfproof []
} }
func IsRoundWinner(ctx context.Context, round types.TaskEpoch, func IsRoundWinner(ctx context.Context, round types.TaskEpoch,
worker address.Address, brand types.BeaconEntry, mb *MinerBase, a MinerAPI) (*types.ElectionProof, error) { worker peer.ID, brand types.BeaconEntry, mb *MinerBase, a MinerAPI) (*types.ElectionProof, error) {
buf := new(bytes.Buffer) buf, err := worker.MarshalBinary()
if err := worker.MarshalCBOR(buf); err != nil { if err != nil {
return nil, xerrors.Errorf("failed to cbor marshal address: %w", err) return nil, xerrors.Errorf("failed to marshal address: %w", err)
} }
electionRand, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf.Bytes()) electionRand, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to draw randomness: %w", err) return nil, xerrors.Errorf("failed to draw randomness: %w", err)
} }

View File

@ -1,33 +1,33 @@
package consensus package consensus
import ( import (
"bytes"
"context" "context"
"sync" "sync"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/types" "github.com/Secured-Finance/dione/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/crypto"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/xerrors" "golang.org/x/xerrors"
) )
type Miner struct { type Miner struct {
address address.Address address peer.ID
api MinerAPI api MinerAPI
mutex sync.Mutex mutex sync.Mutex
} }
type MinerAPI interface { type MinerAPI interface {
WalletSign(context.Context, address.Address, []byte) (*crypto.Signature, error) WalletSign(context.Context, peer.ID, []byte) (*types.Signature, error)
// TODO: get miner base based on epoch; // TODO: get miner base based on epoch;
} }
type MinerBase struct { type MinerBase struct {
MinerStake types.BigInt MinerStake types.BigInt
NetworkStake types.BigInt NetworkStake types.BigInt
WorkerKey address.Address WorkerKey peer.ID
EthWallet common.Address EthWallet common.Address
PrevBeaconEntry types.BeaconEntry PrevBeaconEntry types.BeaconEntry
BeaconEntries []types.BeaconEntry BeaconEntries []types.BeaconEntry
@ -36,15 +36,15 @@ type MinerBase struct {
type MiningBase struct { type MiningBase struct {
epoch types.TaskEpoch epoch types.TaskEpoch
nullRounds types.TaskEpoch nullRounds types.TaskEpoch // currently not used
} }
func NewMinerBase(minerStake, networkStake types.BigInt, minerWallet address.Address, func NewMinerBase(minerStake, networkStake types.BigInt, minerAddress peer.ID,
minerEthWallet common.Address, prev types.BeaconEntry, entries []types.BeaconEntry) *MinerBase { minerEthWallet common.Address, prev types.BeaconEntry, entries []types.BeaconEntry) *MinerBase {
return &MinerBase{ return &MinerBase{
MinerStake: minerStake, MinerStake: minerStake,
NetworkStake: networkStake, NetworkStake: networkStake,
WorkerKey: minerWallet, WorkerKey: minerAddress,
EthWallet: minerEthWallet, EthWallet: minerEthWallet,
PrevBeaconEntry: prev, PrevBeaconEntry: prev,
BeaconEntries: entries, BeaconEntries: entries,
@ -82,21 +82,21 @@ func (m *Miner) MineTask(ctx context.Context, base *MiningBase, mb *MinerBase) (
Miner: m.address, Miner: m.address,
Ticket: ticket, Ticket: ticket,
ElectionProof: winner, ElectionProof: winner,
BeaconEntries: mb.BeaconEntries, BeaconEntries: mb.BeaconEntries, // TODO decide what we need to do with multiple beacon entries
// TODO: signature // TODO: signature
Epoch: round, Epoch: round,
}, nil }, nil
} }
func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry, base *MiningBase, mb *MinerBase) (*types.Ticket, error) { func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry, base *MiningBase, mb *MinerBase) (*types.Ticket, error) {
buf := new(bytes.Buffer) buf, err := m.address.MarshalBinary()
if err := m.address.MarshalCBOR(buf); err != nil { if err != nil {
return nil, xerrors.Errorf("failed to marshal address to cbor: %w", err) return nil, xerrors.Errorf("failed to marshal address: %w", err)
} }
round := base.epoch + base.nullRounds + 1 round := base.epoch + base.nullRounds + 1
input, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_TicketProduction, round-types.TicketRandomnessLookback, buf.Bytes()) input, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_TicketProduction, round-types.TicketRandomnessLookback, buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,4 +1,4 @@
package rpcclient package ethclient
import ( import (
"context" "context"

View File

@ -1,4 +1,4 @@
package rpcclient package ethclient
import ( import (
"crypto/ecdsa" "crypto/ecdsa"

31
go.mod
View File

@ -9,37 +9,43 @@ require (
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/cespare/cp v1.1.1 // indirect github.com/cespare/cp v1.1.1 // indirect
github.com/deckarep/golang-set v1.7.1 // indirect github.com/deckarep/golang-set v1.7.1 // indirect
github.com/dgraph-io/badger/v2 v2.2007.2 // indirect
github.com/drand/drand v1.2.1 github.com/drand/drand v1.2.1
github.com/drand/kyber v1.1.5 github.com/drand/kyber v1.1.5
github.com/elastic/gosigar v0.10.5 // indirect github.com/elastic/gosigar v0.10.5 // indirect
github.com/ethereum/go-ethereum v1.9.5 github.com/ethereum/go-ethereum v1.9.5
github.com/filecoin-project/go-address v0.0.4 github.com/filecoin-project/go-address v0.0.4
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 // indirect
github.com/filecoin-project/go-state-types v0.0.0-20201021025442-0ac4de847f4f github.com/filecoin-project/go-state-types v0.0.0-20201021025442-0ac4de847f4f
github.com/filecoin-project/lotus v1.1.2
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/google/uuid v1.1.1 // indirect github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf // indirect
github.com/google/gopacket v1.1.18 // indirect
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect
github.com/ipfs/go-datastore v0.4.5 // indirect
github.com/ipfs/go-ds-badger2 v0.1.1-0.20200708190120-187fc06f714e // indirect
github.com/ipfs/go-ipld-cbor v0.0.5-0.20200428170625-a0bd04d3cbdf // indirect
github.com/ipfs/go-ipld-format v0.2.0 // indirect
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 // indirect
github.com/jmoiron/sqlx v1.2.0 github.com/jmoiron/sqlx v1.2.0
github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect
github.com/lib/pq v1.7.0 // indirect
github.com/libp2p/go-libp2p v0.11.0 github.com/libp2p/go-libp2p v0.11.0
github.com/libp2p/go-libp2p-core v0.6.1 github.com/libp2p/go-libp2p-core v0.6.1
github.com/libp2p/go-libp2p-discovery v0.5.0 // indirect github.com/libp2p/go-libp2p-noise v0.1.2 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.8.3 // indirect
github.com/libp2p/go-libp2p-peerstore v0.2.6 // indirect
github.com/libp2p/go-libp2p-pubsub v0.3.6 github.com/libp2p/go-libp2p-pubsub v0.3.6
github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8 // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-sqlite3 v1.9.0
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/multiformats/go-multiaddr v0.3.1 github.com/multiformats/go-multiaddr v0.3.1
github.com/olekukonko/tablewriter v0.0.4 // indirect github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/prometheus/common v0.10.0 // indirect github.com/onsi/ginkgo v1.14.0 // indirect
github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a // indirect
github.com/raulk/clock v1.1.0 github.com/raulk/clock v1.1.0
github.com/rjeczalik/notify v0.9.2 // indirect github.com/rjeczalik/notify v0.9.2 // indirect
github.com/rs/cors v1.7.0 // indirect github.com/rs/cors v1.7.0 // indirect
github.com/rs/zerolog v1.20.0 // indirect
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
github.com/smartystreets/assertions v1.0.1 // indirect github.com/smartystreets/assertions v1.0.1 // indirect
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
@ -47,13 +53,16 @@ require (
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
github.com/supranational/blst v0.1.1 // indirect
github.com/tyler-smith/go-bip39 v1.0.2 // indirect github.com/tyler-smith/go-bip39 v1.0.2 // indirect
github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a // indirect
github.com/whyrusleeping/cbor-gen v0.0.0-20200826160007-0b9f6c5fb163
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542
go.opencensus.io v0.22.4
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 // indirect gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 // indirect
gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect

630
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,9 @@ import (
"github.com/Secured-Finance/dione/drand" "github.com/Secured-Finance/dione/drand"
) )
// NewBeaconSchedule creates a new beacon chain schedule // NewBeaconQueue creates a new beacon chain schedule
func (n *Node) NewBeaconSchedule() (beacon.Schedule, error) { func (n *Node) NewBeaconQueue() (beacon.Queue, error) {
schedule := beacon.Schedule{} schedule := beacon.Queue{}
bc, err := drand.NewDrandBeacon(config.ChainGenesis, config.TaskEpochInterval, n.PubSubRouter.Pubsub) bc, err := drand.NewDrandBeacon(config.ChainGenesis, config.TaskEpochInterval, n.PubSubRouter.Pubsub)
if err != nil { if err != nil {
return nil, fmt.Errorf("creating drand beacon: %w", err) return nil, fmt.Errorf("creating drand beacon: %w", err)

View File

@ -12,7 +12,7 @@ import (
"github.com/Secured-Finance/dione/consensus" "github.com/Secured-Finance/dione/consensus"
"github.com/Secured-Finance/dione/pb" "github.com/Secured-Finance/dione/pb"
"github.com/Secured-Finance/dione/rpc" "github.com/Secured-Finance/dione/rpc"
"github.com/Secured-Finance/dione/rpcclient" "github.com/Secured-Finance/dione/ethclient"
"github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p"
crypto "github.com/libp2p/go-libp2p-core/crypto" crypto "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/host"
@ -32,8 +32,9 @@ type Node struct {
OracleTopic string OracleTopic string
Config *config.Config Config *config.Config
Lotus *rpc.LotusClient Lotus *rpc.LotusClient
Ethereum *rpcclient.EthereumClient Ethereum *ethclient.EthereumClient
ConsensusManager *consensus.PBFTConsensusManager ConsensusManager *consensus.PBFTConsensusManager
MinerBase *consensus.MinerBase
} }
func NewNode(configPath string) (*Node, error) { func NewNode(configPath string) (*Node, error) {
@ -57,8 +58,13 @@ func (n *Node) setupNode(ctx context.Context, prvKey crypto.PrivKey, pexDiscover
n.setupConsensusManager(n.Config.ConsensusMaxFaultNodes) n.setupConsensusManager(n.Config.ConsensusMaxFaultNodes)
} }
func (n *Node) setupMiner() error {
// here we do miner base setup
return nil
}
func (n *Node) setupEthereumClient() error { func (n *Node) setupEthereumClient() error {
ethereum := rpcclient.NewEthereumClient() ethereum := ethclient.NewEthereumClient()
n.Ethereum = ethereum n.Ethereum = ethereum
return ethereum.Initialize(context.Background(), return ethereum.Initialize(context.Background(),
n.Config.Ethereum.GatewayAddress, n.Config.Ethereum.GatewayAddress,

49
sigs/ed25519/ed25519.go Normal file
View File

@ -0,0 +1,49 @@
package ed25519
import (
"crypto/ed25519"
"golang.org/x/xerrors"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/sigs"
"github.com/Secured-Finance/dione/types"
)
type ed25519Signer struct{}
func (ed25519Signer) GenPrivate() ([]byte, error) {
_, privKey, err := ed25519.GenerateKey(nil)
if err != nil {
return nil, err
}
return privKey.Seed(), nil
}
func (ed25519Signer) ToPublic(priv []byte) ([]byte, error) {
privKey := ed25519.NewKeyFromSeed(priv)
pubKey := privKey.Public().(ed25519.PublicKey)
return pubKey, nil
}
func (ed25519Signer) Sign(p []byte, msg []byte) ([]byte, error) {
privKey := ed25519.NewKeyFromSeed(p)
return ed25519.Sign(privKey, msg), nil
}
func (ed25519Signer) Verify(sig []byte, a peer.ID, msg []byte) error {
pubKey, err := a.ExtractPublicKey()
if err != nil {
return err
}
if valid, err := pubKey.Verify(msg, sig); err != nil || !valid {
return xerrors.Errorf("failed to verify signature")
}
return nil
}
func init() {
sigs.RegisterSignature(types.SigTypeEd25519, ed25519Signer{})
}

106
sigs/sigs.go Normal file
View File

@ -0,0 +1,106 @@
package sigs
import (
"context"
fmt "fmt"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/types"
"github.com/filecoin-project/go-address"
"golang.org/x/xerrors"
)
// Sign takes in signature type, private key and message. Returns a signature for that message.
// Valid sigTypes are: "ed25519"
func Sign(sigType types.SigType, privkey []byte, msg []byte) (*types.Signature, error) {
sv, ok := sigs[sigType]
if !ok {
return nil, fmt.Errorf("cannot sign message with signature of unsupported type: %v", sigType)
}
sb, err := sv.Sign(privkey, msg)
if err != nil {
return nil, err
}
return &types.Signature{
Type: sigType,
Data: sb,
}, nil
}
// Verify verifies signatures
func Verify(sig *types.Signature, addr peer.ID, msg []byte) error {
if sig == nil {
return xerrors.Errorf("signature is nil")
}
sv, ok := sigs[sig.Type]
if !ok {
return fmt.Errorf("cannot verify signature of unsupported type: %v", sig.Type)
}
return sv.Verify(sig.Data, addr, msg)
}
// Generate generates private key of given type
func Generate(sigType types.SigType) ([]byte, error) {
sv, ok := sigs[sigType]
if !ok {
return nil, fmt.Errorf("cannot generate private key of unsupported type: %v", sigType)
}
return sv.GenPrivate()
}
// ToPublic converts private key to public key
func ToPublic(sigType types.SigType, pk []byte) ([]byte, error) {
sv, ok := sigs[sigType]
if !ok {
return nil, fmt.Errorf("cannot generate public key of unsupported type: %v", sigType)
}
return sv.ToPublic(pk)
}
func CheckTaskSignature(ctx context.Context, task *types.DioneTask, worker address.Address) error {
//if task.IsValidated() {
// return nil
//}
//
//if task.BlockSig == nil {
// return xerrors.New("block signature not present")
//}
//
//sigb, err := task.SigningBytes()
//if err != nil {
// return xerrors.Errorf("failed to get block signing bytes: %w", err)
//}
//
//err = Verify(task.BlockSig, worker, sigb)
//if err == nil {
// task.SetValidated()
//}
// TODO
return nil
}
// SigShim is used for introducing signature functions
type SigShim interface {
GenPrivate() ([]byte, error)
ToPublic(pk []byte) ([]byte, error)
Sign(pk []byte, msg []byte) ([]byte, error)
Verify(sig []byte, a peer.ID, msg []byte) error
}
var sigs map[types.SigType]SigShim
// RegisterSignature should be only used during init
func RegisterSignature(typ types.SigType, vs SigShim) {
if sigs == nil {
sigs = make(map[types.SigType]SigShim)
}
sigs[typ] = vs
}

View File

@ -4,8 +4,8 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/Secured-Finance/dione/ethclient"
"github.com/Secured-Finance/dione/lib" "github.com/Secured-Finance/dione/lib"
"github.com/Secured-Finance/dione/rpcclient"
"github.com/Secured-Finance/dione/types" "github.com/Secured-Finance/dione/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
validation "github.com/go-ozzo/ozzo-validation" validation "github.com/go-ozzo/ozzo-validation"
@ -15,17 +15,17 @@ type DioneStakeInfo struct {
ID int ID int
MinerStake *big.Int MinerStake *big.Int
TotalStake *big.Int TotalStake *big.Int
MinerWallet string MinerAddress string
MinerEthWallet string MinerEthWallet string
Timestamp time.Time Timestamp time.Time
Ethereum *rpcclient.EthereumClient Ethereum *ethclient.EthereumClient
} }
func NewDioneStakeInfo(minerStake, totalStake *big.Int, minerWallet, minerEthWallet string, ethereumClient *rpcclient.EthereumClient) *DioneStakeInfo { func NewDioneStakeInfo(minerStake, totalStake *big.Int, minerWallet, minerEthWallet string, ethereumClient *ethclient.EthereumClient) *DioneStakeInfo {
return &DioneStakeInfo{ return &DioneStakeInfo{
MinerStake: minerStake, MinerStake: minerStake,
TotalStake: totalStake, TotalStake: totalStake,
MinerWallet: minerWallet, MinerAddress: minerWallet,
MinerEthWallet: minerEthWallet, MinerEthWallet: minerEthWallet,
Ethereum: ethereumClient, Ethereum: ethereumClient,
} }
@ -63,10 +63,10 @@ func (s *Store) CreateDioneStakeInfo(stakeStore *DioneStakeInfo) error {
now := lib.Clock.Now() now := lib.Clock.Now()
return s.db.QueryRow( return s.db.QueryRow(
"INSERT INTO staking (miner_stake, total_stake, miner_wallet, miner_eth_wallet, timestamp) VALUES ($1, $2, $3, $4, $5) RETURNING id", "INSERT INTO staking (miner_stake, total_stake, miner_address, miner_eth_wallet, timestamp) VALUES ($1, $2, $3, $4, $5) RETURNING id",
stakeStore.MinerStake, stakeStore.MinerStake,
stakeStore.TotalStake, stakeStore.TotalStake,
stakeStore.MinerWallet, stakeStore.MinerAddress,
stakeStore.MinerEthWallet, stakeStore.MinerEthWallet,
now, now,
).Scan(&stakeStore.ID) ).Scan(&stakeStore.ID)
@ -75,7 +75,7 @@ func (s *Store) CreateDioneStakeInfo(stakeStore *DioneStakeInfo) error {
func (s *Store) GetLastStakeInfo(wallet, ethWallet string) (*DioneStakeInfo, error) { func (s *Store) GetLastStakeInfo(wallet, ethWallet string) (*DioneStakeInfo, error) {
var stake *DioneStakeInfo var stake *DioneStakeInfo
if err := s.db.Select(&stake, if err := s.db.Select(&stake,
`SELECT miner_stake, total_stake, miner_wallet, miner_eth_wallet, timestamp FROM staking ORDER BY TIMESTAMP DESC LIMIT 1 WHERE miner_wallet=$1, miner_eth_wallet=$2`, `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, wallet,
ethWallet, ethWallet,
); err != nil { ); err != nil {
@ -91,7 +91,7 @@ func (s *DioneStakeInfo) Validate() error {
s, s,
validation.Field(&s.MinerStake, validation.Required, validation.By(types.ValidateBigInt(s.MinerStake))), validation.Field(&s.MinerStake, validation.Required, validation.By(types.ValidateBigInt(s.MinerStake))),
validation.Field(&s.TotalStake, validation.Required, validation.By(types.ValidateBigInt(s.TotalStake))), validation.Field(&s.TotalStake, validation.Required, validation.By(types.ValidateBigInt(s.TotalStake))),
validation.Field(&s.MinerWallet, validation.Required), validation.Field(&s.MinerAddress, validation.Required),
validation.Field(&s.MinerEthWallet, validation.Required), validation.Field(&s.MinerEthWallet, validation.Required),
) )
} }

View File

@ -3,6 +3,7 @@ package store
import ( import (
"github.com/Secured-Finance/dione/node" "github.com/Secured-Finance/dione/node"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
) )
type Store struct { type Store struct {
@ -29,7 +30,7 @@ func NewStore(node *node.Node, genesisTs uint64) (*Store, error) {
} }
func newDB(databaseURL string) (*sqlx.DB, error) { func newDB(databaseURL string) (*sqlx.DB, error) {
db, err := sqlx.Connect("postgres", databaseURL) db, err := sqlx.Connect("sqlite3", databaseURL)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -17,8 +16,7 @@ var (
type KeyType string type KeyType string
const ( const (
KTBLS KeyType = "bls" KTEd25519 KeyType = "ed25519"
KTSecp256k1 KeyType = "secp256k1"
) )
func (kt *KeyType) UnmarshalJSON(bb []byte) error { func (kt *KeyType) UnmarshalJSON(bb []byte) error {
@ -38,13 +36,11 @@ func (kt *KeyType) UnmarshalJSON(bb []byte) error {
if err != nil { if err != nil {
return fmt.Errorf("could not unmarshal KeyType either as string nor integer: %w", err) return fmt.Errorf("could not unmarshal KeyType either as string nor integer: %w", err)
} }
bst := crypto.SigType(b) bst := SigType(b)
switch bst { switch bst {
case crypto.SigTypeBLS: case SigTypeEd25519:
*kt = KTBLS *kt = KTEd25519
case crypto.SigTypeSecp256k1:
*kt = KTSecp256k1
default: default:
return fmt.Errorf("unknown sigtype: %d", bst) return fmt.Errorf("unknown sigtype: %d", bst)
} }

107
types/signature.go Normal file
View File

@ -0,0 +1,107 @@
package types
import (
"bytes"
"fmt"
"io"
"math"
cbg "github.com/whyrusleeping/cbor-gen"
)
type SigType byte
const (
SigTypeUnknown = SigType(math.MaxUint8)
SigTypeEd25519 = SigType(iota)
)
const SignatureMaxLength = 200
type Signature struct {
Type SigType
Data []byte
}
func (s *Signature) Equals(o *Signature) bool {
if s == nil || o == nil {
return s == o
}
return s.Type == o.Type && bytes.Equal(s.Data, o.Data)
}
func (s *Signature) MarshalCBOR(w io.Writer) error {
if s == nil {
_, err := w.Write(cbg.CborNull)
return err
}
header := cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(s.Data)+1))
if _, err := w.Write(header); err != nil {
return err
}
if _, err := w.Write([]byte{byte(s.Type)}); err != nil {
return err
}
if _, err := w.Write(s.Data); err != nil {
return err
}
return nil
}
func (s *Signature) UnmarshalCBOR(br io.Reader) error {
maj, l, err := cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajByteString {
return fmt.Errorf("not a byte string")
}
if l > SignatureMaxLength {
return fmt.Errorf("string too long")
}
if l == 0 {
return fmt.Errorf("string empty")
}
buf := make([]byte, l)
if _, err = io.ReadFull(br, buf); err != nil {
return err
}
switch SigType(buf[0]) {
default:
return fmt.Errorf("invalid signature type in cbor input: %d", buf[0])
case SigTypeEd25519:
s.Type = SigTypeEd25519
}
s.Data = buf[1:]
return nil
}
func (s *Signature) MarshalBinary() ([]byte, error) {
bs := make([]byte, len(s.Data)+1)
bs[0] = byte(s.Type)
copy(bs[1:], s.Data)
return bs, nil
}
func (s *Signature) UnmarshalBinary(bs []byte) error {
if len(bs) > SignatureMaxLength {
return fmt.Errorf("invalid signature bytes, too long (%d)", len(bs))
}
if len(bs) == 0 {
return fmt.Errorf("invalid signature bytes of length 0")
}
switch SigType(bs[0]) {
default:
// Do not error during unmarshal but leave a standard value.
// unmarshal(marshal(zero valued sig)) is valuable for test
// and type needs to be checked by caller anyway.
s.Type = SigTypeUnknown
case SigTypeEd25519:
s.Type = SigTypeEd25519
}
s.Data = bs[1:]
return nil
}

View File

@ -3,9 +3,17 @@ package types
import ( import (
"strconv" "strconv"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/config" "github.com/Secured-Finance/dione/config"
"github.com/filecoin-project/go-address" )
"github.com/filecoin-project/go-state-types/crypto"
type TaskType byte
const (
EthereumTaskType = TaskType(iota)
FilecoinTaskType
SolanaTaskType
) )
// TaskEpoch represents the timestamp of Task computed by the Dione miner // TaskEpoch represents the timestamp of Task computed by the Dione miner
@ -20,11 +28,12 @@ func (e TaskEpoch) String() string {
// DioneTask represents the values of task computation // DioneTask represents the values of task computation
// Miner is an address of miner node // Miner is an address of miner node
type DioneTask struct { type DioneTask struct {
Miner address.Address Type TaskType
Miner peer.ID
Ticket *Ticket Ticket *Ticket
ElectionProof *ElectionProof ElectionProof *ElectionProof
BeaconEntries []BeaconEntry BeaconEntries []BeaconEntry
Signature *crypto.Signature Signature *Signature
Epoch TaskEpoch Epoch TaskEpoch
Payload []byte Payload []byte
} }

View File

@ -3,22 +3,23 @@ package wallet
import ( import (
"fmt" "fmt"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/sigs"
"github.com/Secured-Finance/dione/types" "github.com/Secured-Finance/dione/types"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/lib/sigs"
) )
type Key struct { type Key struct {
types.KeyInfo types.KeyInfo
PublicKey []byte PublicKey []byte
Address address.Address Address peer.ID
} }
func GenerateKey(typ types.KeyType) (*Key, error) { func GenerateKey(typ types.KeyType) (*Key, error) {
ctyp := ActSigType(typ) ctyp := ActSigType(typ)
if ctyp == crypto.SigTypeUnknown { if ctyp == types.SigTypeUnknown {
return nil, fmt.Errorf("unknown sig type: %s", typ) return nil, fmt.Errorf("unknown sig type: %s", typ)
} }
pk, err := sigs.Generate(ctyp) pk, err := sigs.Generate(ctyp)
@ -46,16 +47,15 @@ func NewKey(keyinfo types.KeyInfo) (*Key, error) {
} }
switch k.Type { switch k.Type {
case types.KTSecp256k1: case types.KTEd25519:
k.Address, err = address.NewSecp256k1Address(k.PublicKey) pubKey, err := crypto.UnmarshalEd25519PublicKey(k.PublicKey)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal ed25519 public key: %w", err)
}
k.Address, err = peer.IDFromPublicKey(pubKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting Secp256k1 to address: %w", err) return nil, fmt.Errorf("converting Secp256k1 to address: %w", err)
} }
case types.KTBLS:
k.Address, err = address.NewBLSAddress(k.PublicKey)
if err != nil {
return nil, fmt.Errorf("converting BLS to address: %w", err)
}
default: default:
return nil, fmt.Errorf("unsupported key type: %s", k.Type) return nil, fmt.Errorf("unsupported key type: %s", k.Type)
} }
@ -63,13 +63,11 @@ func NewKey(keyinfo types.KeyInfo) (*Key, error) {
} }
func ActSigType(typ types.KeyType) crypto.SigType { func ActSigType(typ types.KeyType) types.SigType {
switch typ { switch typ {
case types.KTBLS: case types.KTEd25519:
return crypto.SigTypeBLS return types.SigTypeEd25519
case types.KTSecp256k1:
return crypto.SigTypeSecp256k1
default: default:
return crypto.SigTypeUnknown return types.SigTypeUnknown
} }
} }

View File

@ -4,10 +4,11 @@ import (
"context" "context"
"sync" "sync"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/Secured-Finance/dione/sigs"
"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/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/lib/sigs"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/xerrors" "golang.org/x/xerrors"
) )
@ -18,7 +19,7 @@ const (
) )
type LocalWallet struct { type LocalWallet struct {
keys map[address.Address]*Key keys map[peer.ID]*Key
keystore types.KeyStore keystore types.KeyStore
lk sync.Mutex lk sync.Mutex
@ -30,7 +31,7 @@ type Default interface {
func NewWallet(keystore types.KeyStore) (*LocalWallet, error) { func NewWallet(keystore types.KeyStore) (*LocalWallet, error) {
w := &LocalWallet{ w := &LocalWallet{
keys: make(map[address.Address]*Key), keys: make(map[peer.ID]*Key),
keystore: keystore, keystore: keystore,
} }
@ -38,7 +39,7 @@ func NewWallet(keystore types.KeyStore) (*LocalWallet, error) {
} }
func KeyWallet(keys ...*Key) *LocalWallet { func KeyWallet(keys ...*Key) *LocalWallet {
m := make(map[address.Address]*Key) m := make(map[peer.ID]*Key)
for _, key := range keys { for _, key := range keys {
m[key.Address] = key m[key.Address] = key
} }
@ -48,7 +49,7 @@ func KeyWallet(keys ...*Key) *LocalWallet {
} }
} }
func (w *LocalWallet) WalletSign(ctx context.Context, addr address.Address, msg []byte) (*crypto.Signature, error) { func (w *LocalWallet) WalletSign(ctx context.Context, addr peer.ID, msg []byte) (*types.Signature, error) {
ki, err := w.findKey(addr) ki, err := w.findKey(addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -60,7 +61,7 @@ func (w *LocalWallet) WalletSign(ctx context.Context, addr address.Address, msg
return sigs.Sign(ActSigType(ki.Type), ki.PrivateKey, msg) return sigs.Sign(ActSigType(ki.Type), ki.PrivateKey, msg)
} }
func (w *LocalWallet) findKey(addr address.Address) (*Key, error) { func (w *LocalWallet) findKey(addr peer.ID) (*Key, error) {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
@ -88,7 +89,7 @@ func (w *LocalWallet) findKey(addr address.Address) (*Key, error) {
return k, nil return k, nil
} }
func (w *LocalWallet) tryFind(addr address.Address) (types.KeyInfo, error) { func (w *LocalWallet) tryFind(addr peer.ID) (types.KeyInfo, error) {
ki, err := w.keystore.Get(KNamePrefix + addr.String()) ki, err := w.keystore.Get(KNamePrefix + addr.String())
if err == nil { if err == nil {
@ -122,52 +123,52 @@ func (w *LocalWallet) tryFind(addr address.Address) (types.KeyInfo, error) {
return ki, nil return ki, nil
} }
func (w *LocalWallet) GetDefault() (address.Address, error) { func (w *LocalWallet) GetDefault() (peer.ID, error) {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
ki, err := w.keystore.Get(KDefault) ki, err := w.keystore.Get(KDefault)
if err != nil { if err != nil {
return address.Undef, xerrors.Errorf("failed to get default key: %w", err) return "", xerrors.Errorf("failed to get default key: %w", err)
} }
k, err := NewKey(ki) k, err := NewKey(ki)
if err != nil { if err != nil {
return address.Undef, xerrors.Errorf("failed to read default key from keystore: %w", err) return "", xerrors.Errorf("failed to read default key from keystore: %w", err)
} }
return k.Address, nil return k.Address, nil
} }
func (w *LocalWallet) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) { func (w *LocalWallet) WalletNew(ctx context.Context, typ types.KeyType) (peer.ID, error) {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
k, err := GenerateKey(typ) k, err := GenerateKey(typ)
if err != nil { if err != nil {
return address.Undef, err return "", err
} }
if err := w.keystore.Put(KNamePrefix+k.Address.String(), k.KeyInfo); err != nil { if err := w.keystore.Put(KNamePrefix+k.Address.String(), k.KeyInfo); err != nil {
return address.Undef, xerrors.Errorf("saving to keystore: %w", err) return "", xerrors.Errorf("saving to keystore: %w", err)
} }
w.keys[k.Address] = k w.keys[k.Address] = k
_, err = w.keystore.Get(KDefault) _, err = w.keystore.Get(KDefault)
if err != nil { if err != nil {
if !xerrors.Is(err, types.ErrKeyInfoNotFound) { if !xerrors.Is(err, types.ErrKeyInfoNotFound) {
return address.Undef, err return "", err
} }
if err := w.keystore.Put(KDefault, k.KeyInfo); err != nil { if err := w.keystore.Put(KDefault, k.KeyInfo); err != nil {
return address.Undef, xerrors.Errorf("failed to set new key as default: %w", err) return "", xerrors.Errorf("failed to set new key as default: %w", err)
} }
} }
return k.Address, nil return k.Address, nil
} }
func (w *LocalWallet) WalletHas(ctx context.Context, addr address.Address) (bool, error) { func (w *LocalWallet) WalletHas(ctx context.Context, addr peer.ID) (bool, error) {
k, err := w.findKey(addr) k, err := w.findKey(addr)
if err != nil { if err != nil {
return false, err return false, err