Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c6ea75f4b | ||
|
|
1ee9fb44e2 | ||
|
|
473afc41e7 | ||
|
|
a055540c25 | ||
|
|
cb0513f4c1 | ||
|
|
be42dd824b | ||
|
|
06c0444d45 | ||
|
|
bae735447d | ||
|
|
1974490ab0 | ||
|
|
d24d3e420d | ||
|
|
0f5aa9ba5c | ||
|
|
4de8f50642 | ||
|
|
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 |
+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
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
# Lotus changelog
|
||||
|
||||
# 1.0.0 / 2020-10-19
|
||||
|
||||
It's 1.0.0! This is an optional release of Lotus that introduces some UX improvements to the 0.10 series.
|
||||
|
||||
This very small release is largely cosmetic, and intended to flag the code that the Filecoin mainnet was launched with.
|
||||
|
||||
## API changes
|
||||
|
||||
- `StateMsgGasCost` has been removed. The equivalent information can be gained by calling `StateReplay`.
|
||||
- A `GasCost` field has been added to the `InvocResult` type, meaning detailed gas costs will be returned when calling `StateReplay`, `StateCompute`, and `StateCall`.
|
||||
- The behaviour of `StateReplay` in response to an empty tipset key has been changed. Instead of simply using the heaviest tipset (which is almost guaranteed to be an unsuccessful replay), we search now search the chain for the tipset that included the message, and replay the message in that tipset (we fail if no such tipset is found).
|
||||
|
||||
## Changes
|
||||
|
||||
- Increase code coverage! (https://github.com/filecoin-project/lotus/pull/4410)
|
||||
- Mpool: Don't block node startup loading messages (https://github.com/filecoin-project/lotus/pull/4411)
|
||||
- Improve the UX of multisig approves (https://github.com/filecoin-project/lotus/pull/4398)
|
||||
- Use build.BlockDelaySecs for deal start buffer (https://github.com/filecoin-project/lotus/pull/4415)
|
||||
- Conformance: support multiple protocol versions (https://github.com/filecoin-project/lotus/pull/4393)
|
||||
- Ensure msig inspect cli works with lotus-lite (https://github.com/filecoin-project/lotus/pull/4421)
|
||||
- Add command to (slowly) prune lotus chain datastore (https://github.com/filecoin-project/lotus/pull/3876)
|
||||
- Add WalletVerify to lotus-gateway (https://github.com/filecoin-project/lotus/pull/4373)
|
||||
- Improve StateMsg APIs (https://github.com/filecoin-project/lotus/pull/4429)
|
||||
- Add endpoints needed by spacegap (https://github.com/filecoin-project/lotus/pull/4426)
|
||||
- Make audit balances capable of printing robust addresses (https://github.com/filecoin-project/lotus/pull/4423)
|
||||
- Custom filters for retrieval deals (https://github.com/filecoin-project/lotus/pull/4424)
|
||||
- Fix message list api (https://github.com/filecoin-project/lotus/pull/4422)
|
||||
- Replace bootstrap peers (https://github.com/filecoin-project/lotus/pull/4447)
|
||||
- Don't overwrite previously-configured maxPieceSize for a persisted ask (https://github.com/filecoin-project/lotus/pull/4480)
|
||||
- State: optimize state snapshot address cache (https://github.com/filecoin-project/lotus/pull/4481)
|
||||
|
||||
# 0.10.2 / 2020-10-14
|
||||
|
||||
This is an optional release of Lotus that updates markets to 0.9.1, which fixes an issue affecting deals that were mid-transfer when the node was upgraded to 0.9.0. This release also includes some tweaks to default gas values and minor performance improvements.
|
||||
|
||||
+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,9 +1,12 @@
|
||||
/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/bootstrap-0.mainnet.filops.net/tcp/1347/p2p/12D3KooWCVe8MmsEMes2FzgTpt9fXtmCY7wrq91GRiaC8PHSCCBj
|
||||
/dns4/bootstrap-1.mainnet.filops.net/tcp/1347/p2p/12D3KooWCwevHg1yLCvktf2nvLu7L9894mcrJR4MsBCcm4syShVc
|
||||
/dns4/bootstrap-2.mainnet.filops.net/tcp/1347/p2p/12D3KooWEWVwHGn2yR36gKLozmb4YjDJGerotAPGxmdWZx2nxMC4
|
||||
/dns4/bootstrap-3.mainnet.filops.net/tcp/1347/p2p/12D3KooWKhgq8c7NQ9iGjbyK7v7phXvG6492HQfiDaGHLHLQjk7R
|
||||
/dns4/bootstrap-4.mainnet.filops.net/tcp/1347/p2p/12D3KooWL6PsFNPhYftrJzGgF5U18hFoaVhfGk7xwzD8yVrHJ3Uc
|
||||
/dns4/bootstrap-5.mainnet.filops.net/tcp/1347/p2p/12D3KooWLFynvDQiUpXoHroV1YxKHhPJgysQGH2k3ZGwtWzR4dFH
|
||||
/dns4/bootstrap-6.mainnet.filops.net/tcp/1347/p2p/12D3KooWP5MwCiqdMETF9ub1P3MbCvQCcfconnYHbWg6sUJcDRQQ
|
||||
/dns4/bootstrap-7.mainnet.filops.net/tcp/1347/p2p/12D3KooWRs3aY1p3juFjPy8gPN95PEQChm2QKGUCAdcDCC4EBMKf
|
||||
/dns4/bootstrap-8.mainnet.filops.net/tcp/1347/p2p/12D3KooWScFR7385LTyR4zU1bYdzSiiAb5rnNABfVahPvVSzyTkR
|
||||
/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
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ func buildType() string {
|
||||
}
|
||||
|
||||
// BuildVersion is the local build version, set by build system
|
||||
const BuildVersion = "0.10.2"
|
||||
const BuildVersion = "1.0.0"
|
||||
|
||||
func UserVersion() string {
|
||||
return BuildVersion + buildType() + CurrentCommit
|
||||
|
||||
@@ -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
|
||||
|
||||
+46
-21
@@ -26,16 +26,18 @@ var log = logging.Logger("statetree")
|
||||
|
||||
// StateTree stores actors state by their ID.
|
||||
type StateTree struct {
|
||||
root adt.Map
|
||||
version types.StateTreeVersion
|
||||
info cid.Cid
|
||||
Store cbor.IpldStore
|
||||
root adt.Map
|
||||
version types.StateTreeVersion
|
||||
info cid.Cid
|
||||
Store cbor.IpldStore
|
||||
lookupIDFun func(address.Address) (address.Address, error)
|
||||
|
||||
snaps *stateSnaps
|
||||
}
|
||||
|
||||
type stateSnaps struct {
|
||||
layers []*stateSnapLayer
|
||||
layers []*stateSnapLayer
|
||||
lastMaybeNonEmptyResolveCache int
|
||||
}
|
||||
|
||||
type stateSnapLayer struct {
|
||||
@@ -67,7 +69,12 @@ func (ss *stateSnaps) addLayer() {
|
||||
|
||||
func (ss *stateSnaps) dropLayer() {
|
||||
ss.layers[len(ss.layers)-1] = nil // allow it to be GCed
|
||||
|
||||
ss.layers = ss.layers[:len(ss.layers)-1]
|
||||
|
||||
if ss.lastMaybeNonEmptyResolveCache == len(ss.layers) {
|
||||
ss.lastMaybeNonEmptyResolveCache = len(ss.layers) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) mergeLastLayer() {
|
||||
@@ -86,7 +93,13 @@ func (ss *stateSnaps) mergeLastLayer() {
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) resolveAddress(addr address.Address) (address.Address, bool) {
|
||||
for i := len(ss.layers) - 1; i >= 0; i-- {
|
||||
for i := ss.lastMaybeNonEmptyResolveCache; i >= 0; i-- {
|
||||
if len(ss.layers[i].resolveCache) == 0 {
|
||||
if ss.lastMaybeNonEmptyResolveCache == i {
|
||||
ss.lastMaybeNonEmptyResolveCache = i - 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
resa, ok := ss.layers[i].resolveCache[addr]
|
||||
if ok {
|
||||
return resa, true
|
||||
@@ -97,6 +110,7 @@ func (ss *stateSnaps) resolveAddress(addr address.Address) (address.Address, boo
|
||||
|
||||
func (ss *stateSnaps) cacheResolveAddress(addr, resa address.Address) {
|
||||
ss.layers[len(ss.layers)-1].resolveCache[addr] = resa
|
||||
ss.lastMaybeNonEmptyResolveCache = len(ss.layers) - 1
|
||||
}
|
||||
|
||||
func (ss *stateSnaps) getActor(addr address.Address) (*types.Actor, error) {
|
||||
@@ -160,13 +174,15 @@ func NewStateTree(cst cbor.IpldStore, ver types.StateTreeVersion) (*StateTree, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StateTree{
|
||||
s := &StateTree{
|
||||
root: root,
|
||||
info: info,
|
||||
version: ver,
|
||||
Store: cst,
|
||||
snaps: newStateSnaps(),
|
||||
}, nil
|
||||
}
|
||||
s.lookupIDFun = s.lookupIDinternal
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func LoadStateTree(cst cbor.IpldStore, c cid.Cid) (*StateTree, error) {
|
||||
@@ -190,13 +206,15 @@ func LoadStateTree(cst cbor.IpldStore, c cid.Cid) (*StateTree, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StateTree{
|
||||
s := &StateTree{
|
||||
root: nd,
|
||||
info: root.Info,
|
||||
version: root.Version,
|
||||
Store: cst,
|
||||
snaps: newStateSnaps(),
|
||||
}, nil
|
||||
}
|
||||
s.lookupIDFun = s.lookupIDinternal
|
||||
return s, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("unsupported state tree version: %d", root.Version)
|
||||
}
|
||||
@@ -213,17 +231,7 @@ func (st *StateTree) SetActor(addr address.Address, act *types.Actor) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupID gets the ID address of this actor's `addr` stored in the `InitActor`.
|
||||
func (st *StateTree) LookupID(addr address.Address) (address.Address, error) {
|
||||
if addr.Protocol() == address.ID {
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
resa, ok := st.snaps.resolveAddress(addr)
|
||||
if ok {
|
||||
return resa, nil
|
||||
}
|
||||
|
||||
func (st *StateTree) lookupIDinternal(addr address.Address) (address.Address, error) {
|
||||
act, err := st.GetActor(init_.Address)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("getting init actor: %w", err)
|
||||
@@ -241,6 +249,23 @@ func (st *StateTree) LookupID(addr address.Address) (address.Address, error) {
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("resolve address %s: %w", addr, err)
|
||||
}
|
||||
return a, err
|
||||
}
|
||||
|
||||
// LookupID gets the ID address of this actor's `addr` stored in the `InitActor`.
|
||||
func (st *StateTree) LookupID(addr address.Address) (address.Address, error) {
|
||||
if addr.Protocol() == address.ID {
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
resa, ok := st.snaps.resolveAddress(addr)
|
||||
if ok {
|
||||
return resa, nil
|
||||
}
|
||||
a, err := st.lookupIDFun(addr)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
|
||||
st.snaps.cacheResolveAddress(addr, a)
|
||||
|
||||
|
||||
@@ -73,6 +73,103 @@ func BenchmarkStateTreeSetFlush(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCache(t *testing.T) {
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst, VersionForNetwork(build.NewestNetworkVersion))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nonId := address.NewForTestGetter()()
|
||||
id, _ := address.NewIDAddress(1000)
|
||||
|
||||
st.lookupIDFun = func(a address.Address) (address.Address, error) {
|
||||
if a == nonId {
|
||||
return id, nil
|
||||
}
|
||||
return address.Undef, types.ErrActorNotFound
|
||||
}
|
||||
|
||||
err = st.SetActor(nonId, &types.Actor{Nonce: 1})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
{
|
||||
err = st.Snapshot(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
act, err := st.GetActor(nonId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if act.Nonce != 1 {
|
||||
t.Fatalf("expected nonce 1, got %d", act.Nonce)
|
||||
}
|
||||
err = st.SetActor(nonId, &types.Actor{Nonce: 2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
act, err = st.GetActor(nonId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if act.Nonce != 2 {
|
||||
t.Fatalf("expected nonce 2, got %d", act.Nonce)
|
||||
}
|
||||
|
||||
if err := st.Revert(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st.ClearSnapshot()
|
||||
}
|
||||
|
||||
act, err := st.GetActor(nonId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if act.Nonce != 1 {
|
||||
t.Fatalf("expected nonce 1, got %d", act.Nonce)
|
||||
}
|
||||
|
||||
{
|
||||
err = st.Snapshot(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
act, err := st.GetActor(nonId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if act.Nonce != 1 {
|
||||
t.Fatalf("expected nonce 1, got %d", act.Nonce)
|
||||
}
|
||||
err = st.SetActor(nonId, &types.Actor{Nonce: 2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
act, err = st.GetActor(nonId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if act.Nonce != 2 {
|
||||
t.Fatalf("expected nonce 2, got %d", act.Nonce)
|
||||
}
|
||||
st.ClearSnapshot()
|
||||
}
|
||||
|
||||
act, err = st.GetActor(nonId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if act.Nonce != 2 {
|
||||
t.Fatalf("expected nonce 2, got %d", act.Nonce)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkStateTree10kGetActor(b *testing.B) {
|
||||
cst := cbor.NewMemCborStore()
|
||||
st, err := NewStateTree(cst, VersionForNetwork(build.NewestNetworkVersion))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -498,7 +498,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 +799,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,37 @@
|
||||
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"},
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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
@@ -41,7 +41,7 @@ require (
|
||||
github.com/filecoin-project/specs-actors v0.9.12
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.0
|
||||
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
|
||||
|
||||
@@ -282,8 +282,8 @@ github.com/filecoin-project/specs-actors/v2 v2.1.0 h1:ocEuGz8DG2cUWw32c/tvF8D6xT
|
||||
github.com/filecoin-project/specs-actors/v2 v2.1.0/go.mod h1:E7fAX4CZkDVQvDNRCxfq+hc3nx56KcCKyuZf0hlQJ20=
|
||||
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) {
|
||||
|
||||
+33
-55
@@ -347,11 +347,37 @@ 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
|
||||
var err error
|
||||
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, err = a.Chain.LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading specified tipset %s: %w", tsk, err)
|
||||
}
|
||||
}
|
||||
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 +388,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 +787,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 +1293,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)
|
||||
|
||||
@@ -398,8 +398,6 @@ func NewStorageAsk(ctx helpers.MetricsCtx, fapi lapi.FullNode, ds dtypes.Metadat
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a := storedAsk.GetAsk().Ask
|
||||
err = storedAsk.SetAsk(a.Price, a.VerifiedPrice, a.Expiry-a.Timestamp)
|
||||
if err != nil {
|
||||
return storedAsk, err
|
||||
}
|
||||
@@ -412,16 +410,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 +472,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 +495,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 +509,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 +563,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