Compare commits
54
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eb42725d9 | ||
|
|
a7cee99968 | ||
|
|
bc805a1d86 | ||
|
|
967d8d91d6 | ||
|
|
4a057d84b8 | ||
|
|
111942b50b | ||
|
|
40e34b334a | ||
|
|
1a049a4f8e | ||
|
|
d0e4150cea | ||
|
|
0775c6c9d5 | ||
|
|
2fd4a97698 | ||
|
|
89f46cb5cc | ||
|
|
6f86b95f62 | ||
|
|
a71fbee2d1 | ||
|
|
99c07703f8 | ||
|
|
af1a422ddd | ||
|
|
8351420404 | ||
|
|
7826cc0bab | ||
|
|
cb801d47c7 | ||
|
|
726eacbe03 | ||
|
|
17fcf30edd | ||
|
|
eae7d62a2b | ||
|
|
5bee85d57a | ||
|
|
0b7dc6971d | ||
|
|
8c7d2efe72 | ||
|
|
a931ff94e9 | ||
|
|
c56ef260d5 | ||
|
|
955d7f9c54 | ||
|
|
1fb9ed1c9a | ||
|
|
7e6ede7563 | ||
|
|
4e659e30c5 | ||
|
|
f5d48b0e86 | ||
|
|
f74852bd73 | ||
|
|
9c99171cb8 | ||
|
|
90d2a92a2d | ||
|
|
2818353418 | ||
|
|
ccc780f35a | ||
|
|
fdb115e369 | ||
|
|
3bd2e2f777 | ||
|
|
6d771d29c7 | ||
|
|
b29e3b5242 | ||
|
|
8340124786 | ||
|
|
8f98c7d960 | ||
|
|
3fb5334959 | ||
|
|
a40fc98e56 | ||
|
|
12f36c5bda | ||
|
|
45cd510da1 | ||
|
|
3d80c38064 | ||
|
|
98c1aa7988 | ||
|
|
04dda0ce21 | ||
|
|
6c58ce7bee | ||
|
|
6addbc6b68 | ||
|
|
ca67bb06a3 | ||
|
|
75450a2ab5 |
+5
-1
@@ -1,5 +1,9 @@
|
||||
comment: off
|
||||
ignore:
|
||||
- "cbor_gen.go"
|
||||
- "**/cbor_gen.go"
|
||||
- "api/test/**/*"
|
||||
- "api/test/*"
|
||||
- "gen/**/*"
|
||||
- "gen/*"
|
||||
github_checks:
|
||||
annotations: false
|
||||
|
||||
+10
-5
@@ -315,19 +315,20 @@ type FullNode interface {
|
||||
|
||||
// MethodGroup: State
|
||||
// The State methods are used to query, inspect, and interact with chain state.
|
||||
// All methods take a TipSetKey as a parameter. The state looked up is the state at that tipset.
|
||||
// Most methods take a TipSetKey as a parameter. The state looked up is the state at that tipset.
|
||||
// A nil TipSetKey can be provided as a param, this will cause the heaviest tipset in the chain to be used.
|
||||
|
||||
// StateCall runs the given message and returns its result without any persisted changes.
|
||||
StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error)
|
||||
// StateReplay returns the result of executing the indicated message, assuming it was executed in the indicated tipset.
|
||||
// StateReplay replays a given message, assuming it was included in a block in the specified tipset.
|
||||
// If no tipset key is provided, the appropriate tipset is looked up.
|
||||
StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error)
|
||||
// StateGetActor returns the indicated actor's nonce and balance.
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error)
|
||||
// StateReadState returns the indicated actor's state.
|
||||
StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*ActorState, error)
|
||||
// StateListMessages looks back and returns all messages with a matching to or from address, stopping at the given height.
|
||||
StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error)
|
||||
StateListMessages(ctx context.Context, match *MessageMatch, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error)
|
||||
|
||||
// StateNetworkName returns the name of the network the node is synced to
|
||||
StateNetworkName(context.Context) (dtypes.NetworkName, error)
|
||||
@@ -370,8 +371,6 @@ type FullNode interface {
|
||||
StateSectorPartition(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok types.TipSetKey) (*miner.SectorLocation, error)
|
||||
// StateSearchMsg searches for a message in the chain, and returns its receipt and the tipset where it was executed
|
||||
StateSearchMsg(context.Context, cid.Cid) (*MsgLookup, error)
|
||||
// StateMsgGasCost searches for a message in the chain, and returns details of the messages gas costs, including the penalty and miner tip
|
||||
StateMsgGasCost(context.Context, cid.Cid, types.TipSetKey) (*MsgGasCost, error)
|
||||
// StateWaitMsg looks back in the chain for a message. If not found, it blocks until the
|
||||
// message arrives on chain, and gets to the indicated confidence depth.
|
||||
StateWaitMsg(ctx context.Context, cid cid.Cid, confidence uint64) (*MsgLookup, error)
|
||||
@@ -740,6 +739,7 @@ type InvocResult struct {
|
||||
MsgCid cid.Cid
|
||||
Msg *types.Message
|
||||
MsgRct *types.MessageReceipt
|
||||
GasCost MsgGasCost
|
||||
ExecutionTrace types.ExecutionTrace
|
||||
Error string
|
||||
Duration time.Duration
|
||||
@@ -918,3 +918,8 @@ type MsigVesting struct {
|
||||
StartEpoch abi.ChainEpoch
|
||||
UnlockDuration abi.ChainEpoch
|
||||
}
|
||||
|
||||
type MessageMatch struct {
|
||||
To address.Address
|
||||
From address.Address
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import (
|
||||
)
|
||||
|
||||
type GatewayAPI interface {
|
||||
ChainHasObj(context.Context, cid.Cid) (bool, error)
|
||||
ChainHead(ctx context.Context) (*types.TipSet, error)
|
||||
ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *MessageSendSpec, tsk types.TipSetKey) (*types.Message, error)
|
||||
MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error)
|
||||
MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)
|
||||
|
||||
+12
-7
@@ -191,7 +191,6 @@ type FullNodeStruct struct {
|
||||
StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"`
|
||||
StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"`
|
||||
StateReadState func(context.Context, address.Address, types.TipSetKey) (*api.ActorState, error) `perm:"read"`
|
||||
StateMsgGasCost func(context.Context, cid.Cid, types.TipSetKey) (*api.MsgGasCost, error) `perm:"read"`
|
||||
StateWaitMsg func(ctx context.Context, cid cid.Cid, confidence uint64) (*api.MsgLookup, error) `perm:"read"`
|
||||
StateWaitMsgLimited func(context.Context, cid.Cid, uint64, abi.ChainEpoch) (*api.MsgLookup, error) `perm:"read"`
|
||||
StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"`
|
||||
@@ -206,7 +205,7 @@ type FullNodeStruct struct {
|
||||
StateChangedActors func(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error) `perm:"read"`
|
||||
StateGetReceipt func(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateMinerSectorCount func(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) `perm:"read"`
|
||||
StateListMessages func(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"`
|
||||
StateListMessages func(ctx context.Context, match *api.MessageMatch, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"`
|
||||
StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"`
|
||||
StateVerifierStatus func(context.Context, address.Address, types.TipSetKey) (*abi.StoragePower, error) `perm:"read"`
|
||||
StateVerifiedClientStatus func(context.Context, address.Address, types.TipSetKey) (*abi.StoragePower, error) `perm:"read"`
|
||||
@@ -371,9 +370,11 @@ type WorkerStruct struct {
|
||||
type GatewayStruct struct {
|
||||
Internal struct {
|
||||
// TODO: does the gateway need perms?
|
||||
ChainHasObj func(context.Context, cid.Cid) (bool, error)
|
||||
ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight func(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainHead func(ctx context.Context) (*types.TipSet, error)
|
||||
ChainReadObj func(context.Context, cid.Cid) ([]byte, error)
|
||||
GasEstimateMessageGas func(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error)
|
||||
MpoolPush func(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error)
|
||||
MsigGetAvailableBalance func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)
|
||||
@@ -890,10 +891,6 @@ func (c *FullNodeStruct) StateReadState(ctx context.Context, addr address.Addres
|
||||
return c.Internal.StateReadState(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMsgGasCost(ctx context.Context, msgc cid.Cid, tsk types.TipSetKey) (*api.MsgGasCost, error) {
|
||||
return c.Internal.StateMsgGasCost(ctx, msgc, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid, confidence uint64) (*api.MsgLookup, error) {
|
||||
return c.Internal.StateWaitMsg(ctx, msgc, confidence)
|
||||
}
|
||||
@@ -946,7 +943,7 @@ func (c *FullNodeStruct) StateGetReceipt(ctx context.Context, msg cid.Cid, tsk t
|
||||
return c.Internal.StateGetReceipt(ctx, msg, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) {
|
||||
func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *api.MessageMatch, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) {
|
||||
return c.Internal.StateListMessages(ctx, match, tsk, toht)
|
||||
}
|
||||
|
||||
@@ -1432,6 +1429,10 @@ func (w *WorkerStruct) Closing(ctx context.Context) (<-chan struct{}, error) {
|
||||
return w.Internal.Closing(ctx)
|
||||
}
|
||||
|
||||
func (g GatewayStruct) ChainHasObj(ctx context.Context, c cid.Cid) (bool, error) {
|
||||
return g.Internal.ChainHasObj(ctx, c)
|
||||
}
|
||||
|
||||
func (g GatewayStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
return g.Internal.ChainHead(ctx)
|
||||
}
|
||||
@@ -1444,6 +1445,10 @@ func (g GatewayStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEp
|
||||
return g.Internal.ChainGetTipSetByHeight(ctx, h, tsk)
|
||||
}
|
||||
|
||||
func (g GatewayStruct) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
|
||||
return g.Internal.ChainReadObj(ctx, c)
|
||||
}
|
||||
|
||||
func (g GatewayStruct) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
|
||||
return g.Internal.GasEstimateMessageGas(ctx, msg, spec, tsk)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,2 @@
|
||||
/dns4/bootstrap-0.testnet.fildev.network/tcp/1347/p2p/12D3KooWJTUBUjtzWJGWU1XSiY21CwmHaCNLNYn2E7jqHEHyZaP7
|
||||
/dns4/bootstrap-1.testnet.fildev.network/tcp/1347/p2p/12D3KooW9yeKXha4hdrJKq74zEo99T8DhriQdWNoojWnnQbsgB3v
|
||||
/dns4/bootstrap-2.testnet.fildev.network/tcp/1347/p2p/12D3KooWCrx8yVG9U9Kf7w8KLN3Edkj5ZKDhgCaeMqQbcQUoB6CT
|
||||
/dns4/bootstrap-4.testnet.fildev.network/tcp/1347/p2p/12D3KooWPkL9LrKRQgHtq7kn9ecNhGU9QaziG8R5tX8v9v7t3h34
|
||||
/dns4/bootstrap-3.testnet.fildev.network/tcp/1347/p2p/12D3KooWKYSsbpgZ3HAjax5M1BXCwXLa6gVkUARciz7uN3FNtr7T
|
||||
/dns4/bootstrap-5.testnet.fildev.network/tcp/1347/p2p/12D3KooWQYzqnLASJAabyMpPb1GcWZvNSe7JDcRuhdRqonFoiK9W
|
||||
/dns4/lotus-bootstrap.forceup.cn/tcp/41778/p2p/12D3KooWFQsv3nRMUevZNWWsY1Wu6NUzUbawnWU5NcRhgKuJA37C
|
||||
/dns4/bootstrap-0.starpool.in/tcp/12757/p2p/12D3KooWGHpBMeZbestVEWkfdnC9u7p6uFHXL1n7m1ZBqsEmiUzz
|
||||
/dns4/bootstrap-1.starpool.in/tcp/12757/p2p/12D3KooWQZrGH1PxSNZPum99M1zNvjNFM33d1AAu5DcvdHptuU7u
|
||||
/dns4/node.glif.io/tcp/1235/p2p/12D3KooWBF8cpp65hp2u9LK5mh19x67ftAam84z9LsfaquTDSBpt
|
||||
/dns4/bootstrap-0.ipfsmain.cn/tcp/34721/p2p/12D3KooWQnwEGNqcM2nAcPtRR9rAX8Hrg4k9kJLCHoTR5chJfz6d
|
||||
/dns4/bootstrap-1.ipfsmain.cn/tcp/34723/p2p/12D3KooWMKxMkD5DMpSWsW7dBddKxKT7L2GgbNuckz9otxvkvByP
|
||||
/dns4/bootstrap-0.butterfly.fildev.network/tcp/1347/p2p/12D3KooWLyXZeuQ9PpVYxuEfXVvKBx1DaozLHoDFsg4ZHMbspMmb
|
||||
/dns4/bootstrap-1.butterfly.fildev.network/tcp/1347/p2p/12D3KooWKP5M226HZWu7R3RwqFB6YoN6kLeuZwFbQWeugEmwjcgQ
|
||||
|
||||
Binary file not shown.
@@ -22,6 +22,8 @@ const UpgradeTapeHeight = -4
|
||||
var UpgradeActorsV2Height = abi.ChainEpoch(10)
|
||||
var UpgradeLiftoffHeight = abi.ChainEpoch(-5)
|
||||
|
||||
const UpgradePostLiftoffHeight = -6
|
||||
|
||||
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
|
||||
0: DrandMainnet,
|
||||
}
|
||||
|
||||
+13
-17
@@ -16,43 +16,39 @@ import (
|
||||
)
|
||||
|
||||
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
|
||||
0: DrandIncentinet,
|
||||
UpgradeSmokeHeight: DrandMainnet,
|
||||
0: DrandMainnet,
|
||||
}
|
||||
|
||||
const UpgradeBreezeHeight = 41280
|
||||
const UpgradeBreezeHeight = -1
|
||||
const BreezeGasTampingDuration = 120
|
||||
|
||||
const UpgradeSmokeHeight = 51000
|
||||
const UpgradeSmokeHeight = -2
|
||||
|
||||
const UpgradeIgnitionHeight = 94000
|
||||
const UpgradeRefuelHeight = 130800
|
||||
const UpgradeIgnitionHeight = -3
|
||||
const UpgradeRefuelHeight = -4
|
||||
|
||||
var UpgradeActorsV2Height = abi.ChainEpoch(138720)
|
||||
var UpgradeActorsV2Height = abi.ChainEpoch(30)
|
||||
|
||||
const UpgradeTapeHeight = 140760
|
||||
const UpgradeTapeHeight = 60
|
||||
|
||||
// This signals our tentative epoch for mainnet launch. Can make it later, but not earlier.
|
||||
// Miners, clients, developers, custodians all need time to prepare.
|
||||
// We still have upgrades and state changes to do, but can happen after signaling timing here.
|
||||
const UpgradeLiftoffHeight = 148888
|
||||
const UpgradeLiftoffHeight = 90
|
||||
const UpgradePostLiftoffHeight = 120
|
||||
|
||||
func init() {
|
||||
policy.SetConsensusMinerMinPower(abi.NewStoragePower(10 << 40))
|
||||
policy.SetConsensusMinerMinPower(abi.NewStoragePower(1 << 30))
|
||||
policy.SetSupportedProofTypes(
|
||||
abi.RegisteredSealProof_StackedDrg512MiBV1,
|
||||
abi.RegisteredSealProof_StackedDrg32GiBV1,
|
||||
abi.RegisteredSealProof_StackedDrg64GiBV1,
|
||||
)
|
||||
|
||||
if os.Getenv("LOTUS_USE_TEST_ADDRESSES") != "1" {
|
||||
SetAddressNetwork(address.Mainnet)
|
||||
}
|
||||
SetAddressNetwork(address.Testnet)
|
||||
|
||||
if os.Getenv("LOTUS_DISABLE_V2_ACTOR_MIGRATION") == "1" {
|
||||
UpgradeActorsV2Height = math.MaxInt64
|
||||
}
|
||||
|
||||
Devnet = false
|
||||
Devnet = true
|
||||
}
|
||||
|
||||
const BlockDelaySecs = uint64(builtin0.EpochDurationSeconds)
|
||||
|
||||
@@ -25,7 +25,7 @@ const UnixfsLinksPerLevel = 1024
|
||||
// Consensus / Network
|
||||
|
||||
const AllowableClockDriftSecs = uint64(1)
|
||||
const NewestNetworkVersion = network.Version5
|
||||
const NewestNetworkVersion = network.Version6
|
||||
const ActorUpgradeNetworkVersion = network.Version4
|
||||
|
||||
// Epochs
|
||||
|
||||
@@ -80,12 +80,13 @@ var (
|
||||
UpgradeBreezeHeight abi.ChainEpoch = -1
|
||||
BreezeGasTampingDuration abi.ChainEpoch = 0
|
||||
|
||||
UpgradeSmokeHeight abi.ChainEpoch = -1
|
||||
UpgradeIgnitionHeight abi.ChainEpoch = -2
|
||||
UpgradeRefuelHeight abi.ChainEpoch = -3
|
||||
UpgradeTapeHeight abi.ChainEpoch = -4
|
||||
UpgradeActorsV2Height abi.ChainEpoch = 10
|
||||
UpgradeLiftoffHeight abi.ChainEpoch = -5
|
||||
UpgradeSmokeHeight abi.ChainEpoch = -1
|
||||
UpgradeIgnitionHeight abi.ChainEpoch = -2
|
||||
UpgradeRefuelHeight abi.ChainEpoch = -3
|
||||
UpgradeTapeHeight abi.ChainEpoch = -4
|
||||
UpgradeActorsV2Height abi.ChainEpoch = 10
|
||||
UpgradeLiftoffHeight abi.ChainEpoch = -5
|
||||
UpgradePostLiftoffHeight abi.ChainEpoch = -6
|
||||
|
||||
DrandSchedule = map[abi.ChainEpoch]DrandEnum{
|
||||
0: DrandMainnet,
|
||||
|
||||
@@ -370,11 +370,23 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journ
|
||||
return err
|
||||
})
|
||||
|
||||
if err := mp.loadLocal(); err != nil {
|
||||
log.Errorf("loading local messages: %+v", err)
|
||||
}
|
||||
mp.curTsLk.Lock()
|
||||
mp.lk.Lock()
|
||||
|
||||
go mp.runLoop()
|
||||
go func() {
|
||||
err := mp.loadLocal()
|
||||
|
||||
mp.lk.Unlock()
|
||||
mp.curTsLk.Unlock()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("loading local messages: %+v", err)
|
||||
}
|
||||
|
||||
log.Info("mpool ready")
|
||||
|
||||
mp.runLoop()
|
||||
}()
|
||||
|
||||
return mp, nil
|
||||
}
|
||||
@@ -667,9 +679,6 @@ func (mp *MessagePool) addLoaded(m *types.SignedMessage) error {
|
||||
return err
|
||||
}
|
||||
|
||||
mp.curTsLk.Lock()
|
||||
defer mp.curTsLk.Unlock()
|
||||
|
||||
curTs := mp.curTs
|
||||
|
||||
if curTs == nil {
|
||||
@@ -685,9 +694,6 @@ func (mp *MessagePool) addLoaded(m *types.SignedMessage) error {
|
||||
return xerrors.Errorf("minimum expected nonce is %d: %w", snonce, ErrNonceTooLow)
|
||||
}
|
||||
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
|
||||
_, err = mp.verifyMsgBeforeAdd(m, curTs, true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -232,6 +232,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
||||
MsgCid: msg.Cid(),
|
||||
Msg: msg,
|
||||
MsgRct: &ret.MessageReceipt,
|
||||
GasCost: MakeMsgGasCost(msg, ret),
|
||||
ExecutionTrace: ret.ExecutionTrace,
|
||||
Error: errs,
|
||||
Duration: ret.Duration,
|
||||
|
||||
@@ -86,6 +86,10 @@ func DefaultUpgradeSchedule() UpgradeSchedule {
|
||||
Height: build.UpgradeLiftoffHeight,
|
||||
Network: network.Version5,
|
||||
Migration: UpgradeLiftoff,
|
||||
}, {
|
||||
Height: build.UpgradePostLiftoffHeight,
|
||||
Network: network.Version6,
|
||||
Migration: nil,
|
||||
}}
|
||||
|
||||
if build.UpgradeActorsV2Height == math.MaxInt64 { // disable actors upgrade
|
||||
@@ -498,7 +502,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
|
||||
Subcalls: subcalls,
|
||||
},
|
||||
Duration: 0,
|
||||
GasCosts: vm.ZeroGasOutputs(),
|
||||
GasCosts: nil,
|
||||
}); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("recording transfers: %w", err)
|
||||
}
|
||||
@@ -799,7 +803,7 @@ func splitGenesisMultisig(ctx context.Context, cb ExecCallback, addr address.Add
|
||||
Subcalls: subcalls,
|
||||
},
|
||||
Duration: 0,
|
||||
GasCosts: vm.ZeroGasOutputs(),
|
||||
GasCosts: nil,
|
||||
}); err != nil {
|
||||
return xerrors.Errorf("recording transfers: %w", err)
|
||||
}
|
||||
|
||||
@@ -209,6 +209,9 @@ func traceFunc(trace *[]*api.InvocResult) func(mcid cid.Cid, msg *types.Message,
|
||||
if ret.ActorErr != nil {
|
||||
ir.Error = ret.ActorErr.Error()
|
||||
}
|
||||
if ret.GasCosts != nil {
|
||||
ir.GasCost = MakeMsgGasCost(msg, ret)
|
||||
}
|
||||
*trace = append(*trace, ir)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -701,3 +701,16 @@ func CheckTotalFIL(ctx context.Context, sm *StateManager, ts *types.TipSet) (abi
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func MakeMsgGasCost(msg *types.Message, ret *vm.ApplyRet) api.MsgGasCost {
|
||||
return api.MsgGasCost{
|
||||
Message: msg.Cid(),
|
||||
GasUsed: big.NewInt(ret.GasUsed),
|
||||
BaseFeeBurn: ret.GasCosts.BaseFeeBurn,
|
||||
OverEstimationBurn: ret.GasCosts.OverEstimationBurn,
|
||||
MinerPenalty: ret.GasCosts.MinerPenalty,
|
||||
MinerTip: ret.GasCosts.MinerTip,
|
||||
Refund: ret.GasCosts.Refund,
|
||||
TotalCost: big.Sub(msg.RequiredFunds(), ret.GasCosts.Refund),
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -214,7 +214,7 @@ type ApplyRet struct {
|
||||
ActorErr aerrors.ActorError
|
||||
ExecutionTrace types.ExecutionTrace
|
||||
Duration time.Duration
|
||||
GasCosts GasOutputs
|
||||
GasCosts *GasOutputs
|
||||
}
|
||||
|
||||
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
||||
@@ -361,7 +361,7 @@ func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*Ap
|
||||
},
|
||||
ActorErr: actorErr,
|
||||
ExecutionTrace: rt.executionTrace,
|
||||
GasCosts: GasOutputs{},
|
||||
GasCosts: nil,
|
||||
Duration: time.Since(start),
|
||||
}, actorErr
|
||||
}
|
||||
@@ -397,7 +397,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
ExitCode: exitcode.SysErrOutOfGas,
|
||||
GasUsed: 0,
|
||||
},
|
||||
GasCosts: gasOutputs,
|
||||
GasCosts: &gasOutputs,
|
||||
Duration: time.Since(start),
|
||||
}, nil
|
||||
}
|
||||
@@ -417,7 +417,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
GasUsed: 0,
|
||||
},
|
||||
ActorErr: aerrors.Newf(exitcode.SysErrSenderInvalid, "actor not found: %s", msg.From),
|
||||
GasCosts: gasOutputs,
|
||||
GasCosts: &gasOutputs,
|
||||
Duration: time.Since(start),
|
||||
}, nil
|
||||
}
|
||||
@@ -434,7 +434,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
GasUsed: 0,
|
||||
},
|
||||
ActorErr: aerrors.Newf(exitcode.SysErrSenderInvalid, "send from not account actor: %s", fromActor.Code),
|
||||
GasCosts: gasOutputs,
|
||||
GasCosts: &gasOutputs,
|
||||
Duration: time.Since(start),
|
||||
}, nil
|
||||
}
|
||||
@@ -450,7 +450,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
ActorErr: aerrors.Newf(exitcode.SysErrSenderStateInvalid,
|
||||
"actor nonce invalid: msg:%d != state:%d", msg.Nonce, fromActor.Nonce),
|
||||
|
||||
GasCosts: gasOutputs,
|
||||
GasCosts: &gasOutputs,
|
||||
Duration: time.Since(start),
|
||||
}, nil
|
||||
}
|
||||
@@ -466,7 +466,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
},
|
||||
ActorErr: aerrors.Newf(exitcode.SysErrSenderStateInvalid,
|
||||
"actor balance less than needed: %s < %s", types.FIL(fromActor.Balance), types.FIL(gascost)),
|
||||
GasCosts: gasOutputs,
|
||||
GasCosts: &gasOutputs,
|
||||
Duration: time.Since(start),
|
||||
}, nil
|
||||
}
|
||||
@@ -560,7 +560,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
},
|
||||
ActorErr: actorErr,
|
||||
ExecutionTrace: rt.executionTrace,
|
||||
GasCosts: gasOutputs,
|
||||
GasCosts: &gasOutputs,
|
||||
Duration: time.Since(start),
|
||||
}, nil
|
||||
}
|
||||
|
||||
+165
-70
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -24,6 +23,7 @@ import (
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -41,6 +41,13 @@ import (
|
||||
var multisigCmd = &cli.Command{
|
||||
Name: "msig",
|
||||
Usage: "Interact with a multisig wallet",
|
||||
Flags: []cli.Flag{
|
||||
&cli.IntFlag{
|
||||
Name: "confidence",
|
||||
Usage: "number of block confirmations to wait for",
|
||||
Value: int(build.MessageConfidence),
|
||||
},
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
msigCreateCmd,
|
||||
msigInspectCmd,
|
||||
@@ -57,6 +64,7 @@ var multisigCmd = &cli.Command{
|
||||
msigLockApproveCmd,
|
||||
msigLockCancelCmd,
|
||||
msigVestedCmd,
|
||||
msigProposeThresholdCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -146,14 +154,14 @@ var msigCreateCmd = &cli.Command{
|
||||
}
|
||||
|
||||
// wait for it to get mined into a block
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check it executed successfully
|
||||
if wait.Receipt.ExitCode != 0 {
|
||||
fmt.Println("actor creation failed!")
|
||||
fmt.Fprintln(cctx.App.Writer, "actor creation failed!")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -163,7 +171,7 @@ var msigCreateCmd = &cli.Command{
|
||||
if err := execreturn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Created new multisig: ", execreturn.IDAddress, execreturn.RobustAddress)
|
||||
fmt.Fprintln(cctx.App.Writer, "Created new multisig: ", execreturn.IDAddress, execreturn.RobustAddress)
|
||||
|
||||
// TODO: maybe register this somewhere
|
||||
return nil
|
||||
@@ -227,25 +235,25 @@ var msigInspectCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Balance: %s\n", types.FIL(act.Balance))
|
||||
fmt.Printf("Spendable: %s\n", types.FIL(types.BigSub(act.Balance, locked)))
|
||||
fmt.Fprintf(cctx.App.Writer, "Balance: %s\n", types.FIL(act.Balance))
|
||||
fmt.Fprintf(cctx.App.Writer, "Spendable: %s\n", types.FIL(types.BigSub(act.Balance, locked)))
|
||||
|
||||
if cctx.Bool("vesting") {
|
||||
ib, err := mstate.InitialBalance()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("InitialBalance: %s\n", types.FIL(ib))
|
||||
fmt.Fprintf(cctx.App.Writer, "InitialBalance: %s\n", types.FIL(ib))
|
||||
se, err := mstate.StartEpoch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("StartEpoch: %d\n", se)
|
||||
fmt.Fprintf(cctx.App.Writer, "StartEpoch: %d\n", se)
|
||||
ud, err := mstate.UnlockDuration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("UnlockDuration: %d\n", ud)
|
||||
fmt.Fprintf(cctx.App.Writer, "UnlockDuration: %d\n", ud)
|
||||
}
|
||||
|
||||
signers, err := mstate.Signers()
|
||||
@@ -256,10 +264,10 @@ var msigInspectCmd = &cli.Command{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Threshold: %d / %d\n", threshold, len(signers))
|
||||
fmt.Println("Signers:")
|
||||
fmt.Fprintf(cctx.App.Writer, "Threshold: %d / %d\n", threshold, len(signers))
|
||||
fmt.Fprintln(cctx.App.Writer, "Signers:")
|
||||
for _, s := range signers {
|
||||
fmt.Printf("\t%s\n", s)
|
||||
fmt.Fprintf(cctx.App.Writer, "\t%s\n", s)
|
||||
}
|
||||
|
||||
pending := make(map[int64]multisig.Transaction)
|
||||
@@ -271,7 +279,7 @@ var msigInspectCmd = &cli.Command{
|
||||
}
|
||||
|
||||
decParams := cctx.Bool("decode-params")
|
||||
fmt.Println("Transactions: ", len(pending))
|
||||
fmt.Fprintln(cctx.App.Writer, "Transactions: ", len(pending))
|
||||
if len(pending) > 0 {
|
||||
var txids []int64
|
||||
for txid := range pending {
|
||||
@@ -281,7 +289,7 @@ var msigInspectCmd = &cli.Command{
|
||||
return txids[i] < txids[j]
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 8, 4, 2, ' ', 0)
|
||||
w := tabwriter.NewWriter(cctx.App.Writer, 8, 4, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "ID\tState\tApprovals\tTo\tValue\tMethod\tParams\n")
|
||||
for _, txid := range txids {
|
||||
tx := pending[txid]
|
||||
@@ -410,7 +418,7 @@ var msigProposeCmd = &cli.Command{
|
||||
|
||||
fmt.Println("send proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -446,14 +454,18 @@ var msigApproveCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() < 5 {
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, message ID, proposer address, destination, and value"))
|
||||
if cctx.Args().Len() < 2 {
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass at least multisig address and message ID"))
|
||||
}
|
||||
|
||||
if cctx.Args().Len() > 5 && cctx.Args().Len() != 7 {
|
||||
return ShowHelp(cctx, fmt.Errorf("usage: msig approve <msig addr> <message ID> <proposer address> <desination> <value> [ <method> <params> ]"))
|
||||
}
|
||||
|
||||
if cctx.Args().Len() > 2 && cctx.Args().Len() != 5 {
|
||||
return ShowHelp(cctx, fmt.Errorf("usage: msig approve <msig addr> <message ID> <proposer address> <desination> <value>"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -471,44 +483,6 @@ var msigApproveCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
proposer, err := address.NewFromString(cctx.Args().Get(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if proposer.Protocol() != address.ID {
|
||||
proposer, err = api.StateLookupID(ctx, proposer, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dest, err := address.NewFromString(cctx.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := types.ParseFIL(cctx.Args().Get(4))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var method uint64
|
||||
var params []byte
|
||||
if cctx.Args().Len() == 7 {
|
||||
m, err := strconv.ParseUint(cctx.Args().Get(5), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
method = m
|
||||
|
||||
p, err := hex.DecodeString(cctx.Args().Get(6))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params = p
|
||||
}
|
||||
|
||||
var from address.Address
|
||||
if cctx.IsSet("from") {
|
||||
f, err := address.NewFromString(cctx.String("from"))
|
||||
@@ -524,14 +498,60 @@ var msigApproveCmd = &cli.Command{
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params)
|
||||
if err != nil {
|
||||
return err
|
||||
var msgCid cid.Cid
|
||||
if cctx.Args().Len() == 2 {
|
||||
msgCid, err = api.MsigApprove(ctx, msig, txid, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
proposer, err := address.NewFromString(cctx.Args().Get(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if proposer.Protocol() != address.ID {
|
||||
proposer, err = api.StateLookupID(ctx, proposer, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dest, err := address.NewFromString(cctx.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := types.ParseFIL(cctx.Args().Get(4))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var method uint64
|
||||
var params []byte
|
||||
if cctx.Args().Len() == 7 {
|
||||
m, err := strconv.ParseUint(cctx.Args().Get(5), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
method = m
|
||||
|
||||
p, err := hex.DecodeString(cctx.Args().Get(6))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params = p
|
||||
}
|
||||
|
||||
msgCid, err = api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("sent approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -602,7 +622,7 @@ var msigRemoveProposeCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent remove proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -678,9 +698,9 @@ var msigAddProposeCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("sent add proposal in message: ", msgCid)
|
||||
fmt.Fprintln(cctx.App.Writer, "sent add proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -762,7 +782,7 @@ var msigAddApproveCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent add approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -839,7 +859,7 @@ var msigAddCancelCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent add cancellation in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -911,7 +931,7 @@ var msigSwapProposeCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent swap proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -993,7 +1013,7 @@ var msigSwapApproveCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent swap approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1070,7 +1090,7 @@ var msigSwapCancelCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent swap cancellation in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1157,7 +1177,7 @@ var msigLockProposeCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent lock proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1254,7 +1274,7 @@ var msigLockApproveCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent lock approval in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1346,7 +1366,7 @@ var msigLockCancelCmd = &cli.Command{
|
||||
|
||||
fmt.Println("sent lock cancellation in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1420,3 +1440,78 @@ var msigVestedCmd = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var msigProposeThresholdCmd = &cli.Command{
|
||||
Name: "propose-threshold",
|
||||
Usage: "Propose setting a different signing threshold on the account",
|
||||
ArgsUsage: "<multisigAddress newM>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
Usage: "account to send the proposal from",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() != 2 {
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass multisig address and new threshold value"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msig, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newM, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var from address.Address
|
||||
if cctx.IsSet("from") {
|
||||
f, err := address.NewFromString(cctx.String("from"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
from = f
|
||||
} else {
|
||||
defaddr, err := api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
from = defaddr
|
||||
}
|
||||
|
||||
params, actErr := actors.SerializeParams(&msig0.ChangeNumApprovalsThresholdParams{
|
||||
NewThreshold: newM,
|
||||
})
|
||||
|
||||
if actErr != nil {
|
||||
return actErr
|
||||
}
|
||||
|
||||
msgCid, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(builtin2.MethodsMultisig.ChangeNumApprovalsThreshold), params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to propose change of threshold: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("sent change threshold proposal in message: ", msgCid)
|
||||
|
||||
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if wait.Receipt.ExitCode != 0 {
|
||||
return fmt.Errorf("change threshold proposal returned exit %d", wait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api/test"
|
||||
clitest "github.com/filecoin-project/lotus/cli/test"
|
||||
builder "github.com/filecoin-project/lotus/node/test"
|
||||
)
|
||||
|
||||
// TestMultisig does a basic test to exercise the multisig CLI
|
||||
// commands
|
||||
func TestMultisig(t *testing.T) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
blocktime := 5 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
nodes, _ := startNodes(ctx, t, blocktime)
|
||||
clientNode := nodes[0]
|
||||
clitest.RunMultisigTest(t, Commands, clientNode)
|
||||
}
|
||||
|
||||
func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) {
|
||||
n, sn := builder.RPCMockSbBuilder(t, test.OneFull, test.OneMiner)
|
||||
|
||||
full := n[0]
|
||||
miner := sn[0]
|
||||
|
||||
// Get everyone connected
|
||||
addrs, err := full.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := miner.NetConnect(ctx, addrs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Start mining blocks
|
||||
bm := test.NewBlockMiner(ctx, t, miner, blocktime)
|
||||
bm.MineBlocks()
|
||||
|
||||
// Get the creator's address
|
||||
creatorAddr, err := full.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create mock CLI
|
||||
return n, []address.Address{creatorAddr}
|
||||
}
|
||||
@@ -437,6 +437,7 @@ type mockCLI struct {
|
||||
out *bytes.Buffer
|
||||
}
|
||||
|
||||
// TODO: refactor to use the methods in cli/test/mockcli.go
|
||||
func newMockCLI(t *testing.T) *mockCLI {
|
||||
// Create a CLI App with an --api-url flag so that we can specify which node
|
||||
// the command should be executed against
|
||||
|
||||
+77
-122
@@ -60,7 +60,7 @@ var stateCmd = &cli.Command{
|
||||
stateSectorCmd,
|
||||
stateGetActorCmd,
|
||||
stateLookupIDCmd,
|
||||
stateReplaySetCmd,
|
||||
stateReplayCmd,
|
||||
stateSectorSizeCmd,
|
||||
stateReadStateCmd,
|
||||
stateListMessagesCmd,
|
||||
@@ -69,7 +69,6 @@ var stateCmd = &cli.Command{
|
||||
stateGetDealSetCmd,
|
||||
stateWaitMsgCmd,
|
||||
stateSearchMsgCmd,
|
||||
stateMsgCostCmd,
|
||||
stateMinerInfo,
|
||||
stateMarketCmd,
|
||||
stateExecTraceCmd,
|
||||
@@ -384,20 +383,27 @@ var stateExecTraceCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var stateReplaySetCmd = &cli.Command{
|
||||
var stateReplayCmd = &cli.Command{
|
||||
Name: "replay",
|
||||
Usage: "Replay a particular message within a tipset",
|
||||
ArgsUsage: "[tipsetKey messageCid]",
|
||||
Usage: "Replay a particular message",
|
||||
ArgsUsage: "<messageCid>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "show-trace",
|
||||
Usage: "print out full execution trace for given message",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "detailed-gas",
|
||||
Usage: "print out detailed gas costs for given message",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() < 1 {
|
||||
fmt.Println("usage: [tipset] <message cid>")
|
||||
fmt.Println("The last cid passed will be used as the message CID")
|
||||
fmt.Println("All preceding ones will be used as the tipset")
|
||||
if cctx.Args().Len() != 1 {
|
||||
fmt.Println("must provide cid of message to replay")
|
||||
return nil
|
||||
}
|
||||
|
||||
args := cctx.Args().Slice()
|
||||
mcid, err := cid.Decode(args[len(args)-1])
|
||||
mcid, err := cid.Decode(cctx.Args().First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("message cid was invalid: %s", err)
|
||||
}
|
||||
@@ -410,52 +416,7 @@ var stateReplaySetCmd = &cli.Command{
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
var ts *types.TipSet
|
||||
{
|
||||
var tscids []cid.Cid
|
||||
for _, s := range args[:len(args)-1] {
|
||||
c, err := cid.Decode(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tipset cid was invalid: %s", err)
|
||||
}
|
||||
tscids = append(tscids, c)
|
||||
}
|
||||
|
||||
if len(tscids) > 0 {
|
||||
var headers []*types.BlockHeader
|
||||
for _, c := range tscids {
|
||||
h, err := fapi.ChainGetBlock(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
headers = append(headers, h)
|
||||
}
|
||||
|
||||
ts, err = types.NewTipSet(headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var r *api.MsgLookup
|
||||
r, err = fapi.StateWaitMsg(ctx, mcid, build.MessageConfidence)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("finding message in chain: %w", err)
|
||||
}
|
||||
|
||||
childTs, err := fapi.ChainGetTipSet(ctx, r.TipSet)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("loading tipset: %w", err)
|
||||
}
|
||||
ts, err = fapi.ChainGetTipSet(ctx, childTs.Parents())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
res, err := fapi.StateReplay(ctx, ts.Key(), mcid)
|
||||
res, err := fapi.StateReplay(ctx, types.EmptyTSK, mcid)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("replay call failed: %w", err)
|
||||
}
|
||||
@@ -464,10 +425,25 @@ var stateReplaySetCmd = &cli.Command{
|
||||
fmt.Printf("Exit code: %d\n", res.MsgRct.ExitCode)
|
||||
fmt.Printf("Return: %x\n", res.MsgRct.Return)
|
||||
fmt.Printf("Gas Used: %d\n", res.MsgRct.GasUsed)
|
||||
|
||||
if cctx.Bool("detailed-gas") {
|
||||
fmt.Printf("Base Fee Burn: %d\n", res.GasCost.BaseFeeBurn)
|
||||
fmt.Printf("Overestimaton Burn: %d\n", res.GasCost.OverEstimationBurn)
|
||||
fmt.Printf("Miner Penalty: %d\n", res.GasCost.MinerPenalty)
|
||||
fmt.Printf("Miner Tip: %d\n", res.GasCost.MinerTip)
|
||||
fmt.Printf("Refund: %d\n", res.GasCost.Refund)
|
||||
}
|
||||
fmt.Printf("Total Message Cost: %d\n", res.GasCost.TotalCost)
|
||||
|
||||
if res.MsgRct.ExitCode != 0 {
|
||||
fmt.Printf("Error message: %q\n", res.Error)
|
||||
}
|
||||
|
||||
if cctx.Bool("show-trace") {
|
||||
fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", res.Msg.From, res.Msg.To, res.Msg.Value, res.Msg.Method, res.Msg.Params, res.MsgRct.ExitCode, res.MsgRct.Return)
|
||||
printInternalExecutions("\t", res.ExecutionTrace.Subcalls)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -837,33 +813,66 @@ var stateListMessagesCmd = &cli.Command{
|
||||
froma = a
|
||||
}
|
||||
|
||||
toh := cctx.Uint64("toheight")
|
||||
toh := abi.ChainEpoch(cctx.Uint64("toheight"))
|
||||
|
||||
ts, err := LoadTipSet(ctx, cctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgs, err := api.StateListMessages(ctx, &types.Message{To: toa, From: froma}, ts.Key(), abi.ChainEpoch(toh))
|
||||
if err != nil {
|
||||
return err
|
||||
if ts == nil {
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts = head
|
||||
}
|
||||
|
||||
for _, c := range msgs {
|
||||
if cctx.Bool("cids") {
|
||||
fmt.Println(c.String())
|
||||
continue
|
||||
windowSize := abi.ChainEpoch(100)
|
||||
|
||||
cur := ts
|
||||
for cur.Height() > toh {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
m, err := api.ChainGetMessage(ctx, c)
|
||||
end := toh
|
||||
if cur.Height()-windowSize > end {
|
||||
end = cur.Height() - windowSize
|
||||
}
|
||||
|
||||
msgs, err := api.StateListMessages(ctx, &lapi.MessageMatch{To: toa, From: froma}, cur.Key(), end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.MarshalIndent(m, "", " ")
|
||||
|
||||
for _, c := range msgs {
|
||||
if cctx.Bool("cids") {
|
||||
fmt.Println(c.String())
|
||||
continue
|
||||
}
|
||||
|
||||
m, err := api.ChainGetMessage(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
if end <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
next, err := api.ChainGetTipSetByHeight(ctx, end-1, cur.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
|
||||
cur = next
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1424,60 +1433,6 @@ var stateSearchMsgCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var stateMsgCostCmd = &cli.Command{
|
||||
Name: "msg-cost",
|
||||
Usage: "Get the detailed gas costs of a message",
|
||||
ArgsUsage: "[messageCid]",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must specify message cid to get gas costs for")
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
msg, err := cid.Decode(cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tsk := types.EmptyTSK
|
||||
|
||||
ts, err := LoadTipSet(ctx, cctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ts != nil {
|
||||
tsk = ts.Key()
|
||||
}
|
||||
|
||||
mgc, err := api.StateMsgGasCost(ctx, msg, tsk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mgc != nil {
|
||||
fmt.Printf("Message CID: %s", mgc.Message)
|
||||
fmt.Printf("\nGas Used: %d", mgc.GasUsed)
|
||||
fmt.Printf("\nBase Fee Burn: %d", mgc.BaseFeeBurn)
|
||||
fmt.Printf("\nOverestimation Burn: %d", mgc.OverEstimationBurn)
|
||||
fmt.Printf("\nMiner Tip: %d", mgc.MinerTip)
|
||||
fmt.Printf("\nRefund: %d", mgc.Refund)
|
||||
fmt.Printf("\nTotal Cost: %d", mgc.TotalCost)
|
||||
fmt.Printf("\nMiner Penalty: %d", mgc.MinerPenalty)
|
||||
} else {
|
||||
fmt.Print("message was not found on chain")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var stateCallCmd = &cli.Command{
|
||||
Name: "call",
|
||||
Usage: "Invoke a method on an actor locally",
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/stretchr/testify/require"
|
||||
lcli "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type mockCLI struct {
|
||||
t *testing.T
|
||||
cmds []*lcli.Command
|
||||
cctx *lcli.Context
|
||||
out *bytes.Buffer
|
||||
}
|
||||
|
||||
func newMockCLI(t *testing.T, cmds []*lcli.Command) *mockCLI {
|
||||
// Create a CLI App with an --api-url flag so that we can specify which node
|
||||
// the command should be executed against
|
||||
app := &lcli.App{
|
||||
Flags: []lcli.Flag{
|
||||
&lcli.StringFlag{
|
||||
Name: "api-url",
|
||||
Hidden: true,
|
||||
},
|
||||
},
|
||||
Commands: cmds,
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
app.Writer = &out
|
||||
app.Setup()
|
||||
|
||||
cctx := lcli.NewContext(app, &flag.FlagSet{}, nil)
|
||||
return &mockCLI{t: t, cmds: cmds, cctx: cctx, out: &out}
|
||||
}
|
||||
|
||||
func (c *mockCLI) client(addr multiaddr.Multiaddr) *mockCLIClient {
|
||||
return &mockCLIClient{t: c.t, cmds: c.cmds, addr: addr, cctx: c.cctx, out: c.out}
|
||||
}
|
||||
|
||||
// mockCLIClient runs commands against a particular node
|
||||
type mockCLIClient struct {
|
||||
t *testing.T
|
||||
cmds []*lcli.Command
|
||||
addr multiaddr.Multiaddr
|
||||
cctx *lcli.Context
|
||||
out *bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) run(cmd []string, params []string, args []string) string {
|
||||
// Add parameter --api-url=<node api listener address>
|
||||
apiFlag := "--api-url=" + c.addr.String()
|
||||
params = append([]string{apiFlag}, params...)
|
||||
|
||||
err := c.cctx.App.Run(append(append(cmd, params...), args...))
|
||||
require.NoError(c.t, err)
|
||||
|
||||
// Get the output
|
||||
str := strings.TrimSpace(c.out.String())
|
||||
c.out.Reset()
|
||||
return str
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) runCmd(input []string) string {
|
||||
cmd := c.cmdByNameSub(input[0], input[1])
|
||||
out, err := c.runCmdRaw(cmd, input[2:])
|
||||
require.NoError(c.t, err)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) cmdByNameSub(name string, sub string) *lcli.Command {
|
||||
for _, c := range c.cmds {
|
||||
if c.Name == name {
|
||||
for _, s := range c.Subcommands {
|
||||
if s.Name == sub {
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) runCmdRaw(cmd *lcli.Command, input []string) (string, error) {
|
||||
// prepend --api-url=<node api listener address>
|
||||
apiFlag := "--api-url=" + c.addr.String()
|
||||
input = append([]string{apiFlag}, input...)
|
||||
|
||||
fs := c.flagSet(cmd)
|
||||
err := fs.Parse(input)
|
||||
require.NoError(c.t, err)
|
||||
|
||||
err = cmd.Action(lcli.NewContext(c.cctx.App, fs, c.cctx))
|
||||
|
||||
// Get the output
|
||||
str := strings.TrimSpace(c.out.String())
|
||||
c.out.Reset()
|
||||
return str, err
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) flagSet(cmd *lcli.Command) *flag.FlagSet {
|
||||
// Apply app level flags (so we can process --api-url flag)
|
||||
fs := &flag.FlagSet{}
|
||||
for _, f := range c.cctx.App.Flags {
|
||||
err := f.Apply(fs)
|
||||
if err != nil {
|
||||
c.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
// Apply command level flags
|
||||
for _, f := range cmd.Flags {
|
||||
err := f.Apply(fs)
|
||||
if err != nil {
|
||||
c.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return fs
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api/test"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/stretchr/testify/require"
|
||||
lcli "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func QuietMiningLogs() {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
}
|
||||
|
||||
func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create mock CLI
|
||||
mockCLI := newMockCLI(t, cmds)
|
||||
clientCLI := mockCLI.client(clientNode.ListenAddr)
|
||||
|
||||
// Create some wallets on the node to use for testing multisig
|
||||
var walletAddrs []address.Address
|
||||
for i := 0; i < 4; i++ {
|
||||
addr, err := clientNode.WalletNew(ctx, types.KTSecp256k1)
|
||||
require.NoError(t, err)
|
||||
|
||||
walletAddrs = append(walletAddrs, addr)
|
||||
|
||||
test.SendFunds(ctx, t, clientNode, addr, types.NewInt(1e15))
|
||||
}
|
||||
|
||||
// Create an msig with three of the addresses and threshold of two sigs
|
||||
// msig create --required=2 --duration=50 --value=1000attofil <addr1> <addr2> <addr3>
|
||||
amtAtto := types.NewInt(1000)
|
||||
threshold := 2
|
||||
paramDuration := "--duration=50"
|
||||
paramRequired := fmt.Sprintf("--required=%d", threshold)
|
||||
paramValue := fmt.Sprintf("--value=%dattofil", amtAtto)
|
||||
cmd := []string{
|
||||
"msig", "create",
|
||||
paramRequired,
|
||||
paramDuration,
|
||||
paramValue,
|
||||
walletAddrs[0].String(),
|
||||
walletAddrs[1].String(),
|
||||
walletAddrs[2].String(),
|
||||
}
|
||||
out := clientCLI.runCmd(cmd)
|
||||
fmt.Println(out)
|
||||
|
||||
// Extract msig robust address from output
|
||||
expCreateOutPrefix := "Created new multisig:"
|
||||
require.Regexp(t, regexp.MustCompile(expCreateOutPrefix), out)
|
||||
parts := strings.Split(strings.TrimSpace(strings.Replace(out, expCreateOutPrefix, "", -1)), " ")
|
||||
require.Len(t, parts, 2)
|
||||
msigRobustAddr := parts[1]
|
||||
fmt.Println("msig robust address:", msigRobustAddr)
|
||||
|
||||
// Propose to add a new address to the msig
|
||||
// msig add-propose --from=<addr> <msig> <addr>
|
||||
paramFrom := fmt.Sprintf("--from=%s", walletAddrs[0])
|
||||
cmd = []string{
|
||||
"msig", "add-propose",
|
||||
paramFrom,
|
||||
msigRobustAddr,
|
||||
walletAddrs[3].String(),
|
||||
}
|
||||
out = clientCLI.runCmd(cmd)
|
||||
fmt.Println(out)
|
||||
|
||||
// msig inspect <msig>
|
||||
cmd = []string{"msig", "inspect", "--vesting", "--decode-params", msigRobustAddr}
|
||||
out = clientCLI.runCmd(cmd)
|
||||
fmt.Println(out)
|
||||
|
||||
// Expect correct balance
|
||||
require.Regexp(t, regexp.MustCompile("Balance: 0.000000000000001 FIL"), out)
|
||||
// Expect 1 transaction
|
||||
require.Regexp(t, regexp.MustCompile(`Transactions:\s*1`), out)
|
||||
// Expect transaction to be "AddSigner"
|
||||
require.Regexp(t, regexp.MustCompile(`AddSigner`), out)
|
||||
|
||||
// Approve adding the new address
|
||||
// msig add-approve --from=<addr> <msig> <addr> 0 <addr> false
|
||||
txnID := "0"
|
||||
paramFrom = fmt.Sprintf("--from=%s", walletAddrs[1])
|
||||
cmd = []string{
|
||||
"msig", "add-approve",
|
||||
paramFrom,
|
||||
msigRobustAddr,
|
||||
walletAddrs[0].String(),
|
||||
txnID,
|
||||
walletAddrs[3].String(),
|
||||
"false",
|
||||
}
|
||||
out = clientCLI.runCmd(cmd)
|
||||
fmt.Println(out)
|
||||
}
|
||||
+133
-6
@@ -6,16 +6,23 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/dline"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
"github.com/filecoin-project/lotus/node/impl/full"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
const (
|
||||
LookbackCap = time.Hour
|
||||
LookbackCap = time.Hour * 12
|
||||
stateWaitLookbackLimit = abi.ChainEpoch(20)
|
||||
)
|
||||
|
||||
@@ -26,9 +33,12 @@ var (
|
||||
// gatewayDepsAPI defines the API methods that the GatewayAPI depends on
|
||||
// (to make it easy to mock for tests)
|
||||
type gatewayDepsAPI interface {
|
||||
ChainHasObj(context.Context, cid.Cid) (bool, error)
|
||||
ChainHead(ctx context.Context) (*types.TipSet, error)
|
||||
ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
ChainGetNode(ctx context.Context, p string) (*api.IpldObject, error)
|
||||
GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error)
|
||||
MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error)
|
||||
MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)
|
||||
@@ -37,10 +47,31 @@ type gatewayDepsAPI interface {
|
||||
StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error)
|
||||
StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
|
||||
StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, h abi.ChainEpoch) (*api.MsgLookup, error)
|
||||
StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error)
|
||||
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error)
|
||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (bitfield.BitField, error)
|
||||
StateMinerRecoveries(context.Context, address.Address, types.TipSetKey) (bitfield.BitField, error)
|
||||
StateMinerInfo(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error)
|
||||
StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) ([]api.Deadline, error)
|
||||
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
StateMinerProvingDeadline(context.Context, address.Address, types.TipSetKey) (*dline.Info, error)
|
||||
StateCirculatingSupply(context.Context, types.TipSetKey) (abi.TokenAmount, error)
|
||||
StateVMCirculatingSupplyInternal(context.Context, types.TipSetKey) (api.CirculatingSupply, error)
|
||||
}
|
||||
|
||||
type GatewayAPI struct {
|
||||
api gatewayDepsAPI
|
||||
api gatewayDepsAPI
|
||||
lookbackCap time.Duration
|
||||
}
|
||||
|
||||
// NewGatewayAPI creates a new GatewayAPI with the default lookback cap
|
||||
func NewGatewayAPI(api gatewayDepsAPI) *GatewayAPI {
|
||||
return newGatewayAPI(api, LookbackCap)
|
||||
}
|
||||
|
||||
// used by the tests
|
||||
func newGatewayAPI(api gatewayDepsAPI, lookbackCap time.Duration) *GatewayAPI {
|
||||
return &GatewayAPI{api: api, lookbackCap: lookbackCap}
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) error {
|
||||
@@ -76,13 +107,17 @@ func (a *GatewayAPI) checkTipsetHeight(ts *types.TipSet, h abi.ChainEpoch) error
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) checkTimestamp(at time.Time) error {
|
||||
if time.Since(at) > LookbackCap {
|
||||
if time.Since(at) > a.lookbackCap {
|
||||
return ErrLookbackTooLong
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) ChainHasObj(ctx context.Context, c cid.Cid) (bool, error) {
|
||||
return a.api.ChainHasObj(ctx, c)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
// TODO: cache and invalidate cache when timestamp is up (or have internal ChainNotify)
|
||||
|
||||
@@ -94,9 +129,19 @@ func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
ts, err := a.api.ChainGetTipSet(ctx, tsk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var ts *types.TipSet
|
||||
if tsk.IsEmpty() {
|
||||
head, err := a.api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts = head
|
||||
} else {
|
||||
gts, err := a.api.ChainGetTipSet(ctx, tsk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts = gts
|
||||
}
|
||||
|
||||
// Check if the tipset key refers to a tipset that's too far in the past
|
||||
@@ -112,6 +157,14 @@ func (a *GatewayAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoc
|
||||
return a.api.ChainGetTipSetByHeight(ctx, h, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
|
||||
return a.api.ChainReadObj(ctx, c)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) ChainGetNode(ctx context.Context, p string) (*api.IpldObject, error) {
|
||||
return a.api.ChainGetNode(ctx, p)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return nil, err
|
||||
@@ -172,6 +225,80 @@ func (a *GatewayAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence u
|
||||
return a.api.StateWaitMsgLimited(ctx, msg, confidence, stateWaitLookbackLimit)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.api.StateReadState(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateMinerPower(ctx context.Context, m address.Address, tsk types.TipSetKey) (*api.MinerPower, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.api.StateMinerPower(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateMinerFaults(ctx context.Context, m address.Address, tsk types.TipSetKey) (bitfield.BitField, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return bitfield.BitField{}, err
|
||||
}
|
||||
return a.api.StateMinerFaults(ctx, m, tsk)
|
||||
}
|
||||
func (a *GatewayAPI) StateMinerRecoveries(ctx context.Context, m address.Address, tsk types.TipSetKey) (bitfield.BitField, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return bitfield.BitField{}, err
|
||||
}
|
||||
return a.api.StateMinerRecoveries(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateMinerInfo(ctx context.Context, m address.Address, tsk types.TipSetKey) (miner.MinerInfo, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return miner.MinerInfo{}, err
|
||||
}
|
||||
return a.api.StateMinerInfo(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateMinerDeadlines(ctx context.Context, m address.Address, tsk types.TipSetKey) ([]api.Deadline, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.api.StateMinerDeadlines(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateMinerAvailableBalance(ctx context.Context, m address.Address, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return types.BigInt{}, err
|
||||
}
|
||||
return a.api.StateMinerAvailableBalance(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateMinerProvingDeadline(ctx context.Context, m address.Address, tsk types.TipSetKey) (*dline.Info, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.api.StateMinerProvingDeadline(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (abi.TokenAmount, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return types.BigInt{}, err
|
||||
}
|
||||
return a.api.StateCirculatingSupply(ctx, tsk)
|
||||
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateVMCirculatingSupplyInternal(ctx context.Context, tsk types.TipSetKey) (api.CirculatingSupply, error) {
|
||||
if err := a.checkTipsetKey(ctx, tsk); err != nil {
|
||||
return api.CirculatingSupply{}, err
|
||||
}
|
||||
return a.api.StateVMCirculatingSupplyInternal(ctx, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) WalletVerify(ctx context.Context, k address.Address, msg []byte, sig *crypto.Signature) (bool, error) {
|
||||
return sigs.Verify(sig, k, msg) == nil, nil
|
||||
}
|
||||
|
||||
var _ api.GatewayAPI = (*GatewayAPI)(nil)
|
||||
var _ full.ChainModuleAPI = (*GatewayAPI)(nil)
|
||||
var _ full.GasModuleAPI = (*GatewayAPI)(nil)
|
||||
|
||||
@@ -88,7 +88,7 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mock := &mockGatewayDepsAPI{}
|
||||
a := &GatewayAPI{api: mock}
|
||||
a := NewGatewayAPI(mock)
|
||||
|
||||
// Create tipsets from genesis up to tskh and return the highest
|
||||
ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS)
|
||||
@@ -107,6 +107,13 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) {
|
||||
type mockGatewayDepsAPI struct {
|
||||
lk sync.RWMutex
|
||||
tipsets []*types.TipSet
|
||||
|
||||
gatewayDepsAPI // satisfies all interface requirements but will panic if
|
||||
// methods are called. easier than filling out with panic stubs IMO
|
||||
}
|
||||
|
||||
func (m *mockGatewayDepsAPI) ChainHasObj(context.Context, cid.Cid) (bool, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockGatewayDepsAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
@@ -158,6 +165,10 @@ func (m *mockGatewayDepsAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.C
|
||||
return m.tipsets[h], nil
|
||||
}
|
||||
|
||||
func (m *mockGatewayDepsAPI) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockGatewayDepsAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
@@ -189,3 +200,7 @@ func (m *mockGatewayDepsAPI) StateLookupID(ctx context.Context, addr address.Add
|
||||
func (m *mockGatewayDepsAPI) StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, h abi.ChainEpoch) (*api.MsgLookup, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockGatewayDepsAPI) StateReadState(ctx context.Context, act address.Address, ts types.TipSetKey) (*api.ActorState, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -4,10 +4,14 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/cli"
|
||||
clitest "github.com/filecoin-project/lotus/cli/test"
|
||||
|
||||
init0 "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/multisig"
|
||||
|
||||
@@ -26,20 +30,22 @@ import (
|
||||
builder "github.com/filecoin-project/lotus/node/test"
|
||||
)
|
||||
|
||||
const maxLookbackCap = time.Duration(math.MaxInt64)
|
||||
|
||||
func init() {
|
||||
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
|
||||
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
|
||||
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
|
||||
}
|
||||
|
||||
// TestEndToEnd tests that API calls can be made on a lite node that is
|
||||
// connected through a gateway to a full API node
|
||||
func TestEndToEnd(t *testing.T) {
|
||||
// TestEndToEndWalletMsig tests that wallet and msig API calls can be made
|
||||
// on a lite node that is connected through a gateway to a full API node
|
||||
func TestEndToEndWalletMsig(t *testing.T) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
blocktime := 5 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
full, lite, closer := startNodes(ctx, t, blocktime)
|
||||
full, lite, closer := startNodes(ctx, t, blocktime, maxLookbackCap)
|
||||
defer closer()
|
||||
|
||||
// The full node starts with a wallet
|
||||
@@ -56,11 +62,11 @@ func TestEndToEnd(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Send some funds from the full node to the lite node
|
||||
err = sendFunds(ctx, t, full, fullWalletAddr, liteWalletAddr, types.NewInt(1e18))
|
||||
err = sendFunds(ctx, full, fullWalletAddr, liteWalletAddr, types.NewInt(1e18))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Send some funds from the lite node back to the full node
|
||||
err = sendFunds(ctx, t, lite, liteWalletAddr, fullWalletAddr, types.NewInt(100))
|
||||
err = sendFunds(ctx, lite, liteWalletAddr, fullWalletAddr, types.NewInt(100))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Sign some data with the lite node wallet address
|
||||
@@ -81,7 +87,7 @@ func TestEndToEnd(t *testing.T) {
|
||||
|
||||
walletAddrs = append(walletAddrs, addr)
|
||||
|
||||
err = sendFunds(ctx, t, lite, liteWalletAddr, addr, types.NewInt(1e15))
|
||||
err = sendFunds(ctx, lite, liteWalletAddr, addr, types.NewInt(1e15))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -135,7 +141,33 @@ func TestEndToEnd(t *testing.T) {
|
||||
require.True(t, approveReturn.Applied)
|
||||
}
|
||||
|
||||
func sendFunds(ctx context.Context, t *testing.T, fromNode test.TestNode, fromAddr address.Address, toAddr address.Address, amt types.BigInt) error {
|
||||
// TestEndToEndMsigCLI tests that msig CLI calls can be made
|
||||
// on a lite node that is connected through a gateway to a full API node
|
||||
func TestEndToEndMsigCLI(t *testing.T) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
clitest.QuietMiningLogs()
|
||||
|
||||
blocktime := 5 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
full, lite, closer := startNodes(ctx, t, blocktime, maxLookbackCap)
|
||||
defer closer()
|
||||
|
||||
// The full node starts with a wallet
|
||||
fullWalletAddr, err := full.WalletDefaultAddress(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a wallet on the lite node
|
||||
liteWalletAddr, err := lite.WalletNew(ctx, types.KTSecp256k1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Send some funds from the full node to the lite node
|
||||
err = sendFunds(ctx, full, fullWalletAddr, liteWalletAddr, types.NewInt(1e18))
|
||||
require.NoError(t, err)
|
||||
|
||||
clitest.RunMultisigTest(t, cli.Commands, lite)
|
||||
}
|
||||
|
||||
func sendFunds(ctx context.Context, fromNode test.TestNode, fromAddr address.Address, toAddr address.Address, amt types.BigInt) error {
|
||||
msg := &types.Message{
|
||||
From: fromAddr,
|
||||
To: toAddr,
|
||||
@@ -158,7 +190,7 @@ func sendFunds(ctx context.Context, t *testing.T, fromNode test.TestNode, fromAd
|
||||
return nil
|
||||
}
|
||||
|
||||
func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (test.TestNode, test.TestNode, jsonrpc.ClientCloser) {
|
||||
func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration, lookbackCap time.Duration) (test.TestNode, test.TestNode, jsonrpc.ClientCloser) {
|
||||
var closer jsonrpc.ClientCloser
|
||||
|
||||
// Create one miner and two full nodes.
|
||||
@@ -175,7 +207,7 @@ func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (tes
|
||||
fullNode := nodes[0]
|
||||
|
||||
// Create a gateway server in front of the full node
|
||||
_, addr, err := builder.CreateRPCServer(&GatewayAPI{api: fullNode})
|
||||
_, addr, err := builder.CreateRPCServer(newGatewayAPI(fullNode, lookbackCap))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a gateway client API that connects to the gateway server
|
||||
|
||||
@@ -76,7 +76,7 @@ var runCmd = &cli.Command{
|
||||
log.Info("Setting up API endpoint at " + address)
|
||||
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", &GatewayAPI{api: api})
|
||||
rpcServer.Register("Filecoin", NewGatewayAPI(api))
|
||||
|
||||
mux.Handle("/rpc/v0", rpcServer)
|
||||
mux.PathPrefix("/").Handler(http.DefaultServeMux)
|
||||
|
||||
@@ -5,6 +5,10 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||
|
||||
_init "github.com/filecoin-project/lotus/chain/actors/builtin/init"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
|
||||
@@ -136,6 +140,9 @@ var chainBalanceStateCmd = &cli.Command{
|
||||
&cli.BoolFlag{
|
||||
Name: "miner-info",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "robust-addresses",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
ctx := context.TODO()
|
||||
@@ -187,6 +194,33 @@ var chainBalanceStateCmd = &cli.Command{
|
||||
|
||||
minerInfo := cctx.Bool("miner-info")
|
||||
|
||||
robustMap := make(map[address.Address]address.Address)
|
||||
if cctx.Bool("robust-addresses") {
|
||||
iact, err := tree.GetActor(_init.Address)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load init actor: %w", err)
|
||||
}
|
||||
|
||||
ist, err := _init.Load(store, iact)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load init actor state: %w", err)
|
||||
}
|
||||
|
||||
err = ist.ForEachActor(func(id abi.ActorID, addr address.Address) error {
|
||||
idAddr, err := address.NewIDAddress(uint64(id))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to write to addr map: %w", err)
|
||||
}
|
||||
|
||||
robustMap[idAddr] = addr
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to invert init address map: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var infos []accountInfo
|
||||
err = tree.ForEach(func(addr address.Address, act *types.Actor) error {
|
||||
|
||||
@@ -201,6 +235,23 @@ var chainBalanceStateCmd = &cli.Command{
|
||||
VestingAmount: types.FIL(big.NewInt(0)),
|
||||
}
|
||||
|
||||
if cctx.Bool("robust-addresses") {
|
||||
robust, found := robustMap[addr]
|
||||
if found {
|
||||
ai.Address = robust
|
||||
} else {
|
||||
id, err := address.IDFromAddress(addr)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get ID address: %w", err)
|
||||
}
|
||||
|
||||
// TODO: This is not the correctest way to determine whether a robust address should exist
|
||||
if id >= genesis.MinerStart {
|
||||
return xerrors.Errorf("address doesn't have a robust address: %s", addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if minerInfo && builtin.IsStorageMinerActor(act.Code) {
|
||||
pow, _, _, err := stmgr.GetPowerRaw(ctx, sm, sroot, addr)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
// ProtocolCodenames is a table that summarises the protocol codenames that
|
||||
// will be set on extracted vectors, depending on the original execution height.
|
||||
//
|
||||
// Implementers rely on these names to filter the vectors they can run through
|
||||
// their implementations, based on their support level
|
||||
var ProtocolCodenames = []struct {
|
||||
firstEpoch abi.ChainEpoch
|
||||
name string
|
||||
}{
|
||||
{0, "genesis"},
|
||||
{build.UpgradeBreezeHeight + 1, "breeze"},
|
||||
{build.UpgradeSmokeHeight + 1, "smoke"},
|
||||
{build.UpgradeIgnitionHeight + 1, "ignition"},
|
||||
{build.UpgradeRefuelHeight + 1, "refuel"},
|
||||
{build.UpgradeActorsV2Height + 1, "actorsv2"},
|
||||
{build.UpgradeTapeHeight + 1, "tape"},
|
||||
{build.UpgradeLiftoffHeight + 1, "liftoff"},
|
||||
{build.UpgradePostLiftoffHeight + 1, "postliftoff"},
|
||||
}
|
||||
|
||||
// GetProtocolCodename gets the protocol codename associated with a height.
|
||||
func GetProtocolCodename(height abi.ChainEpoch) string {
|
||||
for i, v := range ProtocolCodenames {
|
||||
if height < v.firstEpoch {
|
||||
// found the cutoff, return previous.
|
||||
return ProtocolCodenames[i-1].name
|
||||
}
|
||||
}
|
||||
return ProtocolCodenames[len(ProtocolCodenames)-1].name
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
func TestProtocolCodenames(t *testing.T) {
|
||||
if height := abi.ChainEpoch(100); GetProtocolCodename(height) != "genesis" {
|
||||
t.Fatal("expected genesis codename")
|
||||
}
|
||||
|
||||
if height := abi.ChainEpoch(build.UpgradeBreezeHeight + 1); GetProtocolCodename(height) != "breeze" {
|
||||
t.Fatal("expected breeze codename")
|
||||
}
|
||||
|
||||
if height := build.UpgradeActorsV2Height + 1; GetProtocolCodename(height) != "actorsv2" {
|
||||
t.Fatal("expected actorsv2 codename")
|
||||
}
|
||||
|
||||
if height := abi.ChainEpoch(math.MaxInt64); GetProtocolCodename(height) != ProtocolCodenames[len(ProtocolCodenames)-1].name {
|
||||
t.Fatal("expected last codename")
|
||||
}
|
||||
}
|
||||
+17
-13
@@ -72,20 +72,24 @@ func runExecLotus(_ *cli.Context) error {
|
||||
|
||||
func executeTestVector(tv schema.TestVector) error {
|
||||
log.Println("executing test vector:", tv.Meta.ID)
|
||||
r := new(conformance.LogReporter)
|
||||
switch class := tv.Class; class {
|
||||
case "message":
|
||||
conformance.ExecuteMessageVector(r, &tv)
|
||||
case "tipset":
|
||||
conformance.ExecuteTipsetVector(r, &tv)
|
||||
default:
|
||||
return fmt.Errorf("test vector class %s not supported", class)
|
||||
}
|
||||
|
||||
if r.Failed() {
|
||||
log.Println(color.HiRedString("❌ test vector failed"))
|
||||
} else {
|
||||
log.Println(color.GreenString("✅ test vector succeeded"))
|
||||
for _, v := range tv.Pre.Variants {
|
||||
r := new(conformance.LogReporter)
|
||||
|
||||
switch class, v := tv.Class, v; class {
|
||||
case "message":
|
||||
conformance.ExecuteMessageVector(r, &tv, &v)
|
||||
case "tipset":
|
||||
conformance.ExecuteTipsetVector(r, &tv, &v)
|
||||
default:
|
||||
return fmt.Errorf("test vector class %s not supported", class)
|
||||
}
|
||||
|
||||
if r.Failed() {
|
||||
log.Println(color.HiRedString("❌ test vector failed for variant %s", v.ID))
|
||||
} else {
|
||||
log.Println(color.GreenString("✅ test vector succeeded for variant %s", v.ID))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+13
-1
@@ -347,6 +347,13 @@ func doExtract(ctx context.Context, fapi api.FullNode, opts extractOpts) error {
|
||||
return err
|
||||
}
|
||||
|
||||
nv, err := fapi.StateNetworkVersion(ctx, execTs.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
codename := GetProtocolCodename(execTs.Height())
|
||||
|
||||
// Write out the test vector.
|
||||
vector := schema.TestVector{
|
||||
Class: schema.ClassMessage,
|
||||
@@ -363,10 +370,15 @@ func doExtract(ctx context.Context, fapi api.FullNode, opts extractOpts) error {
|
||||
{Source: fmt.Sprintf("execution_tipset:%s", execTs.Key().String())},
|
||||
{Source: "github.com/filecoin-project/lotus", Version: version.String()}},
|
||||
},
|
||||
Selector: schema.Selector{
|
||||
schema.SelectorMinProtocolVersion: codename,
|
||||
},
|
||||
Randomness: recordingRand.Recorded(),
|
||||
CAR: out.Bytes(),
|
||||
Pre: &schema.Preconditions{
|
||||
Epoch: int64(execTs.Height()),
|
||||
Variants: []schema.Variant{
|
||||
{ID: codename, Epoch: int64(execTs.Height()), NetworkVersion: uint(nv)},
|
||||
},
|
||||
CircSupply: circSupply.Int,
|
||||
BaseFee: basefee.Int,
|
||||
StateTree: &schema.StateTree{
|
||||
|
||||
@@ -11,6 +11,11 @@ import (
|
||||
"github.com/filecoin-project/test-vectors/schema"
|
||||
)
|
||||
|
||||
var invokees = map[schema.Class]func(Reporter, *schema.TestVector, *schema.Variant){
|
||||
schema.ClassMessage: ExecuteMessageVector,
|
||||
schema.ClassTipset: ExecuteTipsetVector,
|
||||
}
|
||||
|
||||
const (
|
||||
// EnvSkipConformance, if 1, skips the conformance test suite.
|
||||
EnvSkipConformance = "SKIP_CONFORMANCE"
|
||||
@@ -120,13 +125,16 @@ func TestConformance(t *testing.T) {
|
||||
}
|
||||
|
||||
// dispatch the execution depending on the vector class.
|
||||
switch vector.Class {
|
||||
case "message":
|
||||
ExecuteMessageVector(t, &vector)
|
||||
case "tipset":
|
||||
ExecuteTipsetVector(t, &vector)
|
||||
default:
|
||||
t.Fatalf("test vector class not supported: %s", vector.Class)
|
||||
invokee, ok := invokees[vector.Class]
|
||||
if !ok {
|
||||
t.Fatalf("unsupported test vector class: %s", vector.Class)
|
||||
}
|
||||
|
||||
for _, variant := range vector.Pre.Variants {
|
||||
variant := variant
|
||||
t.Run(variant.ID, func(t *testing.T) {
|
||||
invokee(t, &vector, &variant)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ type ExecuteTipsetResult struct {
|
||||
// This method returns the the receipts root, the poststate root, and the VM
|
||||
// message results. The latter _include_ implicit messages, such as cron ticks
|
||||
// and reward withdrawal per miner.
|
||||
func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, preroot cid.Cid, parentEpoch abi.ChainEpoch, tipset *schema.Tipset) (*ExecuteTipsetResult, error) {
|
||||
func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, preroot cid.Cid, parentEpoch abi.ChainEpoch, tipset *schema.Tipset, execEpoch abi.ChainEpoch) (*ExecuteTipsetResult, error) {
|
||||
var (
|
||||
syscalls = vm.Syscalls(ffiwrapper.ProofVerifier)
|
||||
vmRand = NewFixedRand()
|
||||
@@ -121,11 +121,10 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, preroot
|
||||
messages []*types.Message
|
||||
results []*vm.ApplyRet
|
||||
|
||||
epoch = abi.ChainEpoch(tipset.Epoch)
|
||||
basefee = abi.NewTokenAmount(tipset.BaseFee.Int64())
|
||||
)
|
||||
|
||||
postcid, receiptsroot, err := sm.ApplyBlocks(context.Background(), parentEpoch, preroot, blocks, epoch, vmRand, func(_ cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
|
||||
postcid, receiptsroot, err := sm.ApplyBlocks(context.Background(), parentEpoch, preroot, blocks, execEpoch, vmRand, func(_ cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
|
||||
messages = append(messages, msg)
|
||||
results = append(results, ret)
|
||||
return nil
|
||||
|
||||
+14
-12
@@ -30,11 +30,11 @@ import (
|
||||
)
|
||||
|
||||
// ExecuteMessageVector executes a message-class test vector.
|
||||
func ExecuteMessageVector(r Reporter, vector *schema.TestVector) {
|
||||
func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
epoch = vector.Pre.Epoch
|
||||
root = vector.Pre.StateTree.RootCID
|
||||
ctx = context.Background()
|
||||
baseEpoch = variant.Epoch
|
||||
root = vector.Pre.StateTree.RootCID
|
||||
)
|
||||
|
||||
// Load the CAR into a new temporary Blockstore.
|
||||
@@ -53,16 +53,16 @@ func ExecuteMessageVector(r Reporter, vector *schema.TestVector) {
|
||||
r.Fatalf("failed to deserialize message: %s", err)
|
||||
}
|
||||
|
||||
// add an epoch if one's set.
|
||||
if m.Epoch != nil {
|
||||
epoch = *m.Epoch
|
||||
// add the epoch offset if one is set.
|
||||
if m.EpochOffset != nil {
|
||||
baseEpoch += *m.EpochOffset
|
||||
}
|
||||
|
||||
// Execute the message.
|
||||
var ret *vm.ApplyRet
|
||||
ret, root, err = driver.ExecuteMessage(bs, ExecuteMessageParams{
|
||||
Preroot: root,
|
||||
Epoch: abi.ChainEpoch(epoch),
|
||||
Epoch: abi.ChainEpoch(baseEpoch),
|
||||
Message: msg,
|
||||
BaseFee: BaseFeeOrDefault(vector.Pre.BaseFee),
|
||||
CircSupply: CircSupplyOrDefault(vector.Pre.CircSupply),
|
||||
@@ -86,10 +86,10 @@ func ExecuteMessageVector(r Reporter, vector *schema.TestVector) {
|
||||
}
|
||||
|
||||
// ExecuteTipsetVector executes a tipset-class test vector.
|
||||
func ExecuteTipsetVector(r Reporter, vector *schema.TestVector) {
|
||||
func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
prevEpoch = vector.Pre.Epoch
|
||||
baseEpoch = abi.ChainEpoch(variant.Epoch)
|
||||
root = vector.Pre.StateTree.RootCID
|
||||
tmpds = ds.NewMapDatastore()
|
||||
)
|
||||
@@ -105,9 +105,11 @@ func ExecuteTipsetVector(r Reporter, vector *schema.TestVector) {
|
||||
|
||||
// Apply every tipset.
|
||||
var receiptsIdx int
|
||||
var prevEpoch = baseEpoch
|
||||
for i, ts := range vector.ApplyTipsets {
|
||||
ts := ts // capture
|
||||
ret, err := driver.ExecuteTipset(bs, tmpds, root, abi.ChainEpoch(prevEpoch), &ts)
|
||||
execEpoch := baseEpoch + abi.ChainEpoch(ts.EpochOffset)
|
||||
ret, err := driver.ExecuteTipset(bs, tmpds, root, prevEpoch, &ts, execEpoch)
|
||||
if err != nil {
|
||||
r.Fatalf("failed to apply tipset %d message: %s", i, err)
|
||||
}
|
||||
@@ -122,7 +124,7 @@ func ExecuteTipsetVector(r Reporter, vector *schema.TestVector) {
|
||||
r.Errorf("post receipts root doesn't match; expected: %s, was: %s", expected, actual)
|
||||
}
|
||||
|
||||
prevEpoch = ts.Epoch
|
||||
prevEpoch = execEpoch
|
||||
root = ret.PostStateRoot
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,6 @@
|
||||
* [StateMinerRecoveries](#StateMinerRecoveries)
|
||||
* [StateMinerSectorCount](#StateMinerSectorCount)
|
||||
* [StateMinerSectors](#StateMinerSectors)
|
||||
* [StateMsgGasCost](#StateMsgGasCost)
|
||||
* [StateNetworkName](#StateNetworkName)
|
||||
* [StateNetworkVersion](#StateNetworkVersion)
|
||||
* [StateReadState](#StateReadState)
|
||||
@@ -2989,7 +2988,7 @@ Response:
|
||||
|
||||
## State
|
||||
The State methods are used to query, inspect, and interact with chain state.
|
||||
All methods take a TipSetKey as a parameter. The state looked up is the state at that tipset.
|
||||
Most methods take a TipSetKey as a parameter. The state looked up is the state at that tipset.
|
||||
A nil TipSetKey can be provided as a param, this will cause the heaviest tipset in the chain to be used.
|
||||
|
||||
|
||||
@@ -3100,6 +3099,18 @@ Response:
|
||||
"Return": "Ynl0ZSBhcnJheQ==",
|
||||
"GasUsed": 9
|
||||
},
|
||||
"GasCost": {
|
||||
"Message": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"GasUsed": "0",
|
||||
"BaseFeeBurn": "0",
|
||||
"OverEstimationBurn": "0",
|
||||
"MinerPenalty": "0",
|
||||
"MinerTip": "0",
|
||||
"Refund": "0",
|
||||
"TotalCost": "0"
|
||||
},
|
||||
"ExecutionTrace": {
|
||||
"Msg": {
|
||||
"Version": 42,
|
||||
@@ -3352,19 +3363,8 @@ Inputs:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"Version": 42,
|
||||
"To": "f01234",
|
||||
"From": "f01234",
|
||||
"Nonce": 42,
|
||||
"Value": "0",
|
||||
"GasLimit": 9,
|
||||
"GasFeeCap": "0",
|
||||
"GasPremium": "0",
|
||||
"Method": 1,
|
||||
"Params": "Ynl0ZSBhcnJheQ==",
|
||||
"CID": {
|
||||
"/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s"
|
||||
}
|
||||
"From": "f01234"
|
||||
},
|
||||
[
|
||||
{
|
||||
@@ -3974,45 +3974,6 @@ Inputs:
|
||||
|
||||
Response: `null`
|
||||
|
||||
### StateMsgGasCost
|
||||
StateMsgGasCost searches for a message in the chain, and returns details of the messages gas costs, including the penalty and miner tip
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
[
|
||||
{
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
{
|
||||
"/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve"
|
||||
}
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"Message": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"GasUsed": "0",
|
||||
"BaseFeeBurn": "0",
|
||||
"OverEstimationBurn": "0",
|
||||
"MinerPenalty": "0",
|
||||
"MinerTip": "0",
|
||||
"Refund": "0",
|
||||
"TotalCost": "0"
|
||||
}
|
||||
```
|
||||
|
||||
### StateNetworkName
|
||||
StateNetworkName returns the name of the network the node is synced to
|
||||
|
||||
@@ -4043,7 +4004,7 @@ Inputs:
|
||||
]
|
||||
```
|
||||
|
||||
Response: `5`
|
||||
Response: `6`
|
||||
|
||||
### StateReadState
|
||||
StateReadState returns the indicated actor's state.
|
||||
@@ -4075,7 +4036,8 @@ Response:
|
||||
```
|
||||
|
||||
### StateReplay
|
||||
StateReplay returns the result of executing the indicated message, assuming it was executed in the indicated tipset.
|
||||
StateReplay replays a given message, assuming it was included in a block in the specified tipset.
|
||||
If no tipset key is provided, the appropriate tipset is looked up.
|
||||
|
||||
|
||||
Perms: read
|
||||
@@ -4123,6 +4085,18 @@ Response:
|
||||
"Return": "Ynl0ZSBhcnJheQ==",
|
||||
"GasUsed": 9
|
||||
},
|
||||
"GasCost": {
|
||||
"Message": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"GasUsed": "0",
|
||||
"BaseFeeBurn": "0",
|
||||
"OverEstimationBurn": "0",
|
||||
"MinerPenalty": "0",
|
||||
"MinerTip": "0",
|
||||
"Refund": "0",
|
||||
"TotalCost": "0"
|
||||
},
|
||||
"ExecutionTrace": {
|
||||
"Msg": {
|
||||
"Version": 42,
|
||||
|
||||
Vendored
+1
-1
Submodule extern/test-vectors updated: a8f968adeb...d9a75a7873
@@ -34,14 +34,14 @@ require (
|
||||
github.com/filecoin-project/go-multistore v0.0.3
|
||||
github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20
|
||||
github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20201003010437-c33112184a2b
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20201013222834-41ea465f274f
|
||||
github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe
|
||||
github.com/filecoin-project/go-statestore v0.1.0
|
||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
|
||||
github.com/filecoin-project/specs-actors v0.9.12
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.0
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.1-0.20201014224736-4baf4d4b44d0
|
||||
github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796
|
||||
github.com/filecoin-project/test-vectors/schema v0.0.4
|
||||
github.com/filecoin-project/test-vectors/schema v0.0.5
|
||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
|
||||
github.com/go-kit/kit v0.10.0
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
|
||||
@@ -266,8 +266,8 @@ github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I=
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab h1:cEDC5Ei8UuT99hPWhCjA72SM9AuRtnpvdSTIYbnzN8I=
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g=
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20201003010437-c33112184a2b h1:bMUfG6Sy6YSMbsjQAO1Q2vEZldbSdsbRy/FX3OlTck0=
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20201003010437-c33112184a2b/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g=
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20201013222834-41ea465f274f h1:TZDTu4MtBKSFLXWGKLy+cvC3nHfMFIrVgWLAz/+GgZQ=
|
||||
github.com/filecoin-project/go-state-types v0.0.0-20201013222834-41ea465f274f/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=
|
||||
@@ -278,12 +278,12 @@ github.com/filecoin-project/specs-actors v0.9.4/go.mod h1:BStZQzx5x7TmCkLv0Bpa07
|
||||
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/v2 v2.0.1/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY=
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.0 h1:ocEuGz8DG2cUWw32c/tvF8D6xT+dGVWJTr5yDevU00g=
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.0/go.mod h1:E7fAX4CZkDVQvDNRCxfq+hc3nx56KcCKyuZf0hlQJ20=
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.1-0.20201014224736-4baf4d4b44d0 h1:IzURd/Kg8p9AAiGI71HAT1qvVsQbHcz3e3YPQyuUsXU=
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.1-0.20201014224736-4baf4d4b44d0/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.4 h1:QTRd0gb/NP4ZOTM7Dib5U3xE1/ToGDKnYLfxkC3t/m8=
|
||||
github.com/filecoin-project/test-vectors/schema v0.0.4/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E=
|
||||
github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg=
|
||||
github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as=
|
||||
github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ=
|
||||
|
||||
+41
-19
@@ -6,35 +6,57 @@ import (
|
||||
"encoding/json"
|
||||
"os/exec"
|
||||
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
func CliDealFilter(cmd string) dtypes.DealFilter {
|
||||
// TODO: run some checks on the cmd string
|
||||
|
||||
func CliStorageDealFilter(cmd string) dtypes.StorageDealFilter {
|
||||
return func(ctx context.Context, deal storagemarket.MinerDeal) (bool, string, error) {
|
||||
j, err := json.MarshalIndent(deal, "", " ")
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
d := struct {
|
||||
storagemarket.MinerDeal
|
||||
DealType string
|
||||
}{
|
||||
MinerDeal: deal,
|
||||
DealType: "storage",
|
||||
}
|
||||
return runDealFilter(ctx, cmd, d)
|
||||
}
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
func CliRetrievalDealFilter(cmd string) dtypes.RetrievalDealFilter {
|
||||
return func(ctx context.Context, deal retrievalmarket.ProviderDealState) (bool, string, error) {
|
||||
d := struct {
|
||||
retrievalmarket.ProviderDealState
|
||||
DealType string
|
||||
}{
|
||||
ProviderDealState: deal,
|
||||
DealType: "retrieval",
|
||||
}
|
||||
return runDealFilter(ctx, cmd, d)
|
||||
}
|
||||
}
|
||||
|
||||
func runDealFilter(ctx context.Context, cmd string, deal interface{}) (bool, string, error) {
|
||||
j, err := json.MarshalIndent(deal, "", " ")
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
c := exec.Command("sh", "-c", cmd)
|
||||
c.Stdin = bytes.NewReader(j)
|
||||
c.Stdout = &out
|
||||
c.Stderr = &out
|
||||
var out bytes.Buffer
|
||||
|
||||
switch err := c.Run().(type) {
|
||||
case nil:
|
||||
return true, "", nil
|
||||
case *exec.ExitError:
|
||||
return false, out.String(), nil
|
||||
default:
|
||||
return false, "filter cmd run error", err
|
||||
}
|
||||
c := exec.Command("sh", "-c", cmd)
|
||||
c.Stdin = bytes.NewReader(j)
|
||||
c.Stdout = &out
|
||||
c.Stderr = &out
|
||||
|
||||
switch err := c.Run().(type) {
|
||||
case nil:
|
||||
return true, "", nil
|
||||
case *exec.ExitError:
|
||||
return false, out.String(), nil
|
||||
default:
|
||||
return false, "filter cmd run error", err
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -354,7 +354,8 @@ func Online() Option {
|
||||
Override(new(dtypes.ProviderDataTransfer), modules.NewProviderDAGServiceDataTransfer),
|
||||
Override(new(dtypes.ProviderPieceStore), modules.NewProviderPieceStore),
|
||||
Override(new(*storedask.StoredAsk), modules.NewStorageAsk),
|
||||
Override(new(dtypes.DealFilter), modules.BasicDealFilter(nil)),
|
||||
Override(new(dtypes.StorageDealFilter), modules.BasicDealFilter(nil)),
|
||||
Override(new(dtypes.RetrievalDealFilter), modules.RetrievalDealFilter(nil)),
|
||||
Override(new(modules.ProviderDealFunds), modules.NewProviderDealFunds),
|
||||
Override(new(storagemarket.StorageProvider), modules.StorageProvider),
|
||||
Override(new(storagemarket.StorageProviderNode), storageadapter.NewProviderNodeAdapter(nil)),
|
||||
@@ -484,7 +485,11 @@ func ConfigStorageMiner(c interface{}) Option {
|
||||
ConfigCommon(&cfg.Common),
|
||||
|
||||
If(cfg.Dealmaking.Filter != "",
|
||||
Override(new(dtypes.DealFilter), modules.BasicDealFilter(dealfilter.CliDealFilter(cfg.Dealmaking.Filter))),
|
||||
Override(new(dtypes.StorageDealFilter), modules.BasicDealFilter(dealfilter.CliStorageDealFilter(cfg.Dealmaking.Filter))),
|
||||
),
|
||||
|
||||
If(cfg.Dealmaking.RetrievalFilter != "",
|
||||
Override(new(dtypes.RetrievalDealFilter), modules.RetrievalDealFilter(dealfilter.CliRetrievalDealFilter(cfg.Dealmaking.RetrievalFilter))),
|
||||
),
|
||||
|
||||
Override(new(storagemarket.StorageProviderNode), storageadapter.NewProviderNodeAdapter(&cfg.Fees)),
|
||||
|
||||
+2
-1
@@ -45,7 +45,8 @@ type DealmakingConfig struct {
|
||||
PieceCidBlocklist []cid.Cid
|
||||
ExpectedSealDuration Duration
|
||||
|
||||
Filter string
|
||||
Filter string
|
||||
RetrievalFilter string
|
||||
}
|
||||
|
||||
type SealingConfig struct {
|
||||
|
||||
@@ -40,9 +40,11 @@ import (
|
||||
var log = logging.Logger("fullnode")
|
||||
|
||||
type ChainModuleAPI interface {
|
||||
ChainHasObj(context.Context, cid.Cid) (bool, error)
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
}
|
||||
|
||||
// ChainModule provides a default implementation of ChainModuleAPI.
|
||||
@@ -206,8 +208,8 @@ func (m *ChainModule) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpo
|
||||
return m.Chain.GetTipsetByHeight(ctx, h, ts, true)
|
||||
}
|
||||
|
||||
func (a *ChainAPI) ChainReadObj(ctx context.Context, obj cid.Cid) ([]byte, error) {
|
||||
blk, err := a.Chain.Blockstore().Get(obj)
|
||||
func (m *ChainModule) ChainReadObj(ctx context.Context, obj cid.Cid) ([]byte, error) {
|
||||
blk, err := m.Chain.Blockstore().Get(obj)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("blockstore get: %w", err)
|
||||
}
|
||||
@@ -219,8 +221,8 @@ func (a *ChainAPI) ChainDeleteObj(ctx context.Context, obj cid.Cid) error {
|
||||
return a.Chain.Blockstore().DeleteBlock(obj)
|
||||
}
|
||||
|
||||
func (a *ChainAPI) ChainHasObj(ctx context.Context, obj cid.Cid) (bool, error) {
|
||||
return a.Chain.Blockstore().Has(obj)
|
||||
func (m *ChainModule) ChainHasObj(ctx context.Context, obj cid.Cid) (bool, error) {
|
||||
return m.Chain.Blockstore().Has(obj)
|
||||
}
|
||||
|
||||
func (a *ChainAPI) ChainStatObj(ctx context.Context, obj cid.Cid, base cid.Cid) (api.ObjStat, error) {
|
||||
|
||||
+29
-55
@@ -347,11 +347,33 @@ func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, tsk types.
|
||||
}
|
||||
|
||||
func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) {
|
||||
ts, err := a.Chain.GetTipSetFromKey(tsk)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
|
||||
msgToReplay := mc
|
||||
var ts *types.TipSet
|
||||
if tsk == types.EmptyTSK {
|
||||
mlkp, err := a.StateSearchMsg(ctx, mc)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("searching for msg %s: %w", mc, err)
|
||||
}
|
||||
if mlkp == nil {
|
||||
return nil, xerrors.Errorf("didn't find msg %s", mc)
|
||||
}
|
||||
|
||||
msgToReplay = mlkp.Message
|
||||
|
||||
executionTs, err := a.Chain.GetTipSetFromKey(mlkp.TipSet)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading tipset %s: %w", mlkp.TipSet, err)
|
||||
}
|
||||
|
||||
ts, err = a.Chain.LoadTipSet(executionTs.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading parent tipset %s: %w", mlkp.TipSet, err)
|
||||
}
|
||||
} else {
|
||||
ts = a.Chain.GetHeaviestTipSet()
|
||||
}
|
||||
m, r, err := a.StateManager.Replay(ctx, ts, mc)
|
||||
|
||||
m, r, err := a.StateManager.Replay(ctx, ts, msgToReplay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -362,9 +384,10 @@ func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.
|
||||
}
|
||||
|
||||
return &api.InvocResult{
|
||||
MsgCid: mc,
|
||||
MsgCid: msgToReplay,
|
||||
Msg: m,
|
||||
MsgRct: &r.MessageReceipt,
|
||||
GasCost: stmgr.MakeMsgGasCost(m, r),
|
||||
ExecutionTrace: r.ExecutionTrace,
|
||||
Error: errstr,
|
||||
Duration: r.Duration,
|
||||
@@ -760,7 +783,7 @@ func (a *StateAPI) StateSectorPartition(ctx context.Context, maddr address.Addre
|
||||
return mas.FindSector(sectorNumber)
|
||||
}
|
||||
|
||||
func (a *StateAPI) StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toheight abi.ChainEpoch) ([]cid.Cid, error) {
|
||||
func (a *StateAPI) StateListMessages(ctx context.Context, match *api.MessageMatch, tsk types.TipSetKey, toheight abi.ChainEpoch) ([]cid.Cid, error) {
|
||||
ts, err := a.Chain.GetTipSetFromKey(tsk)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
|
||||
@@ -1266,52 +1289,3 @@ func (a *StateAPI) StateNetworkVersion(ctx context.Context, tsk types.TipSetKey)
|
||||
|
||||
return a.StateManager.GetNtwkVersion(ctx, ts.Height()), nil
|
||||
}
|
||||
|
||||
func (a *StateAPI) StateMsgGasCost(ctx context.Context, inputMsg cid.Cid, tsk types.TipSetKey) (*api.MsgGasCost, error) {
|
||||
var msg cid.Cid
|
||||
var ts *types.TipSet
|
||||
var err error
|
||||
if tsk != types.EmptyTSK {
|
||||
msg = inputMsg
|
||||
ts, err = a.Chain.LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
|
||||
}
|
||||
} else {
|
||||
mlkp, err := a.StateSearchMsg(ctx, inputMsg)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("searching for msg %s: %w", inputMsg, err)
|
||||
}
|
||||
if mlkp == nil {
|
||||
return nil, xerrors.Errorf("didn't find msg %s", inputMsg)
|
||||
}
|
||||
|
||||
executionTs, err := a.Chain.GetTipSetFromKey(mlkp.TipSet)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading tipset %s: %w", mlkp.TipSet, err)
|
||||
}
|
||||
|
||||
ts, err = a.Chain.LoadTipSet(executionTs.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading parent tipset %s: %w", mlkp.TipSet, err)
|
||||
}
|
||||
|
||||
msg = mlkp.Message
|
||||
}
|
||||
|
||||
m, r, err := a.StateManager.Replay(ctx, ts, msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &api.MsgGasCost{
|
||||
Message: msg,
|
||||
GasUsed: big.NewInt(r.GasUsed),
|
||||
BaseFeeBurn: r.GasCosts.BaseFeeBurn,
|
||||
OverEstimationBurn: r.GasCosts.OverEstimationBurn,
|
||||
MinerPenalty: r.GasCosts.MinerPenalty,
|
||||
MinerTip: r.GasCosts.MinerTip,
|
||||
Refund: r.GasCosts.Refund,
|
||||
TotalCost: big.Sub(m.RequiredFunds(), r.GasCosts.Refund),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
@@ -71,4 +72,5 @@ type SetExpectedSealDurationFunc func(time.Duration) error
|
||||
// too determine how long sealing is expected to take
|
||||
type GetExpectedSealDurationFunc func() (time.Duration, error)
|
||||
|
||||
type DealFilter func(ctx context.Context, deal storagemarket.MinerDeal) (bool, string, error)
|
||||
type StorageDealFilter func(ctx context.Context, deal storagemarket.MinerDeal) (bool, string, error)
|
||||
type RetrievalDealFilter func(ctx context.Context, deal retrievalmarket.ProviderDealState) (bool, string, error)
|
||||
|
||||
@@ -412,16 +412,16 @@ func NewProviderDealFunds(ds dtypes.MetadataDS) (ProviderDealFunds, error) {
|
||||
return funds.NewDealFunds(ds, datastore.NewKey("/marketfunds/provider"))
|
||||
}
|
||||
|
||||
func BasicDealFilter(user dtypes.DealFilter) func(onlineOk dtypes.ConsiderOnlineStorageDealsConfigFunc,
|
||||
func BasicDealFilter(user dtypes.StorageDealFilter) func(onlineOk dtypes.ConsiderOnlineStorageDealsConfigFunc,
|
||||
offlineOk dtypes.ConsiderOfflineStorageDealsConfigFunc,
|
||||
blocklistFunc dtypes.StorageDealPieceCidBlocklistConfigFunc,
|
||||
expectedSealTimeFunc dtypes.GetExpectedSealDurationFunc,
|
||||
spn storagemarket.StorageProviderNode) dtypes.DealFilter {
|
||||
spn storagemarket.StorageProviderNode) dtypes.StorageDealFilter {
|
||||
return func(onlineOk dtypes.ConsiderOnlineStorageDealsConfigFunc,
|
||||
offlineOk dtypes.ConsiderOfflineStorageDealsConfigFunc,
|
||||
blocklistFunc dtypes.StorageDealPieceCidBlocklistConfigFunc,
|
||||
expectedSealTimeFunc dtypes.GetExpectedSealDurationFunc,
|
||||
spn storagemarket.StorageProviderNode) dtypes.DealFilter {
|
||||
spn storagemarket.StorageProviderNode) dtypes.StorageDealFilter {
|
||||
|
||||
return func(ctx context.Context, deal storagemarket.MinerDeal) (bool, string, error) {
|
||||
b, err := onlineOk()
|
||||
@@ -474,7 +474,7 @@ func BasicDealFilter(user dtypes.DealFilter) func(onlineOk dtypes.ConsiderOnline
|
||||
|
||||
// Reject if it's more than 7 days in the future
|
||||
// TODO: read from cfg
|
||||
maxStartEpoch := earliest + abi.ChainEpoch(7*builtin.EpochsInDay)
|
||||
maxStartEpoch := earliest + abi.ChainEpoch(7*builtin.SecondsInDay/build.BlockDelaySecs)
|
||||
if deal.Proposal.StartEpoch > maxStartEpoch {
|
||||
return false, fmt.Sprintf("deal start epoch is too far in the future: %s > %s", deal.Proposal.StartEpoch, maxStartEpoch), nil
|
||||
}
|
||||
@@ -497,7 +497,7 @@ func StorageProvider(minerAddress dtypes.MinerAddress,
|
||||
pieceStore dtypes.ProviderPieceStore,
|
||||
dataTransfer dtypes.ProviderDataTransfer,
|
||||
spn storagemarket.StorageProviderNode,
|
||||
df dtypes.DealFilter,
|
||||
df dtypes.StorageDealFilter,
|
||||
funds ProviderDealFunds,
|
||||
) (storagemarket.StorageProvider, error) {
|
||||
net := smnet.NewFromLibp2pHost(h)
|
||||
@@ -511,8 +511,52 @@ func StorageProvider(minerAddress dtypes.MinerAddress,
|
||||
return storageimpl.NewProvider(net, namespace.Wrap(ds, datastore.NewKey("/deals/provider")), store, mds, pieceStore, dataTransfer, spn, address.Address(minerAddress), ffiConfig.SealProofType, storedAsk, funds, opt)
|
||||
}
|
||||
|
||||
func RetrievalDealFilter(userFilter dtypes.RetrievalDealFilter) func(onlineOk dtypes.ConsiderOnlineRetrievalDealsConfigFunc,
|
||||
offlineOk dtypes.ConsiderOfflineRetrievalDealsConfigFunc) dtypes.RetrievalDealFilter {
|
||||
return func(onlineOk dtypes.ConsiderOnlineRetrievalDealsConfigFunc,
|
||||
offlineOk dtypes.ConsiderOfflineRetrievalDealsConfigFunc) dtypes.RetrievalDealFilter {
|
||||
return func(ctx context.Context, state retrievalmarket.ProviderDealState) (bool, string, error) {
|
||||
b, err := onlineOk()
|
||||
if err != nil {
|
||||
return false, "miner error", err
|
||||
}
|
||||
|
||||
if !b {
|
||||
log.Warn("online retrieval deal consideration disabled; rejecting retrieval deal proposal from client")
|
||||
return false, "miner is not accepting online retrieval deals", nil
|
||||
}
|
||||
|
||||
b, err = offlineOk()
|
||||
if err != nil {
|
||||
return false, "miner error", err
|
||||
}
|
||||
|
||||
if !b {
|
||||
log.Info("offline retrieval has not been implemented yet")
|
||||
}
|
||||
|
||||
if userFilter != nil {
|
||||
return userFilter(ctx, state)
|
||||
}
|
||||
|
||||
return true, "", nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RetrievalProvider creates a new retrieval provider attached to the provider blockstore
|
||||
func RetrievalProvider(h host.Host, miner *storage.Miner, sealer sectorstorage.SectorManager, full lapi.FullNode, ds dtypes.MetadataDS, pieceStore dtypes.ProviderPieceStore, mds dtypes.StagingMultiDstore, dt dtypes.ProviderDataTransfer, onlineOk dtypes.ConsiderOnlineRetrievalDealsConfigFunc, offlineOk dtypes.ConsiderOfflineRetrievalDealsConfigFunc) (retrievalmarket.RetrievalProvider, error) {
|
||||
func RetrievalProvider(h host.Host,
|
||||
miner *storage.Miner,
|
||||
sealer sectorstorage.SectorManager,
|
||||
full lapi.FullNode,
|
||||
ds dtypes.MetadataDS,
|
||||
pieceStore dtypes.ProviderPieceStore,
|
||||
mds dtypes.StagingMultiDstore,
|
||||
dt dtypes.ProviderDataTransfer,
|
||||
onlineOk dtypes.ConsiderOnlineRetrievalDealsConfigFunc,
|
||||
offlineOk dtypes.ConsiderOfflineRetrievalDealsConfigFunc,
|
||||
userFilter dtypes.RetrievalDealFilter,
|
||||
) (retrievalmarket.RetrievalProvider, error) {
|
||||
adapter := retrievaladapter.NewRetrievalProviderNode(miner, sealer, full)
|
||||
|
||||
maddr, err := minerAddrFromDS(ds)
|
||||
@@ -521,29 +565,7 @@ func RetrievalProvider(h host.Host, miner *storage.Miner, sealer sectorstorage.S
|
||||
}
|
||||
|
||||
netwk := rmnet.NewFromLibp2pHost(h)
|
||||
|
||||
opt := retrievalimpl.DealDeciderOpt(func(ctx context.Context, state retrievalmarket.ProviderDealState) (bool, string, error) {
|
||||
b, err := onlineOk()
|
||||
if err != nil {
|
||||
return false, "miner error", err
|
||||
}
|
||||
|
||||
if !b {
|
||||
log.Warn("online retrieval deal consideration disabled; rejecting retrieval deal proposal from client")
|
||||
return false, "miner is not accepting online retrieval deals", nil
|
||||
}
|
||||
|
||||
b, err = offlineOk()
|
||||
if err != nil {
|
||||
return false, "miner error", err
|
||||
}
|
||||
|
||||
if !b {
|
||||
log.Info("offline retrieval has not been implemented yet")
|
||||
}
|
||||
|
||||
return true, "", nil
|
||||
})
|
||||
opt := retrievalimpl.DealDeciderOpt(retrievalimpl.DealDecider(userFilter))
|
||||
|
||||
return retrievalimpl.NewProvider(maddr, adapter, netwk, pieceStore, mds, dt, namespace.Wrap(ds, datastore.NewKey("/retrievals/provider")), opt)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user