From b3f22f151d38021076116a41faf91d0c34e3b60e Mon Sep 17 00:00:00 2001 From: bahadylbekov <33404905+bahadylbekov@users.noreply.github.com> Date: Wed, 28 Oct 2020 18:35:56 +0100 Subject: [PATCH] add: leader election proofs, beacon chain randomness, miner wallet and base miner structure --- beacon/beacon.go | 155 ++++++++++++++++++++++ config/tasks.go | 9 ++ consensus/leader.go | 85 ++++++++++++ consensus/randomness.go | 31 +++++ {lib/drand => drand}/drand.go | 63 +++++---- {lib/drand => drand}/drand_test.go | 0 go.mod | 13 +- go.sum | 49 +++++++ node/beacon.go | 21 +++ node/node.go | 5 +- node/node_test.go | 5 +- pb/pubsub_router.go | 6 +- store/stake.go | 9 ++ store/store.go | 22 ++++ types/beacon.go | 15 +++ types/bigint.go | 20 +++ types/electionproof.go | 205 +++++++++++++++++++++++++++++ types/electionproof_test.go | 145 ++++++++++++++++++++ types/eth-denomination.go | 10 ++ types/keystore.go | 72 ++++++++++ types/task.go | 31 +++++ types/ticket.go | 21 +++ wallet/key.go | 75 +++++++++++ wallet/memkeystore.go | 48 +++++++ wallet/wallet.go | 187 ++++++++++++++++++++++++++ 25 files changed, 1268 insertions(+), 34 deletions(-) create mode 100644 beacon/beacon.go create mode 100644 config/tasks.go create mode 100644 consensus/leader.go create mode 100644 consensus/randomness.go rename {lib/drand => drand}/drand.go (66%) rename {lib/drand => drand}/drand_test.go (100%) create mode 100644 node/beacon.go create mode 100644 store/stake.go create mode 100644 store/store.go create mode 100644 types/beacon.go create mode 100644 types/bigint.go create mode 100644 types/electionproof.go create mode 100644 types/electionproof_test.go create mode 100644 types/eth-denomination.go create mode 100644 types/keystore.go create mode 100644 types/task.go create mode 100644 types/ticket.go create mode 100644 wallet/key.go create mode 100644 wallet/memkeystore.go create mode 100644 wallet/wallet.go diff --git a/beacon/beacon.go b/beacon/beacon.go new file mode 100644 index 0000000..d8c59a2 --- /dev/null +++ b/beacon/beacon.go @@ -0,0 +1,155 @@ +package beacon + +import ( + "context" + "fmt" + + "github.com/Secured-Finance/dione/lib" + "github.com/sirupsen/logrus" + + "github.com/Secured-Finance/dione/types" +) + +type Response struct { + Entry types.BeaconEntry + Err error +} + +type Schedule []BeaconPoint + +func (bs Schedule) BeaconForEpoch(e types.TaskEpoch) RandomBeacon { + for i := len(bs) - 1; i >= 0; i-- { + bp := bs[i] + if e >= bp.Start { + return bp.Beacon + } + } + return bs[0].Beacon +} + +type BeaconPoint struct { + Start types.TaskEpoch + Beacon RandomBeacon +} + +// RandomBeacon represents a system that provides randomness. +// Other components interrogate the RandomBeacon to acquire randomness that's +// valid for a specific chain epoch. Also to verify beacon entries that have +// been posted on chain. +type RandomBeacon interface { + Entry(context.Context, uint64) <-chan Response + VerifyEntry(types.BeaconEntry, types.BeaconEntry) error + MaxBeaconRoundForEpoch(types.TaskEpoch) uint64 +} + +func ValidateTaskValues(bSchedule Schedule, t *types.DioneTask, parentEpoch types.TaskEpoch, prevEntry types.BeaconEntry) error { + { + parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) + currBeacon := bSchedule.BeaconForEpoch(t.Height) + 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 + } + } + + // TODO: fork logic + b := bSchedule.BeaconForEpoch(t.Height) + maxRound := b.MaxBeaconRoundForEpoch(t.Height) + if maxRound == 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 != maxRound { + return fmt.Errorf("expected final beacon entry in task to be at round %d, got %d", maxRound, last.Round) + } + + for i, e := range t.BeaconEntries { + if err := b.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 + } + + return nil +} + +func BeaconEntriesForTask(ctx context.Context, bSchedule Schedule, epoch types.TaskEpoch, parentEpoch types.TaskEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) { + { + parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) + currBeacon := bSchedule.BeaconForEpoch(epoch) + if parentBeacon != currBeacon { + // Fork logic + round := currBeacon.MaxBeaconRoundForEpoch(epoch) + 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 + } + } + + beacon := bSchedule.BeaconForEpoch(epoch) + + start := lib.Clock.Now() + + maxRound := beacon.MaxBeaconRoundForEpoch(epoch) + if maxRound == 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 = maxRound - 1 + } + + cur := maxRound + var out []types.BeaconEntry + for cur > prev.Round { + rch := beacon.Entry(ctx, cur) + select { + case resp := <-rch: + if resp.Err != nil { + return nil, fmt.Errorf("beacon entry request returned error: %w", resp.Err) + } + + out = append(out, resp.Entry) + cur = resp.Entry.Round - 1 + case <-ctx.Done(): + return nil, fmt.Errorf("context timed out waiting on beacon entry to come back for epoch %d: %w", epoch, ctx.Err()) + } + } + + logrus.Debug("fetching beacon entries", "took", lib.Clock.Since(start), "numEntries", 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/config/tasks.go b/config/tasks.go new file mode 100644 index 0000000..29e87ae --- /dev/null +++ b/config/tasks.go @@ -0,0 +1,9 @@ +package config + +var ExpectedLeadersPerEpoch = int64(5) + +var TasksPerEpoch = uint64(ExpectedLeadersPerEpoch) + +var ChainGenesis = uint64(1603603302) + +var TaskEpochInterval = uint64(15) diff --git a/consensus/leader.go b/consensus/leader.go new file mode 100644 index 0000000..fb1c76b --- /dev/null +++ b/consensus/leader.go @@ -0,0 +1,85 @@ +package consensus + +import ( + "bytes" + "context" + "fmt" + + "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" + "go.opencensus.io/trace" + "golang.org/x/xerrors" +) + +type MinerWallet interface { + WalletSign(context.Context, address.Address, []byte) (*crypto.Signature, error) +} + +type MinerBase struct { + MinerStake types.BigInt + NetworkStake types.BigInt + WorkerKey address.Address + PrevBeaconEntry types.BeaconEntry + BeaconEntries []types.BeaconEntry +} + +type SignFunc func(context.Context, address.Address, []byte) (*crypto.Signature, error) + +func ComputeVRF(ctx context.Context, sign SignFunc, worker address.Address, sigInput []byte) ([]byte, error) { + sig, err := sign(ctx, worker, sigInput) + if err != nil { + return nil, err + } + + if sig.Type != crypto.SigTypeBLS { + return nil, fmt.Errorf("miner worker address was not a BLS key") + } + + return sig.Data, nil +} + +func VerifyVRF(ctx context.Context, worker address.Address, vrfBase, vrfproof []byte) error { + _, span := trace.StartSpan(ctx, "VerifyVRF") + defer span.End() + + sig := &crypto.Signature{ + Type: crypto.SigTypeBLS, + Data: vrfproof, + } + + if err := sigs.Verify(sig, worker, vrfBase); err != nil { + return xerrors.Errorf("vrf was invalid: %w", err) + } + + return nil +} + +func IsRoundWinner(ctx context.Context, round types.TaskEpoch, + worker address.Address, brand types.BeaconEntry, mbi *MinerBase, a MinerWallet) (*types.ElectionProof, error) { + + buf := new(bytes.Buffer) + if err := worker.MarshalCBOR(buf); err != nil { + return nil, xerrors.Errorf("failed to cbor marshal address: %w", err) + } + + electionRand, err := DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf.Bytes()) + if err != nil { + return nil, xerrors.Errorf("failed to draw randomness: %w", err) + } + + vrfout, err := ComputeVRF(ctx, a.WalletSign, mbi.WorkerKey, electionRand) + if err != nil { + return nil, xerrors.Errorf("failed to compute VRF: %w", err) + } + + ep := &types.ElectionProof{VRFProof: vrfout} + j := ep.ComputeWinCount(mbi.MinerPower, mbi.NetworkPower) + ep.WinCount = j + if j < 1 { + return nil, nil + } + + return ep, nil +} diff --git a/consensus/randomness.go b/consensus/randomness.go new file mode 100644 index 0000000..91891e0 --- /dev/null +++ b/consensus/randomness.go @@ -0,0 +1,31 @@ +package consensus + +import ( + "encoding/binary" + + "github.com/Secured-Finance/dione/types" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/minio/blake2b-simd" + "golang.org/x/xerrors" +) + +func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round types.TaskEpoch, entropy []byte) ([]byte, error) { + h := blake2b.New256() + if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil { + return nil, xerrors.Errorf("deriving randomness: %w", 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: %w", err) + } + _, err = h.Write(entropy) + if err != nil { + return nil, xerrors.Errorf("hashing entropy: %w", err) + } + + return h.Sum(nil), nil +} diff --git a/lib/drand/drand.go b/drand/drand.go similarity index 66% rename from lib/drand/drand.go rename to drand/drand.go index 0406ba7..c896a46 100644 --- a/lib/drand/drand.go +++ b/drand/drand.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/Secured-Finance/dione/beacon" "github.com/drand/drand/chain" "github.com/drand/drand/client" httpClient "github.com/drand/drand/client/http" @@ -18,8 +19,10 @@ import ( "github.com/Secured-Finance/dione/config" "github.com/Secured-Finance/dione/lib" + types "github.com/Secured-Finance/dione/types" ) +// DrandRes structure representing response from drand network type DrandRes struct { // PreviousSig is the previous signature generated PreviousSig []byte @@ -31,21 +34,19 @@ type DrandRes struct { Randomness []byte } -type Beacon struct { - DrandResponse DrandRes - Error error -} - type DrandBeacon struct { DrandClient client.Client PublicKey kyber.Point Interval time.Duration + chainGenesisTime uint64 + chainRoundTime uint64 + drandGenesisTime uint64 cacheLock sync.Mutex - localCache map[uint64]DrandRes + localCache map[uint64]types.BeaconEntry } -func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { +func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub) (*DrandBeacon, error) { cfg := config.NewDrandConfig() drandChain, err := chain.InfoFromJSON(bytes.NewReader([]byte(cfg.ChainInfo))) @@ -81,22 +82,24 @@ func NewDrandBeacon(ps *pubsub.PubSub) (*DrandBeacon, error) { db := &DrandBeacon{ DrandClient: drandClient, - localCache: make(map[uint64]DrandRes), + localCache: make(map[uint64]types.BeaconEntry), } db.PublicKey = drandChain.PublicKey db.Interval = drandChain.Period db.drandGenesisTime = uint64(drandChain.GenesisTime) + db.chainRoundTime = interval + db.chainGenesisTime = genesisTs return db, nil } -func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan Beacon { - out := make(chan Beacon, 1) +func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan beacon.Response { + out := make(chan beacon.Response, 1) if round != 0 { - res := db.getCachedValue(round) - if res != nil { - out <- Beacon{DrandResponse: *res} + be := db.getCachedValue(round) + if be != nil { + out <- beacon.Response{Entry: *be} close(out) return out } @@ -107,27 +110,27 @@ func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan Beacon { logrus.Info("start fetching randomness", "round", round) resp, err := db.DrandClient.Get(ctx, round) - var res Beacon + var br beacon.Response if err != nil { - res.Error = fmt.Errorf("drand failed Get request: %w", err) + br.Err = fmt.Errorf("drand failed Get request: %w", err) } else { - res.DrandResponse.Round = resp.Round() - res.DrandResponse.Signature = resp.Signature() + br.Entry.Round = resp.Round() + br.Entry.Data = resp.Signature() } logrus.Info("done fetching randomness", "round", round, "took", lib.Clock.Since(start)) - out <- res + out <- br close(out) }() return out } -func (db *DrandBeacon) cacheValue(res DrandRes) { +func (db *DrandBeacon) cacheValue(res types.BeaconEntry) { db.cacheLock.Lock() defer db.cacheLock.Unlock() db.localCache[res.Round] = res } -func (db *DrandBeacon) getCachedValue(round uint64) *DrandRes { +func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry { db.cacheLock.Lock() defer db.cacheLock.Unlock() v, ok := db.localCache[round] @@ -137,7 +140,7 @@ func (db *DrandBeacon) getCachedValue(round uint64) *DrandRes { return &v } -func (db *DrandBeacon) VerifyEntry(curr DrandRes, prev DrandRes) error { +func (db *DrandBeacon) VerifyEntry(curr, prev types.BeaconEntry) error { if prev.Round == 0 { return nil } @@ -145,9 +148,9 @@ func (db *DrandBeacon) VerifyEntry(curr DrandRes, prev DrandRes) error { return nil } b := &chain.Beacon{ - PreviousSig: prev.PreviousSig, + PreviousSig: prev.Data, Round: curr.Round, - Signature: curr.Signature, + Signature: curr.Data, } err := chain.VerifyBeacon(db.PublicKey, b) if err == nil { @@ -155,3 +158,17 @@ func (db *DrandBeacon) VerifyEntry(curr DrandRes, prev DrandRes) error { } return err } + +func (db *DrandBeacon) MaxBeaconRoundForEpoch(taskEpoch types.TaskEpoch) uint64 { + var latestTs uint64 + if taskEpoch == 0 { + latestTs = db.chainGenesisTime + } else { + latestTs = ((uint64(taskEpoch) * db.chainRoundTime) + db.chainGenesisTime) - db.chainRoundTime + } + + dround := (latestTs - db.drandGenesisTime) / uint64(db.Interval.Seconds()) + return dround +} + +var _ beacon.RandomBeacon = (*DrandBeacon)(nil) diff --git a/lib/drand/drand_test.go b/drand/drand_test.go similarity index 100% rename from lib/drand/drand_test.go rename to drand/drand_test.go diff --git a/go.mod b/go.mod index 218675b..08aa1b9 100644 --- a/go.mod +++ b/go.mod @@ -11,17 +11,16 @@ require ( github.com/drand/kyber v1.1.5 github.com/elastic/gosigar v0.10.5 // indirect github.com/ethereum/go-ethereum v1.9.5 + github.com/filecoin-project/go-address v0.0.4 + github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 github.com/filecoin-project/go-state-types v0.0.0-20201021025442-0ac4de847f4f - github.com/filecoin-project/lotus v1.1.0 + github.com/filecoin-project/lotus v1.1.2 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/go-kit/kit v0.10.0 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect - github.com/golang/protobuf v1.4.2 // indirect github.com/google/uuid v1.1.1 github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect - github.com/ipfs/go-log v1.0.4 - github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/libp2p/go-libp2p v0.11.0 github.com/libp2p/go-libp2p-core v0.6.1 @@ -29,11 +28,14 @@ require ( github.com/libp2p/go-libp2p-kad-dht v0.8.3 github.com/libp2p/go-libp2p-peerstore v0.2.6 github.com/libp2p/go-libp2p-pubsub v0.3.6 + github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/multiformats/go-multiaddr v0.3.1 github.com/olekukonko/tablewriter v0.0.4 // indirect + github.com/prometheus/common v0.10.0 github.com/raulk/clock v1.1.0 github.com/rjeczalik/notify v0.9.2 // indirect github.com/rs/cors v1.7.0 // indirect + github.com/rs/zerolog v1.20.0 github.com/sirupsen/logrus v1.6.0 github.com/smartystreets/assertions v1.0.1 // indirect github.com/spf13/viper v1.7.1 @@ -41,8 +43,11 @@ require ( github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect github.com/stretchr/testify v1.6.1 + github.com/supranational/blst v0.1.1 github.com/tyler-smith/go-bip39 v1.0.2 // indirect github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect + 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/xerrors v0.0.0-20200804184101-5ec99f83aff1 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect diff --git a/go.sum b/go.sum index 6581a20..2bfc989 100644 --- a/go.sum +++ b/go.sum @@ -52,9 +52,11 @@ github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBA github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -127,6 +129,7 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= 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/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -147,8 +150,10 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= @@ -162,6 +167,7 @@ github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f h1:BOaYiTvg8p github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e h1:lj77EKYUpYXTd8CD/+QMIf8b6OIOTsfEBSXiAzuEHTU= github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e/go.mod h1:3ZQK6DMPSz/QZ73jlWxBtUhNA8xZx7LzUFSq/OfP8vk= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -206,6 +212,7 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elastic/go-sysinfo v1.3.0 h1:eb2XFGTMlSwG/yyU9Y8jVAYLIzU2sFzWXwo2gmetyrE= github.com/elastic/go-sysinfo v1.3.0/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= github.com/elastic/gosigar v0.10.5 h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59jo= @@ -224,6 +231,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fd/go-nat v1.0.0/go.mod h1:BTBu/CKvMmOMUPkKVef1pngt2WFH/lg7E6yQnulfp6E= +github.com/filecoin-project/filecoin-ffi v0.30.4-0.20200716204036-cddc56607e1d h1:YVh0Q+1iUvbv7SIfwA/alULOlWjQNOEnV72rgeYweLY= github.com/filecoin-project/filecoin-ffi v0.30.4-0.20200716204036-cddc56607e1d/go.mod h1:XE4rWG1P7zWPaC11Pkn1CVR20stqN52MnMkIrF4q6ZU= github.com/filecoin-project/go-address v0.0.2-0.20200218010043-eb9bb40ed5be/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0= github.com/filecoin-project/go-address v0.0.3 h1:eVfbdjEbpbzIrbiSa+PiGUY+oDK9HnUn+M1R/ggoHf8= @@ -240,10 +248,12 @@ github.com/filecoin-project/go-bitfield v0.2.1 h1:S6Uuqcspqu81sWJ0He4OAfFLm1tSwP github.com/filecoin-project/go-bitfield v0.2.1/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8= github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg= +github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ= github.com/filecoin-project/go-data-transfer v0.9.0 h1:nTT8j7Hu3TM0wRWrGy83/ctawG7sleJGdFWtIsUsKgY= github.com/filecoin-project/go-data-transfer v0.9.0/go.mod h1:i2CqUy7TMQGKukj9BgqIxiP8nDHDXU2VLd771KVaCaQ= github.com/filecoin-project/go-ds-versioning v0.1.0/go.mod h1:mp16rb4i2QPmxBnmanUx8i/XANp+PFCCJWiAb+VW4/s= +github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f h1:GxJzR3oRIMTPtpZ0b7QF8FKPK6/iPAc7trhlL5k/g+s= github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ= github.com/filecoin-project/go-fil-markets v1.0.0 h1:np9+tlnWXh9xYG4oZfha6HZFLYOaAZoMGR3V4w6DM48= github.com/filecoin-project/go-fil-markets v1.0.0/go.mod h1:lXExJyYHwpMMddCqhEdNrc7euYJKNkp04K76NZqJLGg= @@ -251,9 +261,11 @@ github.com/filecoin-project/go-hamt-ipld v0.1.5 h1:uoXrKbCQZ49OHpsTCkrThPNelC4W3 github.com/filecoin-project/go-hamt-ipld v0.1.5/go.mod h1:6Is+ONR5Cd5R6XZoCse1CWaXZc0Hdb/JeX+EQCQzX24= github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0 h1:b3UDemBYN2HNfk3KOXNuxgTTxlWi3xVvbQP0IT38fvM= github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+eEvrDCGJoPLxFpDynFjYfBjI= +github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49 h1:FSY245KeXFCUgyfFEu+bhrZNk8BGGJyfpSmQl2aiPU8= github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= github.com/filecoin-project/go-multistore v0.0.3 h1:vaRBY4YiA2UZFPK57RNuewypB8u0DzzQwqsL0XarpnI= github.com/filecoin-project/go-multistore v0.0.3/go.mod h1:kaNqCC4IhU4B1uyr7YWFHd23TL4KM32aChS0jNkyUvQ= +github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 h1:+/4aUeUoKr6AKfPE3mBhXA5spIV6UcKdTYDPNU2Tdmg= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak= github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= @@ -262,19 +274,24 @@ github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go github.com/filecoin-project/go-state-types v0.0.0-20201013222834-41ea465f274f/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.0.0-20201021025442-0ac4de847f4f h1:aKh+3jNOemceyBOO6uOL/3Zi3yNr4r9TiWVe0tBByZE= github.com/filecoin-project/go-state-types v0.0.0-20201021025442-0ac4de847f4f/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= +github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe h1:dF8u+LEWeIcTcfUcCf3WFVlc81Fr2JKg8zPzIbBDKDw= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statestore v0.1.0 h1:t56reH59843TwXHkMcwyuayStBIiWBRilQjQ+5IiwdQ= github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI= github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b/go.mod h1:Q0GQOBtKf1oE10eSXSlhN45kDBdGvEcVOqMiffqX+N8= github.com/filecoin-project/lotus v1.1.0 h1:ka2I5FcIXtUkBcTLnNj4gLBVpA7f7WVKG5wr69nmWQs= github.com/filecoin-project/lotus v1.1.0/go.mod h1:TCDAZYleaxC4NdKBsp0n+jscID12SxPoeYeHMCTT+TQ= +github.com/filecoin-project/lotus v1.1.2 h1:cs74C5oNVoIIFmjovpSuJR3qXzXcqS9cpOT+oSmNRiE= +github.com/filecoin-project/lotus v1.1.2/go.mod h1:jSlQv+/+WGox3TdEzBEfmXzvbot4OOsBiIh98t7AnAA= github.com/filecoin-project/specs-actors v0.6.1/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY= github.com/filecoin-project/specs-actors v0.9.4/go.mod h1:BStZQzx5x7TmCkLv0Bpa07U6cPKol6fd3w9KjMPZ6Z4= github.com/filecoin-project/specs-actors v0.9.12 h1:iIvk58tuMtmloFNHhAOQHG+4Gci6Lui0n7DYQGi3cJk= github.com/filecoin-project/specs-actors v0.9.12/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= +github.com/filecoin-project/specs-actors v0.9.13 h1:rUEOQouefi9fuVY/2HOroROJlZbOzWYXXeIh41KF2M4= github.com/filecoin-project/specs-actors/v2 v2.0.1/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY= github.com/filecoin-project/specs-actors/v2 v2.2.0 h1:IyCICb0NHYeD0sdSqjVGwWydn/7r7xXuxdpvGAcRCGY= github.com/filecoin-project/specs-actors/v2 v2.2.0/go.mod h1:rlv5Mx9wUhV8Qsz+vUezZNm+zL4tK08O0HreKKPB2Wc= +github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796 h1:dJsTPWpG2pcTeojO2pyn0c6l+x/3MZYCBgo/9d11JEk= github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= @@ -399,6 +416,7 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -523,11 +541,14 @@ github.com/ipfs/go-ds-badger2 v0.1.1-0.20200708190120-187fc06f714e/go.mod h1:lJn github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= +github.com/ipfs/go-ds-leveldb v0.4.2 h1:QmQoAJ9WkPMUfBLnu1sBVy0xWWlJPg0m4kRAiJL9iaw= github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= +github.com/ipfs/go-ds-measure v0.1.0 h1:vE4TyY4aeLeVgnnPBC5QzKIjKrqzha0NCujTfgvVbVQ= github.com/ipfs/go-ds-measure v0.1.0/go.mod h1:1nDiFrhLlwArTME1Ees2XaBOl49OoCgd2A3f8EchMSY= github.com/ipfs/go-ds-pebble v0.0.2-0.20200921225637-ce220f8ac459/go.mod h1:oh4liWHulKcDKVhCska5NLelE3MatWl+1FwSz3tY91g= github.com/ipfs/go-filestore v1.0.0 h1:QR7ekKH+q2AGiWDc7W2Q0qHuYSRZGUJqUn0GsegEPb0= github.com/ipfs/go-filestore v1.0.0/go.mod h1:/XOCuNtIe2f1YPbiXdYvD0BKLA0JR1MgPiFOdcuu9SM= +github.com/ipfs/go-fs-lock v0.0.6 h1:sn3TWwNVQqSeNjlWy6zQ1uUGAZrV3hPOyEA6y1/N2a0= github.com/ipfs/go-fs-lock v0.0.6/go.mod h1:OTR+Rj9sHiRubJh3dRhD15Juhd/+w6VPOY28L7zESmM= github.com/ipfs/go-graphsync v0.1.0/go.mod h1:jMXfqIEDFukLPZHqDPp8tJMbHO9Rmeb9CEGevngQbmE= github.com/ipfs/go-graphsync v0.3.0/go.mod h1:gEBvJUNelzMkaRPJTpg/jaKN4AQW/7wDWu0K92D8o10= @@ -559,6 +580,7 @@ github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAz github.com/ipfs/go-ipfs-files v0.0.2/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= github.com/ipfs/go-ipfs-files v0.0.4/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= +github.com/ipfs/go-ipfs-files v0.0.8 h1:8o0oFJkJ8UkO/ABl8T6ac6tKF3+NIpj67aAB6ZpusRg= github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs= github.com/ipfs/go-ipfs-flags v0.0.1/go.mod h1:RnXBb9WV53GSfTrSDVK61NLTFKvWc60n+K9EgCDh+rA= github.com/ipfs/go-ipfs-http-client v0.0.5/go.mod h1:8EKP9RGUrUex4Ff86WhnKU7seEBOtjdgXlY9XHYvYMw= @@ -610,6 +632,7 @@ github.com/ipfs/go-merkledag v0.3.2 h1:MRqj40QkrWkvPswXs4EfSslhZ4RVPRbxwX11js0t1 github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= +github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks= github.com/ipfs/go-path v0.0.3/go.mod h1:zIRQUez3LuQIU25zFjC2hpBTHimWx7VK5bjZgRLbbdo= github.com/ipfs/go-path v0.0.7/go.mod h1:6KTKmeRnBXgqrTvzFrPV3CamxcgvXX/4z79tfAd2Sno= github.com/ipfs/go-peertaskqueue v0.0.4/go.mod h1:03H8fhyeMfKNFWqzYEVyMbcPUeYrqP1MX6Kd+aN+rMQ= @@ -625,12 +648,15 @@ github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZ github.com/ipfs/interface-go-ipfs-core v0.2.3/go.mod h1:Tihp8zxGpUeE3Tokr94L6zWZZdkRQvG5TL6i9MuNE+s= github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg= github.com/ipfs/iptb-plugins v0.2.1/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs= +github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4 h1:6phjU3kXvCEWOZpu+Ob0w6DzgPFZmDLgLPxJhD8RxEY= github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4/go.mod h1:xrMEcuSq+D1vEwl+YAXsg/JfA98XGpXDwnkIL4Aimqw= github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8= github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f h1:XpOuNQ5GbXxUcSukbQcW9jkE7REpaFGJU2/T00fo9kA= github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM= github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs= +github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6 h1:6Mq+tZGSEMEoJJ1NbJRhddeelkXZcU8yfH/ZRYUo/Es= github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6/go.mod h1:3pHYooM9Ea65jewRwrb2u5uHZCNkNTe9ABsVB+SrkH0= +github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 h1:QG4CGBqCeuBo6aZlGAamSkxWdgWfZGeE49eUOWJPA4c= github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52/go.mod h1:fdg+/X9Gg4AsAIzWpEHwnqd+QY3b7lajxyjE1m4hkq4= github.com/jackpal/gateway v1.0.4/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= @@ -654,6 +680,7 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M 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/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= 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/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -676,6 +703,7 @@ github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4 github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 h1:ZHuwnjpP8LsVsUYqTqeVAI+GfDfJ6UNPrExZF+vX/DQ= github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= github.com/kilic/bls12-381 v0.0.0-20200731194930-64c428e1bff5/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= @@ -1074,6 +1102,7 @@ github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKU github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -1286,6 +1315,9 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= +github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= @@ -1384,6 +1416,7 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/supranational/blst v0.1.1 h1:GsK4oq7QZ7yfHI6dCh2NQsUe4imjlFwm5NXF5PWAWoo= github.com/supranational/blst v0.1.1/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= @@ -1442,12 +1475,14 @@ github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible/go.mod h1:34LEDb github.com/whyrusleeping/go-smux-multistream v2.0.2+incompatible/go.mod h1:dRWHHvc4HDQSHh9gbKEBbUZ+f2Q8iZTPG3UOGYODxSQ= github.com/whyrusleeping/go-smux-yamux v2.0.8+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI= github.com/whyrusleeping/go-smux-yamux v2.0.9+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI= +github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4 h1:NwiwjQDB3CzQ5XH0rdMh1oQqzJH7O2PSLWxif/w3zsY= github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4/go.mod h1:K+EVq8d5QcQ2At5VECsA+SNZvWefyBXh8TnIsxo1OvQ= github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= +github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d h1:wnjWu1N8UTNf2zzF5FWlEyNNbNw5GMVHaHaaLdvdTdA= github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d/go.mod h1:g7ckxrjiFh8mi1AY7ox23PZD0g6QU/TxW3U3unX7I3A= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= @@ -1459,13 +1494,16 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= +github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 h1:oWgZJmC1DorFZDpfMfWg7xk29yEOZiXmo/wZl+utTI8= github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xtaci/kcp-go v5.4.5+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.12.1 h1:hYRcyznPRJp+5mzF2sazTLP2nGvGjYDD2VzhHhFomLU= github.com/zondax/ledger-go v0.12.1/go.mod h1:KatxXrVDzgWwbssUWsF5+cOJHXPvzQ09YSlzGNuhOEo= go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ= @@ -1493,8 +1531,13 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +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 h1:7OAz8ucp35AU8eydejpYG7QrbE8rLKzGhHbZlJi5LYY= 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= @@ -1510,6 +1553,7 @@ go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= 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= @@ -1642,6 +1686,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180202135801-37707fdb30a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1742,6 +1787,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190912185636-87d9f09c5d89/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1751,6 +1797,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= @@ -1846,6 +1893,7 @@ google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEG google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1901,6 +1949,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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 h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/node/beacon.go b/node/beacon.go new file mode 100644 index 0000000..b6b5b18 --- /dev/null +++ b/node/beacon.go @@ -0,0 +1,21 @@ +package node + +import ( + "fmt" + + "github.com/Secured-Finance/dione/beacon" + "github.com/Secured-Finance/dione/config" + "github.com/Secured-Finance/dione/drand" +) + +// NewBeaconSchedule creates a new beacon chain schedule +func (n *Node) NewBeaconSchedule() (beacon.Schedule, error) { + schedule := beacon.Schedule{} + bc, err := drand.NewDrandBeacon(config.ChainGenesis, config.TaskEpochInterval, n.PubSubRouter.Pubsub) + if err != nil { + return nil, fmt.Errorf("creating drand beacon: %w", err) + } + schedule = append(schedule, beacon.BeaconPoint{Start: 0, Beacon: bc}) + + return schedule, nil +} diff --git a/node/node.go b/node/node.go index 3e70d95..5d166c5 100644 --- a/node/node.go +++ b/node/node.go @@ -5,10 +5,11 @@ import ( "crypto/rand" "flag" "fmt" - "github.com/libp2p/go-libp2p-kad-dht/dual" "sync" "time" + "github.com/libp2p/go-libp2p-kad-dht/dual" + "github.com/Secured-Finance/dione/config" "github.com/Secured-Finance/dione/consensus" "github.com/Secured-Finance/dione/pb" @@ -212,3 +213,5 @@ func generatePrivateKey() (crypto.PrivKey, error) { } return prvKey, nil } + +// TODO generate MinerBase for the node diff --git a/node/node_test.go b/node/node_test.go index b05c1e0..3c08af9 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -46,7 +46,6 @@ func TestConsensus(t *testing.T) { cfg.ListenPort = "1239" node6 := newNode(cfg) - time.Sleep(10 * time.Second) go node2.ConsensusManager.NewTestConsensus("test", "123") go node1.ConsensusManager.NewTestConsensus("test1", "123") @@ -54,7 +53,7 @@ func TestConsensus(t *testing.T) { go node4.ConsensusManager.NewTestConsensus("test1", "123") go node5.ConsensusManager.NewTestConsensus("test", "123") go node6.ConsensusManager.NewTestConsensus("test2", "123") - select{} + select {} } func newNode(cfg *config.Config) *Node { @@ -72,4 +71,4 @@ func newNode(cfg *config.Config) *Node { } node.setupNode(ctx, privKey) return node -} \ No newline at end of file +} diff --git a/pb/pubsub_router.go b/pb/pubsub_router.go index dcc886e..2367807 100644 --- a/pb/pubsub_router.go +++ b/pb/pubsub_router.go @@ -13,7 +13,7 @@ import ( type PubSubRouter struct { node host.Host - pubsub *pubsub.PubSub + Pubsub *pubsub.PubSub context context.Context contextCancel context.CancelFunc handlers map[string][]Handler @@ -44,7 +44,7 @@ func NewPubSubRouter(h host.Host, oracleTopic string) *PubSubRouter { if err != nil { logrus.Fatal("Error occurred when subscribing to service topic", err) } - psr.pubsub = pb + psr.Pubsub = pb go func() { for { @@ -109,7 +109,7 @@ func (psr *PubSubRouter) BroadcastToServiceTopic(msg *models.Message) error { if err != nil { return err } - err = psr.pubsub.Publish(psr.oracleTopic, data) + err = psr.Pubsub.Publish(psr.oracleTopic, data) return err } diff --git a/store/stake.go b/store/stake.go new file mode 100644 index 0000000..b804030 --- /dev/null +++ b/store/stake.go @@ -0,0 +1,9 @@ +package store + +import "math/big" + +// TODO: specify store for staking mechanism +type StakeTokenInfo struct { + TotalTokensStaked *big.Int + NodeTokensStaked *big.Int +} diff --git a/store/store.go b/store/store.go new file mode 100644 index 0000000..cb56f47 --- /dev/null +++ b/store/store.go @@ -0,0 +1,22 @@ +package store + +import ( + "database/sql" + + "github.com/Secured-Finance/dione/node" +) + +type Store struct { + db *sql.DB + node *node.Node + genesisTs uint64 + // genesisTask *types.DioneTask +} + +func NewStore(db *sql.DB, node *node.Node, genesisTs uint64) *Store { + return &Store{ + db: db, + node: node, + genesisTs: genesisTs, + } +} diff --git a/types/beacon.go b/types/beacon.go new file mode 100644 index 0000000..567b1b9 --- /dev/null +++ b/types/beacon.go @@ -0,0 +1,15 @@ +package types + +type BeaconEntry struct { + Round uint64 + Data []byte +} + +type Randomness []byte + +func NewBeaconEntry(round uint64, data []byte) BeaconEntry { + return BeaconEntry{ + Round: round, + Data: data, + } +} diff --git a/types/bigint.go b/types/bigint.go new file mode 100644 index 0000000..9190abf --- /dev/null +++ b/types/bigint.go @@ -0,0 +1,20 @@ +package types + +import ( + "math/big" + + big2 "github.com/filecoin-project/go-state-types/big" +) + +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} +} diff --git a/types/electionproof.go b/types/electionproof.go new file mode 100644 index 0000000..0627b4b --- /dev/null +++ b/types/electionproof.go @@ -0,0 +1,205 @@ +package types + +import ( + "math/big" + + "github.com/Secured-Finance/dione/config" + "github.com/minio/blake2b-simd" +) + +type ElectionProof struct { + WinCount int64 + VRFProof []byte +} + +const precision = 256 + +var ( + expNumCoef []*big.Int + expDenoCoef []*big.Int +) + +func init() { + parse := func(coefs []string) []*big.Int { + out := make([]*big.Int, len(coefs)) + for i, coef := range coefs { + c, ok := new(big.Int).SetString(coef, 10) + if !ok { + panic("could not parse exp paramemter") + } + // << 256 (Q.0 to Q.256), >> 128 to transform integer params to coefficients + c = c.Lsh(c, precision-128) + out[i] = c + } + return out + } + + // parameters are in integer format, + // coefficients are *2^-128 of that + num := []string{ + "-648770010757830093818553637600", + "67469480939593786226847644286976", + "-3197587544499098424029388939001856", + "89244641121992890118377641805348864", + "-1579656163641440567800982336819953664", + "17685496037279256458459817590917169152", + "-115682590513835356866803355398940131328", + "340282366920938463463374607431768211456", + } + expNumCoef = parse(num) + + deno := []string{ + "1225524182432722209606361", + "114095592300906098243859450", + "5665570424063336070530214243", + "194450132448609991765137938448", + "5068267641632683791026134915072", + "104716890604972796896895427629056", + "1748338658439454459487681798864896", + "23704654329841312470660182937960448", + "259380097567996910282699886670381056", + "2250336698853390384720606936038375424", + "14978272436876548034486263159246028800", + "72144088983913131323343765784380833792", + "224599776407103106596571252037123047424", + "340282366920938463463374607431768211456", + } + expDenoCoef = parse(deno) +} + +// expneg accepts x in Q.256 format and computes e^-x. +// It is most precise within [0, 1.725) range, where error is less than 3.4e-30. +// Over the [0, 5) range its error is less than 4.6e-15. +// Output is in Q.256 format. +func expneg(x *big.Int) *big.Int { + // exp is approximated by rational function + // polynomials of the rational function are evaluated using Horner's method + num := polyval(expNumCoef, x) // Q.256 + deno := polyval(expDenoCoef, x) // Q.256 + + num = num.Lsh(num, precision) // Q.512 + return num.Div(num, deno) // Q.512 / Q.256 => Q.256 +} + +// polyval evaluates a polynomial given by coefficients `p` in Q.256 format +// at point `x` in Q.256 format. Output is in Q.256. +// Coefficients should be ordered from the highest order coefficient to the lowest. +func polyval(p []*big.Int, x *big.Int) *big.Int { + // evaluation using Horner's method + res := new(big.Int).Set(p[0]) // Q.256 + tmp := new(big.Int) // big.Int.Mul doesn't like when input is reused as output + for _, c := range p[1:] { + tmp = tmp.Mul(res, x) // Q.256 * Q.256 => Q.512 + res = res.Rsh(tmp, precision) // Q.512 >> 256 => Q.256 + res = res.Add(res, c) + } + + return res +} + +// 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 + return lam +} + +var MaxWinCount = 3 * int64(config.TasksPerEpoch) + +type poiss struct { + lam *big.Int + pmf *big.Int + icdf *big.Int + + tmp *big.Int // temporary variable for optimization + + k uint64 +} + +// newPoiss starts poisson inverted CDF +// lambda is in Q.256 format +// returns (instance, `1-poisscdf(0, lambda)`) +// CDF value returend is reused when calling `next` +func newPoiss(lambda *big.Int) (*poiss, *big.Int) { + + // pmf(k) = (lambda^k)*(e^lambda) / k! + // k = 0 here, so it simplifies to just e^-lambda + elam := expneg(lambda) // Q.256 + pmf := new(big.Int).Set(elam) + + // icdf(k) = 1 - ∑ᵏᵢ₌₀ pmf(i) + // icdf(0) = 1 - pmf(0) + icdf := big.NewInt(1) + icdf = icdf.Lsh(icdf, precision) // Q.256 + icdf = icdf.Sub(icdf, pmf) // Q.256 + + k := uint64(0) + + p := &poiss{ + lam: lambda, + pmf: pmf, + + tmp: elam, + icdf: icdf, + + k: k, + } + + return p, icdf +} + +// next computes `k++, 1-poisscdf(k, lam)` +// return is in Q.256 format +func (p *poiss) next() *big.Int { + // incrementally compute next pmf and icdf + + // pmf(k) = (lambda^k)*(e^lambda) / k! + // so pmf(k) = pmf(k-1) * lambda / k + p.k++ + p.tmp.SetUint64(p.k) // Q.0 + + // calculate pmf for k + p.pmf = p.pmf.Div(p.pmf, p.tmp) // Q.256 / Q.0 => Q.256 + // we are using `tmp` as target for multiplication as using an input as output + // for Int.Mul causes allocations + p.tmp = p.tmp.Mul(p.pmf, p.lam) // Q.256 * Q.256 => Q.512 + p.pmf = p.pmf.Rsh(p.tmp, precision) // Q.512 >> 256 => Q.256 + + // calculate output + // icdf(k) = icdf(k-1) - pmf(k) + p.icdf = p.icdf.Sub(p.icdf, p.pmf) // Q.256 + return p.icdf +} + +// 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 { + h := blake2b.Sum256(ep.VRFProof) + + lhs := BigFromBytes(h[:]).Int // 256bits, assume Q.256 so [0, 1) + + // We are calculating upside-down CDF of Poisson distribution with + // rate λ=power*E/totalPower + // Steps: + // 1. calculate λ=power*E/totalPower + // 2. calculate elam = exp(-λ) + // 3. Check how many times we win: + // j = 0 + // pmf = elam + // rhs = 1 - pmf + // for h(vrf) < rhs: j++; pmf = pmf * lam / j; rhs = rhs - pmf + + lam := lambda(power.Int, totalPower.Int) // Q.256 + + p, rhs := newPoiss(lam) + + var j int64 + for lhs.Cmp(rhs) < 0 && j < MaxWinCount { + rhs = p.next() + j++ + } + + return j +} diff --git a/types/electionproof_test.go b/types/electionproof_test.go new file mode 100644 index 0000000..47f684e --- /dev/null +++ b/types/electionproof_test.go @@ -0,0 +1,145 @@ +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) + } +} diff --git a/types/eth-denomination.go b/types/eth-denomination.go new file mode 100644 index 0000000..274b3c1 --- /dev/null +++ b/types/eth-denomination.go @@ -0,0 +1,10 @@ +package types + +// All ethereum values uses bit.Int +// to calculate Wei values in GWei: +// new(big.Int).Mul(value, big.NewInt(params.GWei)) +const ( + Wei = 1 + GWei = 1e9 + Ether = 1e18 +) diff --git a/types/keystore.go b/types/keystore.go new file mode 100644 index 0000000..5842307 --- /dev/null +++ b/types/keystore.go @@ -0,0 +1,72 @@ +package types + +import ( + "encoding/json" + "fmt" + + "github.com/filecoin-project/go-state-types/crypto" + "github.com/sirupsen/logrus" +) + +var ( + ErrKeyInfoNotFound = fmt.Errorf("key info not found") + ErrKeyExists = fmt.Errorf("key already exists") +) + +// KeyType defines a type of a key +type KeyType string + +const ( + KTBLS KeyType = "bls" + KTSecp256k1 KeyType = "secp256k1" +) + +func (kt *KeyType) UnmarshalJSON(bb []byte) error { + { + // first option, try unmarshaling as string + var s string + err := json.Unmarshal(bb, &s) + if err == nil { + *kt = KeyType(s) + return nil + } + } + + { + var b byte + err := json.Unmarshal(bb, &b) + if err != nil { + return fmt.Errorf("could not unmarshal KeyType either as string nor integer: %w", err) + } + bst := crypto.SigType(b) + + switch bst { + case crypto.SigTypeBLS: + *kt = KTBLS + case crypto.SigTypeSecp256k1: + *kt = KTSecp256k1 + default: + return fmt.Errorf("unknown sigtype: %d", bst) + } + logrus.Warn("deprecation: integer style 'KeyType' is deprecated, switch to string style") + return nil + } +} + +// KeyInfo is used for storing keys in KeyStore +type KeyInfo struct { + Type KeyType + PrivateKey []byte +} + +// KeyStore is used for storing secret keys +type KeyStore interface { + // List lists all the keys stored in the KeyStore + List() ([]string, error) + // Get gets a key out of keystore and returns KeyInfo corresponding to named key + Get(string) (KeyInfo, error) + // Put saves a key info under given name + Put(string, KeyInfo) error + // Delete removes a key from keystore + Delete(string) error +} diff --git a/types/task.go b/types/task.go new file mode 100644 index 0000000..c78beb3 --- /dev/null +++ b/types/task.go @@ -0,0 +1,31 @@ +package types + +import ( + "strconv" + + "github.com/Secured-Finance/dione/config" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/crypto" +) + +// TaskEpoch represents the timestamp of Task computed by the Dione miner +type TaskEpoch int64 + +func (e TaskEpoch) String() string { + return strconv.FormatInt(int64(e), 10) +} + +// DioneTask represents the values of task computation +// Miner is an address of miner node +type DioneTask struct { + Miner address.Address + Ticket *Ticket + ElectionProof *ElectionProof + BeaconEntries []BeaconEntry + Signature *crypto.Signature + Height TaskEpoch +} + +var tasksPerEpoch = NewInt(config.TasksPerEpoch) + +const sha256bits = 256 diff --git a/types/ticket.go b/types/ticket.go new file mode 100644 index 0000000..68f7425 --- /dev/null +++ b/types/ticket.go @@ -0,0 +1,21 @@ +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 +} diff --git a/wallet/key.go b/wallet/key.go new file mode 100644 index 0000000..1314a54 --- /dev/null +++ b/wallet/key.go @@ -0,0 +1,75 @@ +package wallet + +import ( + "fmt" + + "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 { + types.KeyInfo + + PublicKey []byte + Address address.Address +} + +func GenerateKey(typ types.KeyType) (*Key, error) { + ctyp := ActSigType(typ) + if ctyp == crypto.SigTypeUnknown { + return nil, fmt.Errorf("unknown sig type: %s", typ) + } + pk, err := sigs.Generate(ctyp) + if err != nil { + return nil, err + } + ki := types.KeyInfo{ + Type: typ, + PrivateKey: pk, + } + return NewKey(ki) +} + +// NewKey generates a new Key based on private key and signature type +// it works with both types of signatures Secp256k1 and BLS +func NewKey(keyinfo types.KeyInfo) (*Key, error) { + k := &Key{ + KeyInfo: keyinfo, + } + + var err error + k.PublicKey, err = sigs.ToPublic(ActSigType(k.Type), k.PrivateKey) + if err != nil { + return nil, err + } + + switch k.Type { + case types.KTSecp256k1: + k.Address, err = address.NewSecp256k1Address(k.PublicKey) + if err != nil { + 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: + return nil, fmt.Errorf("unsupported key type: %s", k.Type) + } + return k, nil + +} + +func ActSigType(typ types.KeyType) crypto.SigType { + switch typ { + case types.KTBLS: + return crypto.SigTypeBLS + case types.KTSecp256k1: + return crypto.SigTypeSecp256k1 + default: + return crypto.SigTypeUnknown + } +} diff --git a/wallet/memkeystore.go b/wallet/memkeystore.go new file mode 100644 index 0000000..b36e7bd --- /dev/null +++ b/wallet/memkeystore.go @@ -0,0 +1,48 @@ +package wallet + +import ( + "github.com/Secured-Finance/dione/types" +) + +type MemKeyStore struct { + m map[string]types.KeyInfo +} + +func NewMemKeyStore() *MemKeyStore { + return &MemKeyStore{ + make(map[string]types.KeyInfo), + } +} + +// List lists all the keys stored in the KeyStore +func (mks *MemKeyStore) List() ([]string, error) { + var out []string + for k := range mks.m { + out = append(out, k) + } + return out, nil +} + +// Get gets a key out of keystore and returns KeyInfo corresponding to named key +func (mks *MemKeyStore) Get(k string) (types.KeyInfo, error) { + ki, ok := mks.m[k] + if !ok { + return types.KeyInfo{}, types.ErrKeyInfoNotFound + } + + return ki, nil +} + +// Put saves a key info under given name +func (mks *MemKeyStore) Put(k string, ki types.KeyInfo) error { + mks.m[k] = ki + return nil +} + +// Delete removes a key from keystore +func (mks *MemKeyStore) Delete(k string) error { + delete(mks.m, k) + return nil +} + +var _ (types.KeyStore) = (*MemKeyStore)(nil) diff --git a/wallet/wallet.go b/wallet/wallet.go new file mode 100644 index 0000000..ae6176e --- /dev/null +++ b/wallet/wallet.go @@ -0,0 +1,187 @@ +package wallet + +import ( + "context" + "sync" + + "github.com/Secured-Finance/dione/lib/sigs" + "github.com/Secured-Finance/dione/types" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/sirupsen/logrus" + "golang.org/x/xerrors" +) + +const ( + KNamePrefix = "wallet-" + KDefault = "default" +) + +type LocalWallet struct { + keys map[address.Address]*Key + keystore types.KeyStore + + lk sync.Mutex +} + +type Default interface { + GetDefault() (address.Address, error) +} + +func NewWallet(keystore types.KeyStore) (*LocalWallet, error) { + w := &LocalWallet{ + keys: make(map[address.Address]*Key), + keystore: keystore, + } + + return w, nil +} + +func KeyWallet(keys ...*Key) *LocalWallet { + m := make(map[address.Address]*Key) + for _, key := range keys { + m[key.Address] = key + } + + return &LocalWallet{ + keys: m, + } +} + +func (w *LocalWallet) WalletSign(ctx context.Context, addr address.Address, msg []byte) (*crypto.Signature, error) { + ki, err := w.findKey(addr) + if err != nil { + return nil, err + } + if ki == nil { + return nil, xerrors.Errorf("signing using key '%s': %w", addr.String(), types.ErrKeyInfoNotFound) + } + + return sigs.Sign(ActSigType(ki.Type), ki.PrivateKey, msg) +} + +func (w *LocalWallet) findKey(addr address.Address) (*Key, error) { + w.lk.Lock() + defer w.lk.Unlock() + + k, ok := w.keys[addr] + if ok { + return k, nil + } + if w.keystore == nil { + logrus.Warn("findKey didn't find the key in in-memory wallet") + return nil, nil + } + + ki, err := w.tryFind(addr) + if err != nil { + if xerrors.Is(err, types.ErrKeyInfoNotFound) { + return nil, nil + } + return nil, xerrors.Errorf("getting from keystore: %w", err) + } + k, err = NewKey(ki) + if err != nil { + return nil, xerrors.Errorf("decoding from keystore: %w", err) + } + w.keys[k.Address] = k + return k, nil +} + +func (w *LocalWallet) tryFind(addr address.Address) (types.KeyInfo, error) { + + ki, err := w.keystore.Get(KNamePrefix + addr.String()) + if err == nil { + return ki, err + } + + if !xerrors.Is(err, types.ErrKeyInfoNotFound) { + return types.KeyInfo{}, err + } + + // We got an ErrKeyInfoNotFound error + // Try again, this time with the testnet prefix + + tAddress, err := swapMainnetForTestnetPrefix(addr.String()) + if err != nil { + return types.KeyInfo{}, err + } + + ki, err = w.keystore.Get(KNamePrefix + tAddress) + if err != nil { + return types.KeyInfo{}, err + } + + // We found it with the testnet prefix + // Add this KeyInfo with the mainnet prefix address string + err = w.keystore.Put(KNamePrefix+addr.String(), ki) + if err != nil { + return types.KeyInfo{}, err + } + + return ki, nil +} + +func (w *LocalWallet) GetDefault() (address.Address, error) { + w.lk.Lock() + defer w.lk.Unlock() + + ki, err := w.keystore.Get(KDefault) + if err != nil { + return address.Undef, xerrors.Errorf("failed to get default key: %w", err) + } + + k, err := NewKey(ki) + if err != nil { + return address.Undef, xerrors.Errorf("failed to read default key from keystore: %w", err) + } + + return k.Address, nil +} + +func (w *LocalWallet) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) { + w.lk.Lock() + defer w.lk.Unlock() + + k, err := GenerateKey(typ) + if err != nil { + return address.Undef, err + } + + if err := w.keystore.Put(KNamePrefix+k.Address.String(), k.KeyInfo); err != nil { + return address.Undef, xerrors.Errorf("saving to keystore: %w", err) + } + w.keys[k.Address] = k + + _, err = w.keystore.Get(KDefault) + if err != nil { + if !xerrors.Is(err, types.ErrKeyInfoNotFound) { + return address.Undef, err + } + + 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 k.Address, nil +} + +func (w *LocalWallet) WalletHas(ctx context.Context, addr address.Address) (bool, error) { + k, err := w.findKey(addr) + if err != nil { + return false, err + } + return k != nil, nil +} + +func swapMainnetForTestnetPrefix(addr string) (string, error) { + aChars := []rune(addr) + prefixRunes := []rune(address.TestnetPrefix) + if len(prefixRunes) != 1 { + return "", xerrors.Errorf("unexpected prefix length: %d", len(prefixRunes)) + } + + aChars[0] = prefixRunes[0] + return string(aChars), nil +}