Merge remote-tracking branch 'origin/master' into feat/post-schedule
This commit is contained in:
commit
d55e872135
@ -14,4 +14,7 @@ const DealVoucherSkewLimit = 10
|
||||
const ForkLengthThreshold = 20
|
||||
const RandomnessLookback = 20
|
||||
|
||||
const ProvingPeriodDuration = 2 * 60 // an hour, for now
|
||||
const PoSTChallangeTime = 1 * 60
|
||||
|
||||
// TODO: Move other important consts here
|
||||
|
@ -2,7 +2,9 @@ package actors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/build"
|
||||
"github.com/filecoin-project/go-lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/go-lotus/chain/address"
|
||||
"github.com/filecoin-project/go-lotus/chain/types"
|
||||
@ -12,10 +14,10 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var ProvingPeriodDuration = uint64(2 * 60) // an hour, for now
|
||||
const POST_SECTORS_COUNT = 8192
|
||||
|
||||
type StorageMinerActor struct{}
|
||||
@ -118,9 +120,10 @@ type maMethods struct {
|
||||
IsLate uint64
|
||||
PaymentVerifyInclusion uint64
|
||||
PaymentVerifySector uint64
|
||||
AddFaults uint64
|
||||
}
|
||||
|
||||
var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}
|
||||
var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
||||
|
||||
func (sma StorageMinerActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
@ -142,6 +145,7 @@ func (sma StorageMinerActor) Exports() []interface{} {
|
||||
//16: sma.IsLate,
|
||||
17: sma.PaymentVerifyInclusion,
|
||||
18: sma.PaymentVerifySector,
|
||||
19: nil,
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,11 +251,6 @@ func (sma StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContex
|
||||
return nil, aerrors.New(3, "not enough collateral")
|
||||
}
|
||||
|
||||
// ensure that the miner cannot commit more sectors than can be proved with a single PoSt
|
||||
if self.SectorSetSize >= POST_SECTORS_COUNT {
|
||||
return nil, aerrors.New(4, "too many sectors")
|
||||
}
|
||||
|
||||
// Note: There must exist a unique index in the miner's sector set for each
|
||||
// sector ID. The `faults`, `recovered`, and `done` parameters of the
|
||||
// SubmitPoSt method express indices into this sector set.
|
||||
@ -272,7 +271,7 @@ func (sma StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContex
|
||||
if self.ProvingSetSize == 0 {
|
||||
self.ProvingSet = self.Sectors
|
||||
self.ProvingSetSize = self.SectorSetSize
|
||||
self.ProvingPeriodEnd = vmctx.BlockHeight() + ProvingPeriodDuration
|
||||
self.ProvingPeriodEnd = vmctx.BlockHeight() + build.ProvingPeriodDuration
|
||||
}
|
||||
|
||||
nstate, err := vmctx.Storage().Put(self)
|
||||
@ -287,6 +286,8 @@ func (sma StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContex
|
||||
}
|
||||
|
||||
type SubmitPoStParams struct {
|
||||
Proof []byte
|
||||
DoneSet types.BitField
|
||||
// TODO: once the spec changes finish, we have more work to do here...
|
||||
}
|
||||
|
||||
@ -307,13 +308,116 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext,
|
||||
return nil, aerrors.New(1, "not authorized to submit post for miner")
|
||||
}
|
||||
|
||||
oldProvingSetSize := self.ProvingSetSize
|
||||
feesRequired := types.NewInt(0)
|
||||
nextProvingPeriodEnd := self.ProvingPeriodEnd + build.ProvingPeriodDuration
|
||||
if vmctx.BlockHeight() > nextProvingPeriodEnd {
|
||||
return nil, aerrors.New(1, "PoSt submited too late")
|
||||
}
|
||||
|
||||
self.ProvingSet = self.Sectors
|
||||
self.ProvingSetSize = self.SectorSetSize
|
||||
var lateSubmission bool
|
||||
if vmctx.BlockHeight() > self.ProvingPeriodEnd {
|
||||
//TODO late fee calc
|
||||
lateSubmission = true
|
||||
feesRequired = types.BigAdd(feesRequired, types.NewInt(1000))
|
||||
}
|
||||
|
||||
//TODO temporary sector failure fees
|
||||
|
||||
msgVal := vmctx.Message().Value
|
||||
if types.BigCmp(msgVal, feesRequired) < 0 {
|
||||
return nil, aerrors.New(2, "not enough funds to pay post submission fees")
|
||||
}
|
||||
|
||||
if types.BigCmp(msgVal, feesRequired) > 0 {
|
||||
_, err := vmctx.Send(vmctx.Message().From, 0,
|
||||
types.BigSub(msgVal, feesRequired), nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not refund excess fees")
|
||||
}
|
||||
}
|
||||
|
||||
var seed [sectorbuilder.CommLen]byte
|
||||
{
|
||||
var rand []byte
|
||||
var err ActorError
|
||||
if !lateSubmission {
|
||||
rand, err = vmctx.GetRandomness(self.ProvingPeriodEnd - build.PoSTChallangeTime)
|
||||
} else {
|
||||
rand, err = vmctx.GetRandomness(nextProvingPeriodEnd - build.PoSTChallangeTime)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get randomness for PoST")
|
||||
}
|
||||
if len(rand) < len(seed) {
|
||||
return nil, aerrors.Escalate(fmt.Errorf("randomness too small (%d < %d)",
|
||||
len(rand), len(seed)), "improper randomness")
|
||||
}
|
||||
copy(seed[:], rand)
|
||||
}
|
||||
|
||||
pss, lerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
|
||||
if lerr != nil {
|
||||
return nil, aerrors.Escalate(lerr, "could not load sector set node")
|
||||
}
|
||||
|
||||
var sectorInfos []sectorbuilder.SectorInfo
|
||||
if err := pss.ForEach(func(id uint64, v *cbg.Deferred) error {
|
||||
var comms [][]byte
|
||||
if err := cbor.DecodeInto(v.Raw, &comms); err != nil {
|
||||
return xerrors.New("could not decode comms")
|
||||
}
|
||||
si := sectorbuilder.SectorInfo{
|
||||
SectorID: id,
|
||||
}
|
||||
commR := comms[0]
|
||||
if len(commR) != len(si.CommR) {
|
||||
return xerrors.Errorf("commR length is wrong: %d", len(commR))
|
||||
}
|
||||
copy(si.CommR[:], commR)
|
||||
|
||||
sectorInfos = append(sectorInfos, si)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, aerrors.Absorb(err, 3, "could not decode sectorset")
|
||||
}
|
||||
|
||||
faults := self.CurrentFaultSet.All()
|
||||
|
||||
if ok, lerr := sectorbuilder.VerifyPost(mi.SectorSize.Uint64(),
|
||||
sectorbuilder.NewSortedSectorInfo(sectorInfos), seed, params.Proof,
|
||||
faults); !ok || lerr != nil {
|
||||
if lerr != nil {
|
||||
// TODO: study PoST errors
|
||||
return nil, aerrors.Absorb(lerr, 4, "PoST error")
|
||||
}
|
||||
if !ok {
|
||||
return nil, aerrors.New(4, "PoST invalid")
|
||||
}
|
||||
}
|
||||
self.CurrentFaultSet = self.NextFaultSet
|
||||
self.NextFaultSet = types.NewBitField()
|
||||
|
||||
ss, lerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
|
||||
if lerr != nil {
|
||||
return nil, aerrors.Escalate(lerr, "could not load sector set node")
|
||||
}
|
||||
|
||||
if err := ss.BatchDelete(params.DoneSet.All()); err != nil {
|
||||
// TODO: this could fail for system reasons (block not found) or for
|
||||
// bad user input reasons (e.g. bad doneset). The latter should be a
|
||||
// non-fatal error
|
||||
return nil, aerrors.Escalate(err, "failed to delete sectors in done set")
|
||||
}
|
||||
|
||||
self.ProvingSet, lerr = ss.Flush()
|
||||
if lerr != nil {
|
||||
return nil, aerrors.Escalate(lerr, "could not flush AMT")
|
||||
}
|
||||
|
||||
oldPower := self.Power
|
||||
self.Power = types.BigMul(types.NewInt(oldProvingSetSize), mi.SectorSize)
|
||||
self.Power = types.BigMul(types.NewInt(self.ProvingSetSize-uint64(len(faults))),
|
||||
mi.SectorSize)
|
||||
|
||||
enc, err := SerializeParams(&UpdateStorageParams{Delta: types.BigSub(self.Power, oldPower)})
|
||||
if err != nil {
|
||||
@ -325,6 +429,10 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.ProvingSet = self.Sectors
|
||||
self.ProvingSetSize = self.SectorSetSize
|
||||
self.NextDoneSet = params.DoneSet
|
||||
|
||||
c, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -13,6 +13,10 @@ type BitField struct {
|
||||
bits map[uint64]struct{}
|
||||
}
|
||||
|
||||
func NewBitField() BitField {
|
||||
return BitField{bits: make(map[uint64]struct{})}
|
||||
}
|
||||
|
||||
// Set ...s bit in the BitField
|
||||
func (bf BitField) Set(bit uint64) {
|
||||
bf.bits[bit] = struct{}{}
|
||||
@ -29,6 +33,15 @@ func (bf BitField) Has(bit uint64) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// All returns all set bits, in random order
|
||||
func (bf BitField) All() []uint64 {
|
||||
res := make([]uint64, 0, len(bf.bits))
|
||||
for i := range bf.bits {
|
||||
res = append(res, i)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (bf BitField) MarshalCBOR(w io.Writer) error {
|
||||
ints := make([]uint64, 0, len(bf.bits))
|
||||
for i := range bf.bits {
|
||||
|
@ -36,6 +36,7 @@ type VMContext interface {
|
||||
StateTree() (StateTree, aerrors.ActorError)
|
||||
VerifySignature(sig *Signature, from address.Address, data []byte) aerrors.ActorError
|
||||
ChargeGas(uint64) aerrors.ActorError
|
||||
GetRandomness(height uint64) ([]byte, aerrors.ActorError)
|
||||
}
|
||||
|
||||
type storageWrapper struct {
|
||||
|
@ -66,6 +66,15 @@ func (vmc *VMContext) Message() *types.Message {
|
||||
return vmc.msg
|
||||
}
|
||||
|
||||
func (vmc *VMContext) GetRandomness(height uint64) ([]byte, aerrors.ActorError) {
|
||||
relHeight := int(vmc.BlockHeight()) - int(height)
|
||||
res, err := vmc.vm.cs.GetRandomness(vmc.ctx, vmc.vm.cs.GetHeaviestTipSet(), nil, relHeight)
|
||||
if err != nil {
|
||||
return nil, aerrors.Escalate(err, "could not get randomness")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Storage interface
|
||||
|
||||
func (vmc *VMContext) Put(i cbg.CBORMarshaler) (cid.Cid, aerrors.ActorError) {
|
||||
|
41
cli/auth.go
Normal file
41
cli/auth.go
Normal file
@ -0,0 +1,41 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/api"
|
||||
)
|
||||
|
||||
var authCmd = &cli.Command{
|
||||
Name: "auth",
|
||||
Usage: "Manage RPC permissions",
|
||||
Subcommands: []*cli.Command{
|
||||
authCreateAdminToken,
|
||||
},
|
||||
}
|
||||
|
||||
var authCreateAdminToken = &cli.Command{
|
||||
Name: "create-admin-token",
|
||||
Usage: "Create admin token",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
napi, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
// TODO: Probably tell the user how powerful this token is
|
||||
|
||||
token, err := napi.AuthNew(ctx, api.AllPermissions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Log in audit log when it is implemented
|
||||
|
||||
fmt.Println(string(token))
|
||||
return nil
|
||||
},
|
||||
}
|
@ -110,6 +110,7 @@ func ReqContext(cctx *cli.Context) context.Context {
|
||||
}
|
||||
|
||||
var Commands = []*cli.Command{
|
||||
authCmd,
|
||||
chainCmd,
|
||||
clientCmd,
|
||||
createMinerCmd,
|
||||
|
2
go.mod
2
go.mod
@ -6,7 +6,7 @@ require (
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.1.0
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190917221444-2ed85149c65d
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16
|
||||
github.com/filecoin-project/go-bls-sigs v0.0.0-20190718224239-4bc4b8a7bbf8
|
||||
github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543
|
||||
github.com/filecoin-project/go-sectorbuilder v0.0.0-00010101000000-000000000000
|
||||
|
5
go.sum
5
go.sum
@ -68,10 +68,9 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190917010905-40ffeec492ae h1:rSg6wenxKdXby0piY57Vv5gOJR6Eibqq/4PxEk6KjvE=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190917010905-40ffeec492ae/go.mod h1:lKjJYPg2kwbav5f78i5YA8kGccnZn18IySbpneXvaQs=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190917221444-2ed85149c65d h1:fAJ40dcN0kpSFfdTssa1kLxlDImSEZy8e1d7a32tyBY=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190917221444-2ed85149c65d/go.mod h1:lKjJYPg2kwbav5f78i5YA8kGccnZn18IySbpneXvaQs=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16 h1:NzojcJU1VbS6zdLG13JMYis/cQy/MrN3rxmZRq56jKA=
|
||||
github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16/go.mod h1:lKjJYPg2kwbav5f78i5YA8kGccnZn18IySbpneXvaQs=
|
||||
github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543 h1:aMJGfgqe1QDhAVwxRg5fjCRF533xHidiKsugk7Vvzug=
|
||||
github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543/go.mod h1:mjrHv1cDGJWDlGmC0eDc1E5VJr8DmL9XMUcaFwiuKg8=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
|
@ -19,6 +19,8 @@ type StagedSectorMetadata = sectorbuilder.StagedSectorMetadata
|
||||
|
||||
type SortedSectorInfo = sectorbuilder.SortedSectorInfo
|
||||
|
||||
type SectorInfo = sectorbuilder.SectorInfo
|
||||
|
||||
const CommLen = sectorbuilder.CommitmentBytesLen
|
||||
|
||||
type SectorBuilder struct {
|
||||
@ -109,7 +111,10 @@ func VerifyPieceInclusionProof(sectorSize uint64, pieceSize uint64, commP []byte
|
||||
return sectorbuilder.VerifyPieceInclusionProof(sectorSize, pieceSize, commPa, commDa, proof)
|
||||
}
|
||||
|
||||
func VerifyPost(sectorSize uint64, sortedCommRs [][CommLen]byte, challengeSeed [CommLen]byte, proofs [][]byte, faults []uint64) (bool, error) {
|
||||
// sectorbuilder.VerifyPost()
|
||||
panic("no")
|
||||
func NewSortedSectorInfo(sectors []SectorInfo) SortedSectorInfo {
|
||||
return sectorbuilder.NewSortedSectorInfo(sectors...)
|
||||
}
|
||||
|
||||
func VerifyPost(sectorSize uint64, sectorInfo SortedSectorInfo, challengeSeed [CommLen]byte, proof []byte, faults []uint64) (bool, error) {
|
||||
return sectorbuilder.VerifyPoSt(sectorSize, sectorInfo, challengeSeed, proof, faults)
|
||||
}
|
||||
|
102
lotuspond/front/package-lock.json
generated
102
lotuspond/front/package-lock.json
generated
@ -5816,6 +5816,11 @@
|
||||
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
|
||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
||||
},
|
||||
"gud": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||
},
|
||||
"gzip-size": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz",
|
||||
@ -5957,6 +5962,19 @@
|
||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
||||
},
|
||||
"history": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"loose-envify": "^1.2.0",
|
||||
"resolve-pathname": "^3.0.0",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0",
|
||||
"value-equal": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
@ -8378,6 +8396,16 @@
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
|
||||
},
|
||||
"mini-create-react-context": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
|
||||
"integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.0",
|
||||
"gud": "^1.0.0",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"mini-css-extract-plugin": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz",
|
||||
@ -10521,6 +10549,60 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz",
|
||||
"integrity": "sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"history": "^4.9.0",
|
||||
"hoist-non-react-statics": "^3.1.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
"mini-create-react-context": "^0.3.0",
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-is": "^16.6.0",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
|
||||
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
|
||||
"requires": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
|
||||
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
|
||||
"requires": {
|
||||
"isarray": "0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-router-dom": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.1.tgz",
|
||||
"integrity": "sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"history": "^4.9.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-router": "5.0.1",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"react-scripts": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.0.1.tgz",
|
||||
@ -10887,6 +10969,11 @@
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
||||
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
|
||||
},
|
||||
"resolve-pathname": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
|
||||
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
|
||||
},
|
||||
"resolve-url": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||
@ -12083,6 +12170,16 @@
|
||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
||||
},
|
||||
"tiny-invariant": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
|
||||
"integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
|
||||
},
|
||||
"tiny-warning": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
@ -12478,6 +12575,11 @@
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"value-equal": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
||||
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
||||
},
|
||||
"varint": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz",
|
||||
|
@ -11,6 +11,7 @@
|
||||
"react": "^16.8.6",
|
||||
"react-cristal": "^0.0.12",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-scripts": "3.0.1",
|
||||
"rpc-websockets": "^4.5.1",
|
||||
"styled-components": "^3.3.3",
|
||||
|
@ -105,7 +105,7 @@ class Address extends React.Component {
|
||||
}
|
||||
|
||||
addr = <span className={`pondaddr-${this.props.addr}`}
|
||||
onMouseEnter={() => sheet.sheet.insertRule(`.pondaddr-${this.props.addr}, .pondaddr-${this.props.addr} * { color: #11ee11; }`, 0)}
|
||||
onMouseEnter={() => sheet.sheet.insertRule(`.pondaddr-${this.props.addr}, .pondaddr-${this.props.addr} * { color: #11ee11 !important; }`, 0)}
|
||||
onMouseLeave={() => sheet.sheet.deleteRule(0)}
|
||||
>{addr}</span>
|
||||
|
||||
|
@ -1,11 +1,129 @@
|
||||
.Index {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: #1a1a1a;
|
||||
color: #f0f0f0;
|
||||
font-family: monospace;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto 40vw auto;
|
||||
grid-template-rows: auto auto auto 3em;
|
||||
grid-template-areas:
|
||||
". . ."
|
||||
". main ."
|
||||
". . ."
|
||||
"footer footer footer";
|
||||
}
|
||||
|
||||
.Index-footer {
|
||||
background: #2a2a2a;
|
||||
grid-area: footer;
|
||||
}
|
||||
|
||||
.Index-footer > div {
|
||||
padding-left: 0.7em;
|
||||
padding-top: 0.7em;
|
||||
}
|
||||
|
||||
.Index-nodes {
|
||||
grid-area: main;
|
||||
background: #2a2a2a;
|
||||
}
|
||||
|
||||
.Index-node {
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
background: #1f1f1f;
|
||||
}
|
||||
|
||||
.Index-addwrap {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* SingleNode */
|
||||
|
||||
|
||||
.SingleNode-connecting {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background: #1a1a1a;
|
||||
color: #ffffff;
|
||||
font-family: monospace;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto min-content auto;
|
||||
grid-template-rows: auto min-content auto;
|
||||
grid-template-areas:
|
||||
". . ."
|
||||
". main ."
|
||||
". . ."
|
||||
}
|
||||
|
||||
.SingleNode-connecting > div {
|
||||
grid-area: main;
|
||||
padding: 15px;
|
||||
white-space: nowrap;
|
||||
background: #2a2a2a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****/
|
||||
|
||||
a:link {
|
||||
color: #50f020;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #50f020;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #30a00a;
|
||||
}
|
||||
|
||||
.Button {
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
background: #1f1f1f;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.Window {
|
||||
background: #2a2a2a !important;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.Window b {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.Window > :first-child > :nth-child(2)::before {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.Window > :first-child > :nth-child(2)::after {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.Window a:link {
|
||||
color: #30a015;
|
||||
}
|
||||
|
||||
.Window a:visited {
|
||||
color: #30a015;
|
||||
}
|
||||
|
||||
/* POND */
|
||||
|
||||
.App {
|
||||
min-height: 100vh;
|
||||
background: #b7c4cd;
|
||||
background: #1a1a1a;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.NodeList {
|
||||
background: #f9be77;
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 40em;
|
||||
@ -13,7 +131,6 @@
|
||||
}
|
||||
|
||||
.FullNode {
|
||||
background: #f9be77;
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 50em;
|
||||
@ -25,7 +142,6 @@
|
||||
}
|
||||
|
||||
.StorageNode {
|
||||
background: #f9be77;
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 40em;
|
||||
@ -33,7 +149,6 @@
|
||||
}
|
||||
|
||||
.Block {
|
||||
background: #f9be77;
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 50em;
|
||||
@ -41,7 +156,6 @@
|
||||
}
|
||||
|
||||
.State {
|
||||
background: #f9be77;
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 40em;
|
||||
@ -49,7 +163,6 @@
|
||||
}
|
||||
|
||||
.Client {
|
||||
background: #f9be77;
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
display: inline-block;
|
||||
@ -68,19 +181,20 @@
|
||||
|
||||
.ChainExplorer {
|
||||
font-family: monospace;
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
.ChainExplorer-at {
|
||||
min-width: 40em;
|
||||
background: #77ff77;
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
.ChainExplorer-after {
|
||||
background: #cc9c44
|
||||
background: #440000
|
||||
}
|
||||
|
||||
.ChainExplorer-before {
|
||||
background: #cccc00
|
||||
background: #444400
|
||||
}
|
||||
|
||||
.Logs {
|
||||
|
@ -1,53 +1,96 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import { Client } from 'rpc-websockets'
|
||||
import NodeList from "./NodeList";
|
||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||
import Pond from "./Pond";
|
||||
import SingleNode from "./SingleNode";
|
||||
|
||||
class Index extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {rpcUrl: "ws://127.0.0.1:1234/rpc/v0", rpcToken: ''}
|
||||
|
||||
const initialState = JSON.parse(window.localStorage.getItem('saved-nodes'))
|
||||
if (initialState) {
|
||||
this.state.nodes = initialState
|
||||
} else {
|
||||
this.state.nodes = []
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
window.localStorage.setItem('saved-nodes', JSON.stringify(this.state.nodes))
|
||||
}
|
||||
|
||||
onAdd = () => {
|
||||
this.setState({addingNode: true})
|
||||
}
|
||||
|
||||
update = (name) => (e) => this.setState({ [name]: e.target.value })
|
||||
|
||||
tokenOk = () => {
|
||||
let m = this.state.rpcToken.match(/\.(.+)\./)
|
||||
// TODO: eww
|
||||
if(m && atob(m[1]) === '{"Allow":["read","write","sign","admin"]}') {
|
||||
return (
|
||||
<span>-Token OK-
|
||||
<div>
|
||||
<button onClick={this.addNode}>Add Node</button>
|
||||
</div>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return <span>-Expecting valid admin token-</span>
|
||||
}
|
||||
|
||||
addNode = async () => {
|
||||
this.setState(p => ({nodes: [...p.nodes, {addr: this.state.rpcUrl, token: this.state.rpcToken}], addingNode: true}))
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="Index">
|
||||
<div className="Index-nodes">
|
||||
<div>
|
||||
{
|
||||
this.state.nodes.map((node, i) => <div className="Index-node">
|
||||
<span>{i}. {node.addr} <Link to={`/app/node/${i}`}>[OPEN UI]</Link></span>
|
||||
</div>)
|
||||
}
|
||||
</div>
|
||||
<a hidden={this.state.addingNode} href='#' onClick={this.onAdd} className="Button">[Add Node]</a>
|
||||
<div hidden={!this.state.addingNode}>
|
||||
<div>---------------</div>
|
||||
<div>
|
||||
+ RPC:<input defaultValue={"ws://127.0.0.1:1234/rpc/v0"} onChange={this.update("rpcUrl")}/>
|
||||
</div>
|
||||
<div>
|
||||
Token (<code>lotus auth create-admin-token</code>): <input onChange={this.update("rpcToken")}/>{this.tokenOk()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Index-footer">
|
||||
<div>
|
||||
<Link to={"/app/pond"}>Open Pond</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const client = new Client('ws://127.0.0.1:2222/rpc/v0')
|
||||
client.on('open', () => {
|
||||
this.setState(() => ({client: client}))
|
||||
})
|
||||
|
||||
this.state = {
|
||||
windows: {},
|
||||
nextWindow: 0,
|
||||
}
|
||||
|
||||
this.mountWindow = this.mountWindow.bind(this)
|
||||
}
|
||||
|
||||
mountWindow(cb) {
|
||||
const id = this.state.nextWindow
|
||||
this.setState({nextWindow: id + 1})
|
||||
|
||||
const window = cb(() => {
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
||||
})
|
||||
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.client === undefined) {
|
||||
return (
|
||||
<div>
|
||||
Connecting to RPC
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<NodeList client={this.state.client} mountWindow={this.mountWindow}/>
|
||||
<div>
|
||||
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
<Router>
|
||||
<Route path="/" exact component={Index} />
|
||||
<Route path="/app/pond/" component={Pond} />
|
||||
<Route path="/app/node/:node" component={SingleNode} />
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
import Pond from './Pond';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.render(<Pond />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {Cristal} from "react-cristal";
|
||||
import {BlockLinks} from "./BlockLink";
|
||||
import Address from "./Address";
|
||||
import Window from "./Window";
|
||||
|
||||
class Block extends React.Component {
|
||||
constructor(props) {
|
||||
@ -57,9 +57,9 @@ class Block extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
return (<Cristal className="CristalScroll" initialSize={{width: 700, height: 400}} onClose={this.props.onClose} title={`Block ${this.props.cid['/']}`}>
|
||||
return (<Window className="CristalScroll" initialSize={{width: 700, height: 400}} onClose={this.props.onClose} title={`Block ${this.props.cid['/']}`}>
|
||||
{content}
|
||||
</Cristal>)
|
||||
</Window>)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import {Cristal} from "react-cristal";
|
||||
import {BlockLinks} from "./BlockLink";
|
||||
import Window from "./Window";
|
||||
|
||||
const rows = 32
|
||||
|
||||
@ -132,9 +132,9 @@ class ChainExplorer extends React.Component {
|
||||
return <div key={row} className={className}>@{row} {info}</div>
|
||||
})}</div>
|
||||
|
||||
return (<Cristal onClose={this.props.onClose} title={`Chain Explorer ${this.state.follow ? '(Following)' : ''}`}>
|
||||
return (<Window onClose={this.props.onClose} title={`Chain Explorer ${this.state.follow ? '(Following)' : ''}`}>
|
||||
{content}
|
||||
</Cristal>)
|
||||
</Window>)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Cristal from 'react-cristal'
|
||||
import Address from "./Address";
|
||||
import Window from "./Window";
|
||||
|
||||
const dealStates = [
|
||||
"Unknown",
|
||||
@ -70,7 +70,7 @@ class Client extends React.Component {
|
||||
let ppb = Math.round(this.state.total / this.state.blocks * 100) / 100
|
||||
let ppmbb = Math.round(ppb / (this.state.kbs / 1000) * 100) / 100
|
||||
|
||||
let dealMaker = <div>
|
||||
let dealMaker = <div hidden={!this.props.pondClient}>
|
||||
<span>Make Deal: </span>
|
||||
<select><option>t0101</option></select>
|
||||
<abbr title="Data length">L:</abbr> <input placeholder="KBs" defaultValue={1} onChange={this.update("kbs")}/>
|
||||
@ -94,12 +94,12 @@ class Client extends React.Component {
|
||||
|
||||
</div>)
|
||||
|
||||
return <Cristal title={"Client - Node " + this.props.node.ID}>
|
||||
return <Window title={"Client - Node " + this.props.node.ID} onClose={this.props.onClose}>
|
||||
<div className="Client">
|
||||
<div>{dealMaker}</div>
|
||||
<div>{deals}</div>
|
||||
</div>
|
||||
</Cristal>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import Cristal from 'react-cristal'
|
||||
import Window from "./Window";
|
||||
|
||||
async function awaitReducer(prev, c) {
|
||||
return {...await prev, ...await c}
|
||||
@ -107,7 +107,7 @@ class ConnMgr extends React.Component {
|
||||
})
|
||||
|
||||
return(
|
||||
<Cristal title={`Connection Manager${this.state.lock ? ' (syncing)' : ''}`}>
|
||||
<Window title={`Connection Manager${this.state.lock ? ' (syncing)' : ''}`}>
|
||||
<table>
|
||||
<thead><tr><td></td>{keys.slice(0, -1).map((i) => (<td key={i}>{i}</td>))}</tr></thead>
|
||||
<tbody>{rows}</tbody>
|
||||
@ -118,7 +118,7 @@ class ConnMgr extends React.Component {
|
||||
<button onClick={this.connect1}>Conn1</button>
|
||||
<button onClick={this.connectChain}>ConnChain</button>
|
||||
</div>
|
||||
</Cristal>
|
||||
</Window>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import {Cristal} from "react-cristal";
|
||||
import {BlockLinks} from "./BlockLink";
|
||||
import Window from "./Window";
|
||||
|
||||
function styleForHDiff(max, act) {
|
||||
switch (max - act) {
|
||||
@ -42,7 +42,7 @@ class Consensus extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<Cristal title={`Consensus`}>
|
||||
return (<Window title={`Consensus`}>
|
||||
<div className='Consensus'>
|
||||
<div>Max Height: {this.state.maxH}</div>
|
||||
<div>
|
||||
@ -62,7 +62,7 @@ class Consensus extends React.Component {
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Cristal>)
|
||||
</Window>)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import Cristal from 'react-cristal'
|
||||
import { BlockLinks } from "./BlockLink";
|
||||
import StorageNodeInit from "./StorageNodeInit";
|
||||
import Address from "./Address";
|
||||
import ChainExplorer from "./ChainExplorer";
|
||||
import Client from "./Client";
|
||||
import Window from "./Window";
|
||||
|
||||
class FullNode extends React.Component {
|
||||
constructor(props) {
|
||||
@ -121,7 +120,7 @@ class FullNode extends React.Component {
|
||||
miners = this.state.minerList.map((a, k) => <div key={k}><Address miner={true} client={this.props.client} addr={a} mountWindow={this.props.mountWindow}/></div>)
|
||||
}
|
||||
|
||||
let storageMine = <a href="#" onClick={this.startStorageMiner}>[Spawn Storage Miner]</a>
|
||||
let storageMine = <a href="#" onClick={this.startStorageMiner} hidden={!this.props.spawnStorageNode}>[Spawn Storage Miner]</a>
|
||||
|
||||
let addresses = this.state.addrs.map((addr) => {
|
||||
let line = <Address client={this.props.client} add1k={this.add1k} add10k={true} nonce={true} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||
@ -167,10 +166,13 @@ class FullNode extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
let nodeID = this.props.node.ID ? this.props.node.ID : ''
|
||||
let nodePos = this.props.node.ID ? {x: this.props.node.ID*30, y: this.props.node.ID * 30} : 'center'
|
||||
|
||||
return (
|
||||
<Cristal
|
||||
title={"Node " + this.props.node.ID}
|
||||
initialPosition={{x: this.props.node.ID*30, y: this.props.node.ID * 30}}
|
||||
<Window
|
||||
title={"Node " + nodeID}
|
||||
initialPosition={nodePos}
|
||||
initialSize={{width: 690, height: 300}}
|
||||
onClose={this.stop} >
|
||||
<div className="CristalScroll">
|
||||
@ -178,7 +180,7 @@ class FullNode extends React.Component {
|
||||
{runtime}
|
||||
</div>
|
||||
</div>
|
||||
</Cristal>
|
||||
</Window>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import {Cristal} from "react-cristal";
|
||||
import { Terminal } from 'xterm';
|
||||
import { AttachAddon } from "xterm-addon-attach";
|
||||
import 'xterm/dist/xterm.css';
|
||||
import * as fit from 'xterm/lib/addons/fit/fit';
|
||||
import Window from "./Window";
|
||||
|
||||
class Logs extends React.Component {
|
||||
constructor(props) {
|
||||
@ -22,9 +22,9 @@ class Logs extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Cristal className="Logs-window" onClose={this.props.onClose} initialSize={{width: 1000, height: 480}} title={`Node ${this.props.node} Logs`}>
|
||||
return <Window className="Logs-window" onClose={this.props.onClose} initialSize={{width: 1000, height: 480}} title={`Node ${this.props.node} Logs`}>
|
||||
<div ref={this.termRef} className="Logs"/>
|
||||
</Cristal>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,12 @@ import React from 'react';
|
||||
import FullNode from "./FullNode";
|
||||
import ConnMgr from "./ConnMgr";
|
||||
import Consensus from "./Consensus";
|
||||
import {Cristal} from "react-cristal";
|
||||
import StorageNode from "./StorageNode";
|
||||
import {Client} from "rpc-websockets";
|
||||
import pushMessage from "./chain/send";
|
||||
import Logs from "./Logs";
|
||||
import StorageNodeInit from "./StorageNodeInit";
|
||||
import Window from "./Window";
|
||||
|
||||
const [NodeUnknown, NodeRunning, NodeStopped] = [0, 1, 2]
|
||||
|
||||
@ -155,7 +155,7 @@ class NodeList extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<Cristal title={"Node List"} initialPosition="bottom-left">
|
||||
<Window title={"Node List"} initialPosition="bottom-left">
|
||||
<div className={'NodeList'}>
|
||||
<div>
|
||||
<button onClick={this.spawnNode} disabled={!this.state.existingLoaded}>Spawn Node</button>
|
||||
@ -190,7 +190,7 @@ class NodeList extends React.Component {
|
||||
{connMgr}
|
||||
{consensus}
|
||||
</div>
|
||||
</Cristal>
|
||||
</Window>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
55
lotuspond/front/src/Pond.js
Normal file
55
lotuspond/front/src/Pond.js
Normal file
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import { Client } from 'rpc-websockets'
|
||||
import NodeList from "./NodeList";
|
||||
|
||||
|
||||
class Pond extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const client = new Client('ws://127.0.0.1:2222/rpc/v0')
|
||||
client.on('open', () => {
|
||||
this.setState(() => ({client: client}))
|
||||
})
|
||||
|
||||
this.state = {
|
||||
windows: {},
|
||||
nextWindow: 0,
|
||||
}
|
||||
|
||||
this.mountWindow = this.mountWindow.bind(this)
|
||||
}
|
||||
|
||||
mountWindow(cb) {
|
||||
const id = this.state.nextWindow
|
||||
this.setState({nextWindow: id + 1})
|
||||
|
||||
const window = cb(() => {
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
||||
})
|
||||
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.client === undefined) {
|
||||
return (
|
||||
<div>
|
||||
Connecting to RPC
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<NodeList client={this.state.client} mountWindow={this.mountWindow}/>
|
||||
<div>
|
||||
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Pond
|
67
lotuspond/front/src/SingleNode.js
Normal file
67
lotuspond/front/src/SingleNode.js
Normal file
@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import {Client} from "rpc-websockets";
|
||||
import FullNode from "./FullNode";
|
||||
|
||||
class SingleNode extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const nodes = JSON.parse(window.localStorage.getItem('saved-nodes'))
|
||||
const node = nodes[this.props.match.params.node]
|
||||
|
||||
const client = new Client(`${node.addr}?token=${node.token}`)
|
||||
client.on('open', async () => {
|
||||
this.setState(() => ({client: client}))
|
||||
})
|
||||
|
||||
this.state = {
|
||||
windows: {},
|
||||
nextWindow: 0,
|
||||
|
||||
addr: node.addr
|
||||
}
|
||||
}
|
||||
|
||||
mountWindow = (cb) => {
|
||||
const id = this.state.nextWindow
|
||||
this.setState({nextWindow: id + 1})
|
||||
|
||||
const window = cb(() => {
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
||||
})
|
||||
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.client === undefined) {
|
||||
return (
|
||||
<div className="SingleNode-connecting">
|
||||
<div>
|
||||
<div>Connecting to Node RPC:</div>
|
||||
<div>{`${this.state.addr}?token=****`}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
let node = <FullNode
|
||||
node={{Repo: '/i/dont/exist/fixme', ID: ''}}
|
||||
client={this.state.client}
|
||||
give1k={null}
|
||||
mountWindow={this.mountWindow}
|
||||
/>
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
{node}
|
||||
<div>
|
||||
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SingleNode;
|
@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import {Cristal} from "react-cristal";
|
||||
import Window from "./Window";
|
||||
|
||||
class State extends React.Component {
|
||||
constructor(props) {
|
||||
@ -21,9 +21,9 @@ class State extends React.Component {
|
||||
<div>{Object.keys(this.state.State).map(k => <div key={k}>{k}: <span>{JSON.stringify(this.state.State[k])}</span></div>)}</div>
|
||||
</div>
|
||||
|
||||
return <Cristal onClose={this.props.onClose} title={`Actor ${this.props.addr} @{this.props.ts.Height}`}>
|
||||
return <Window onClose={this.props.onClose} title={`Actor ${this.props.addr} @{this.props.ts.Height}`}>
|
||||
{content}
|
||||
</Cristal>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {Cristal} from "react-cristal";
|
||||
import { Client } from 'rpc-websockets'
|
||||
import Address from "./Address";
|
||||
import Window from "./Window";
|
||||
|
||||
const stateConnected = 'connected'
|
||||
const stateConnecting = 'connecting'
|
||||
@ -121,7 +121,7 @@ class StorageNode extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
return <Cristal
|
||||
return <Window
|
||||
title={"Storage Miner Node " + this.props.node.ID}
|
||||
initialPosition={{x: this.props.node.ID*30, y: this.props.node.ID * 30}}
|
||||
onClose={this.stop} >
|
||||
@ -130,7 +130,7 @@ class StorageNode extends React.Component {
|
||||
{runtime}
|
||||
</div>
|
||||
</div>
|
||||
</Cristal>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Cristal} from "react-cristal";
|
||||
import StorageNode from "./StorageNode";
|
||||
import Window from "./Window";
|
||||
|
||||
class StorageNodeInit extends React.Component {
|
||||
async componentDidMount() {
|
||||
@ -11,7 +10,7 @@ class StorageNodeInit extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Cristal
|
||||
return <Window
|
||||
title={"Storage miner initializing"}
|
||||
initialPosition={'center'}>
|
||||
<div className="CristalScroll">
|
||||
@ -19,7 +18,7 @@ class StorageNodeInit extends React.Component {
|
||||
Waiting for init, make sure at least one miner is enabled
|
||||
</div>
|
||||
</div>
|
||||
</Cristal>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
|
15
lotuspond/front/src/Window.js
Normal file
15
lotuspond/front/src/Window.js
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
import {Cristal} from "react-cristal";
|
||||
|
||||
class Window extends React.Component {
|
||||
render() {
|
||||
let props = {className: '', ...this.props}
|
||||
props.className = `${props.className} Window`
|
||||
|
||||
return <Cristal {...props}>
|
||||
{this.props.children}
|
||||
</Cristal>
|
||||
}
|
||||
}
|
||||
|
||||
export default Window
|
@ -56,6 +56,23 @@ func (ct *Tracker) TrackCommitSectorMsg(miner address.Address, sectorId uint64,
|
||||
tracking, err := ct.commitDs.Get(key)
|
||||
switch err {
|
||||
case datastore.ErrNotFound:
|
||||
comm := &commitment{Msg: mcid}
|
||||
commB, err := cbor.DumpObject(comm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ct.commitDs.Put(key, commB); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
waits, ok := ct.waits[key]
|
||||
if ok {
|
||||
close(waits)
|
||||
delete(ct.waits, key)
|
||||
}
|
||||
return nil
|
||||
case nil:
|
||||
var comm commitment
|
||||
if err := cbor.DecodeInto(tracking, &comm); err != nil {
|
||||
return err
|
||||
@ -67,29 +84,9 @@ func (ct *Tracker) TrackCommitSectorMsg(miner address.Address, sectorId uint64,
|
||||
|
||||
log.Warnf("commitment.TrackCommitSectorMsg called more than once for miner %s, sector %d, message %s", miner, sectorId, mcid)
|
||||
return nil
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
comm := &commitment{Msg: mcid}
|
||||
commB, err := cbor.DumpObject(comm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ct.commitDs.Put(key, commB); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
waits, ok := ct.waits[key]
|
||||
if ok {
|
||||
close(waits)
|
||||
delete(ct.waits, key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ct *Tracker) WaitCommit(ctx context.Context, miner address.Address, sectorId uint64) (cid.Cid, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user