Merge remote-tracking branch 'origin/master' into feat/async-restartable-workers

This commit is contained in:
Łukasz Magiera 2020-10-13 21:33:21 +02:00
commit 71b3b9075d
156 changed files with 4865 additions and 1134 deletions

1
.gitignore vendored
View File

@ -12,6 +12,7 @@
/lotus-bench /lotus-bench
/lotus-gateway /lotus-gateway
/lotus-pcr /lotus-pcr
/lotus-wallet
/bench.json /bench.json
/lotuspond/front/node_modules /lotuspond/front/node_modules
/lotuspond/front/build /lotuspond/front/build

View File

@ -1,5 +1,44 @@
# Lotus changelog # Lotus changelog
# 0.10.0 / 2020-10-12
This is a consensus-breaking hotfix that addresses an issue in specs-actors v2.0.3 that made it impossible to pledge new 32GiB sectors. The change in Lotus is to update to actors v2.1.0, behind the new network version 5.
## Changes
- make pledge test pass with the race detector (https://github.com/filecoin-project/lotus/pull/4291)
- fix a race in tipset cache usage (https://github.com/filecoin-project/lotus/pull/4282)
- add an api for removing multisig signers (https://github.com/filecoin-project/lotus/pull/4274)
- cli: Don't output errors to stdout (https://github.com/filecoin-project/lotus/pull/4298)
- Fix panic in wallet export when key is not found (https://github.com/filecoin-project/lotus/pull/4299)
- Dump the block validation cache whenever we perform an import (https://github.com/filecoin-project/lotus/pull/4287)
- Fix two races (https://github.com/filecoin-project/lotus/pull/4301)
- sync unmark-bad --all (https://github.com/filecoin-project/lotus/pull/4296)
- decode parameters for multisig transactions in inspect (https://github.com/filecoin-project/lotus/pull/4312)
- Chain is love (https://github.com/filecoin-project/lotus/pull/4321)
- lotus-stats: optmize getting miner power (https://github.com/filecoin-project/lotus/pull/4315)
- implement tape upgrade (https://github.com/filecoin-project/lotus/pull/4322)
# 0.9.1 / 2020-10-10
This release fixes an issue which may cause the actors v2 migration to compute the state incorrectly when more than one migration is running in parallel.
## Changes
- Make concurrent actor migrations safe (https://github.com/filecoin-project/lotus/pull/4293)
- Remote wallet backends (https://github.com/filecoin-project/lotus/pull/3583)
- Track funds in FundMgr correctly in case of AddFunds failing (https://github.com/filecoin-project/lotus/pull/4273)
- Partial lite-node mode (https://github.com/filecoin-project/lotus/pull/4095)
- Fix potential infinite loop in GetBestMiningCandidate (https://github.com/filecoin-project/lotus/pull/3444)
- sync wait: Handle processed message offset (https://github.com/filecoin-project/lotus/pull/4253)
- Add some new endpoints for querying Msig info (https://github.com/filecoin-project/lotus/pull/4250)
- Update markets v0.7.1 (https://github.com/filecoin-project/lotus/pull/4254)
- Optimize SearchForMessage and GetReceipt (https://github.com/filecoin-project/lotus/pull/4246)
- Use FIL instead of attoFIL in CLI more consistently (https://github.com/filecoin-project/lotus/pull/4249)
- fix: clash between daemon --api flag and cli tests (https://github.com/filecoin-project/lotus/pull/4241)
- add more info to chain sync lookback failure (https://github.com/filecoin-project/lotus/pull/4245)
- Add message counts to inspect chain output (https://github.com/filecoin-project/lotus/pull/4230)
# 0.9.0 / 2020-10-07 # 0.9.0 / 2020-10-07
This consensus-breaking release of Lotus upgrades the actors version to v2.0.0. This requires migrating actor state from v0 to v2. The changes that break consensus are: This consensus-breaking release of Lotus upgrades the actors version to v2.0.0. This requires migrating actor state from v0 to v2. The changes that break consensus are:

View File

@ -186,6 +186,12 @@ lotus-health:
.PHONY: lotus-health .PHONY: lotus-health
BINS+=lotus-health BINS+=lotus-health
lotus-wallet:
rm -f lotus-wallet
go build -o lotus-wallet ./cmd/lotus-wallet
.PHONY: lotus-wallet
BINS+=lotus-wallet
testground: testground:
go build -tags testground -o /dev/null ./cmd/lotus go build -tags testground -o /dev/null ./cmd/lotus
.PHONY: testground .PHONY: testground

View File

@ -172,6 +172,9 @@ type FullNode interface {
// SyncUnmarkBad unmarks a blocks as bad, making it possible to be validated and synced again. // SyncUnmarkBad unmarks a blocks as bad, making it possible to be validated and synced again.
SyncUnmarkBad(ctx context.Context, bcid cid.Cid) error SyncUnmarkBad(ctx context.Context, bcid cid.Cid) error
// SyncUnmarkAllBad purges bad block cache, making it possible to sync to chains previously marked as bad
SyncUnmarkAllBad(ctx context.Context) error
// SyncCheckBad checks if a block was marked as bad, and if it was, returns // SyncCheckBad checks if a block was marked as bad, and if it was, returns
// the reason. // the reason.
SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error)
@ -226,7 +229,9 @@ type FullNode interface {
// MethodGroup: Wallet // MethodGroup: Wallet
// WalletNew creates a new address in the wallet with the given sigType. // WalletNew creates a new address in the wallet with the given sigType.
WalletNew(context.Context, crypto.SigType) (address.Address, error) // Available key types: bls, secp256k1, secp256k1-ledger
// Support for numerical types: 1 - secp256k1, 2 - BLS is deprecated
WalletNew(context.Context, types.KeyType) (address.Address, error)
// WalletHas indicates whether the given address is in the wallet. // WalletHas indicates whether the given address is in the wallet.
WalletHas(context.Context, address.Address) (bool, error) WalletHas(context.Context, address.Address) (bool, error)
// WalletList lists all the addresses in the wallet. // WalletList lists all the addresses in the wallet.
@ -367,6 +372,10 @@ type FullNode interface {
// StateWaitMsg looks back in the chain for a message. If not found, it blocks until the // 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. // message arrives on chain, and gets to the indicated confidence depth.
StateWaitMsg(ctx context.Context, cid cid.Cid, confidence uint64) (*MsgLookup, error) StateWaitMsg(ctx context.Context, cid cid.Cid, confidence uint64) (*MsgLookup, error)
// StateWaitMsgLimited looks back up to limit epochs in the chain for a message.
// If not found, it blocks until the message arrives on chain, and gets to the
// indicated confidence depth.
StateWaitMsgLimited(ctx context.Context, cid cid.Cid, confidence uint64, limit abi.ChainEpoch) (*MsgLookup, error)
// StateListMiners returns the addresses of every miner that has claimed power in the Power Actor // StateListMiners returns the addresses of every miner that has claimed power in the Power Actor
StateListMiners(context.Context, types.TipSetKey) ([]address.Address, error) StateListMiners(context.Context, types.TipSetKey) ([]address.Address, error)
// StateListActors returns the addresses of every actor in the state // StateListActors returns the addresses of every actor in the state
@ -407,8 +416,12 @@ type FullNode interface {
// can issue. It takes the deal size and verified status as parameters. // can issue. It takes the deal size and verified status as parameters.
StateDealProviderCollateralBounds(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (DealCollateralBounds, error) StateDealProviderCollateralBounds(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (DealCollateralBounds, error)
// StateCirculatingSupply returns the circulating supply of Filecoin at the given tipset // StateCirculatingSupply returns the exact circulating supply of Filecoin at the given tipset.
StateCirculatingSupply(context.Context, types.TipSetKey) (CirculatingSupply, error) // This is not used anywhere in the protocol itself, and is only for external consumption.
StateCirculatingSupply(context.Context, types.TipSetKey) (abi.TokenAmount, error)
// StateVMCirculatingSupplyInternal returns an approximation of the circulating supply of Filecoin at the given tipset.
// This is the value reported by the runtime interface to actors code.
StateVMCirculatingSupplyInternal(context.Context, types.TipSetKey) (CirculatingSupply, error)
// StateNetworkVersion returns the network version at the given tipset // StateNetworkVersion returns the network version at the given tipset
StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error) StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error)
@ -418,6 +431,8 @@ type FullNode interface {
// MsigGetAvailableBalance returns the portion of a multisig's balance that can be withdrawn or spent // MsigGetAvailableBalance returns the portion of a multisig's balance that can be withdrawn or spent
MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
// MsigGetVestingSchedule returns the vesting details of a given multisig.
MsigGetVestingSchedule(context.Context, address.Address, types.TipSetKey) (MsigVesting, error)
// MsigGetVested returns the amount of FIL that vested in a multisig in a certain period. // MsigGetVested returns the amount of FIL that vested in a multisig in a certain period.
// It takes the following params: <multisig address>, <start epoch>, <end epoch> // It takes the following params: <multisig address>, <start epoch>, <end epoch>
MsigGetVested(context.Context, address.Address, types.TipSetKey, types.TipSetKey) (types.BigInt, error) MsigGetVested(context.Context, address.Address, types.TipSetKey, types.TipSetKey) (types.BigInt, error)
@ -429,12 +444,21 @@ type FullNode interface {
// It takes the following params: <multisig address>, <recipient address>, <value to transfer>, // It takes the following params: <multisig address>, <recipient address>, <value to transfer>,
// <sender address of the propose msg>, <method to call in the proposed message>, <params to include in the proposed message> // <sender address of the propose msg>, <method to call in the proposed message>, <params to include in the proposed message>
MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
// MsigApprove approves a previously-proposed multisig message
// MsigApprove approves a previously-proposed multisig message by transaction ID
// It takes the following params: <multisig address>, <proposed transaction ID> <signer address>
MsigApprove(context.Context, address.Address, uint64, address.Address) (cid.Cid, error)
// MsigApproveTxnHash approves a previously-proposed multisig message, specified
// using both transaction ID and a hash of the parameters used in the
// proposal. This method of approval can be used to ensure you only approve
// exactly the transaction you think you are.
// It takes the following params: <multisig address>, <proposed message ID>, <proposer address>, <recipient address>, <value to transfer>, // It takes the following params: <multisig address>, <proposed message ID>, <proposer address>, <recipient address>, <value to transfer>,
// <sender address of the approve msg>, <method to call in the proposed message>, <params to include in the proposed message> // <sender address of the approve msg>, <method to call in the proposed message>, <params to include in the proposed message>
MsigApprove(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) MsigApproveTxnHash(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
// MsigCancel cancels a previously-proposed multisig message // MsigCancel cancels a previously-proposed multisig message
// It takes the following params: <multisig address>, <proposed message ID>, <recipient address>, <value to transfer>, // It takes the following params: <multisig address>, <proposed transaction ID>, <recipient address>, <value to transfer>,
// <sender address of the cancel msg>, <method to call in the proposed message>, <params to include in the proposed message> // <sender address of the cancel msg>, <method to call in the proposed message>, <params to include in the proposed message>
MsigCancel(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) MsigCancel(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
// MsigAddPropose proposes adding a signer in the multisig // MsigAddPropose proposes adding a signer in the multisig
@ -462,6 +486,13 @@ type FullNode interface {
// <old signer>, <new signer> // <old signer>, <new signer>
MsigSwapCancel(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (cid.Cid, error) MsigSwapCancel(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (cid.Cid, error)
// MsigRemoveSigner proposes the removal of a signer from the multisig.
// It accepts the multisig to make the change on, the proposer address to
// send the message from, the address to be removed, and a boolean
// indicating whether or not the signing threshold should be lowered by one
// along with the address removal.
MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error)
MarketEnsureAvailable(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error) MarketEnsureAvailable(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error)
// MarketFreeBalance // MarketFreeBalance
@ -871,3 +902,15 @@ type Fault struct {
Miner address.Address Miner address.Address
Epoch abi.ChainEpoch Epoch abi.ChainEpoch
} }
var EmptyVesting = MsigVesting{
InitialBalance: types.EmptyInt,
StartEpoch: -1,
UnlockDuration: -1,
}
type MsigVesting struct {
InitialBalance abi.TokenAmount
StartEpoch abi.ChainEpoch
UnlockDuration abi.ChainEpoch
}

24
api/api_gateway.go Normal file
View File

@ -0,0 +1,24 @@
package api
import (
"context"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
)
type GatewayAPI interface {
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)
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)
MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
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)
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*MsgLookup, error)
}

47
api/api_wallet.go Normal file
View File

@ -0,0 +1,47 @@
package api
import (
"context"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/chain/types"
)
type MsgType string
const (
MTUnknown = "unknown"
// Signing message CID. MsgMeta.Extra contains raw cbor message bytes
MTChainMsg = "message"
// Signing a blockheader. signing raw cbor block bytes (MsgMeta.Extra is empty)
MTBlock = "block"
// Signing a deal proposal. signing raw cbor proposal bytes (MsgMeta.Extra is empty)
MTDealProposal = "dealproposal"
// TODO: Deals, Vouchers, VRF
)
type MsgMeta struct {
Type MsgType
// Additional data related to what is signed. Should be verifiable with the
// signed bytes (e.g. CID(Extra).Bytes() == toSign)
Extra []byte
}
type WalletAPI interface {
WalletNew(context.Context, types.KeyType) (address.Address, error)
WalletHas(context.Context, address.Address) (bool, error)
WalletList(context.Context) ([]address.Address, error)
WalletSign(ctx context.Context, signer address.Address, toSign []byte, meta MsgMeta) (*crypto.Signature, error)
WalletExport(context.Context, address.Address) (*types.KeyInfo, error)
WalletImport(context.Context, *types.KeyInfo) (address.Address, error)
WalletDelete(context.Context, address.Address) error
}

View File

@ -36,3 +36,9 @@ func PermissionedWorkerAPI(a api.WorkerAPI) api.WorkerAPI {
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal) auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
return &out return &out
} }
func PermissionedWalletAPI(a api.WalletAPI) api.WalletAPI {
var out WalletStruct
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
return &out
}

View File

@ -111,6 +111,7 @@ type FullNodeStruct struct {
SyncCheckpoint func(ctx context.Context, key types.TipSetKey) error `perm:"admin"` SyncCheckpoint func(ctx context.Context, key types.TipSetKey) error `perm:"admin"`
SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"` SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
SyncUnmarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"` SyncUnmarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
SyncUnmarkAllBad func(ctx context.Context) error `perm:"admin"`
SyncCheckBad func(ctx context.Context, bcid cid.Cid) (string, error) `perm:"read"` SyncCheckBad func(ctx context.Context, bcid cid.Cid) (string, error) `perm:"read"`
SyncValidateTipset func(ctx context.Context, tsk types.TipSetKey) (bool, error) `perm:"read"` SyncValidateTipset func(ctx context.Context, tsk types.TipSetKey) (bool, error) `perm:"read"`
@ -132,7 +133,7 @@ type FullNodeStruct struct {
MinerGetBaseInfo func(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"` MinerGetBaseInfo func(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"`
MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"` MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"`
WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"` WalletNew func(context.Context, types.KeyType) (address.Address, error) `perm:"write"`
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"` WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
WalletList func(context.Context) ([]address.Address, error) `perm:"write"` WalletList func(context.Context) ([]address.Address, error) `perm:"write"`
WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"` WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"`
@ -190,6 +191,7 @@ type FullNodeStruct struct {
StateReadState func(context.Context, address.Address, types.TipSetKey) (*api.ActorState, 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"` 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"` 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"` StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"`
StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"` StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
StateListActors func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"` StateListActors func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
@ -208,14 +210,17 @@ type FullNodeStruct struct {
StateVerifiedClientStatus 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"`
StateVerifiedRegistryRootKey func(ctx context.Context, tsk types.TipSetKey) (address.Address, error) `perm:"read"` StateVerifiedRegistryRootKey func(ctx context.Context, tsk types.TipSetKey) (address.Address, error) `perm:"read"`
StateDealProviderCollateralBounds func(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (api.DealCollateralBounds, error) `perm:"read"` StateDealProviderCollateralBounds func(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (api.DealCollateralBounds, error) `perm:"read"`
StateCirculatingSupply func(context.Context, types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"` StateCirculatingSupply func(context.Context, types.TipSetKey) (abi.TokenAmount, error) `perm:"read"`
StateVMCirculatingSupplyInternal func(context.Context, types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"`
StateNetworkVersion func(context.Context, types.TipSetKey) (stnetwork.Version, error) `perm:"read"` StateNetworkVersion func(context.Context, types.TipSetKey) (stnetwork.Version, error) `perm:"read"`
MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"` MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
MsigGetVestingSchedule func(context.Context, address.Address, types.TipSetKey) (api.MsigVesting, error) `perm:"read"`
MsigGetVested func(context.Context, address.Address, types.TipSetKey, types.TipSetKey) (types.BigInt, error) `perm:"read"` MsigGetVested func(context.Context, address.Address, types.TipSetKey, types.TipSetKey) (types.BigInt, error) `perm:"read"`
MsigCreate func(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"` MsigCreate func(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"`
MsigPropose func(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"` MsigPropose func(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
MsigApprove func(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"` MsigApprove func(context.Context, address.Address, uint64, address.Address) (cid.Cid, error) `perm:"sign"`
MsigApproveTxnHash func(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
MsigCancel func(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"` MsigCancel func(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
MsigAddPropose func(context.Context, address.Address, address.Address, address.Address, bool) (cid.Cid, error) `perm:"sign"` MsigAddPropose func(context.Context, address.Address, address.Address, address.Address, bool) (cid.Cid, error) `perm:"sign"`
MsigAddApprove func(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, bool) (cid.Cid, error) `perm:"sign"` MsigAddApprove func(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, bool) (cid.Cid, error) `perm:"sign"`
@ -223,6 +228,7 @@ type FullNodeStruct struct {
MsigSwapPropose func(context.Context, address.Address, address.Address, address.Address, address.Address) (cid.Cid, error) `perm:"sign"` MsigSwapPropose func(context.Context, address.Address, address.Address, address.Address, address.Address) (cid.Cid, error) `perm:"sign"`
MsigSwapApprove func(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, address.Address) (cid.Cid, error) `perm:"sign"` MsigSwapApprove func(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, address.Address) (cid.Cid, error) `perm:"sign"`
MsigSwapCancel func(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (cid.Cid, error) `perm:"sign"` MsigSwapCancel func(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (cid.Cid, error) `perm:"sign"`
MsigRemoveSigner func(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) `perm:"sign"`
MarketEnsureAvailable func(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"` MarketEnsureAvailable func(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"`
@ -371,6 +377,35 @@ type WorkerStruct struct {
} }
} }
type GatewayStruct struct {
Internal struct {
// TODO: does the gateway need perms?
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)
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)
MsigGetVested func(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
StateAccountKey func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
StateGetActor func(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error)
StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
StateWaitMsg func(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
}
}
type WalletStruct struct {
Internal struct {
WalletNew func(context.Context, types.KeyType) (address.Address, error) `perm:"write"`
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
WalletList func(context.Context) ([]address.Address, error) `perm:"write"`
WalletSign func(context.Context, address.Address, []byte, api.MsgMeta) (*crypto.Signature, error) `perm:"sign"`
WalletExport func(context.Context, address.Address) (*types.KeyInfo, error) `perm:"admin"`
WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"`
WalletDelete func(context.Context, address.Address) error `perm:"write"`
}
}
// CommonStruct // CommonStruct
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) { func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) {
@ -608,7 +643,7 @@ func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.Chain
return c.Internal.ChainGetTipSetByHeight(ctx, h, tsk) return c.Internal.ChainGetTipSetByHeight(ctx, h, tsk)
} }
func (c *FullNodeStruct) WalletNew(ctx context.Context, typ crypto.SigType) (address.Address, error) { func (c *FullNodeStruct) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) {
return c.Internal.WalletNew(ctx, typ) return c.Internal.WalletNew(ctx, typ)
} }
@ -760,6 +795,10 @@ func (c *FullNodeStruct) SyncUnmarkBad(ctx context.Context, bcid cid.Cid) error
return c.Internal.SyncUnmarkBad(ctx, bcid) return c.Internal.SyncUnmarkBad(ctx, bcid)
} }
func (c *FullNodeStruct) SyncUnmarkAllBad(ctx context.Context) error {
return c.Internal.SyncUnmarkAllBad(ctx)
}
func (c *FullNodeStruct) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error) { func (c *FullNodeStruct) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error) {
return c.Internal.SyncCheckBad(ctx, bcid) return c.Internal.SyncCheckBad(ctx, bcid)
} }
@ -864,6 +903,10 @@ func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid, confide
return c.Internal.StateWaitMsg(ctx, msgc, confidence) return c.Internal.StateWaitMsg(ctx, msgc, confidence)
} }
func (c *FullNodeStruct) StateWaitMsgLimited(ctx context.Context, msgc cid.Cid, confidence uint64, limit abi.ChainEpoch) (*api.MsgLookup, error) {
return c.Internal.StateWaitMsgLimited(ctx, msgc, confidence, limit)
}
func (c *FullNodeStruct) StateSearchMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) { func (c *FullNodeStruct) StateSearchMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) {
return c.Internal.StateSearchMsg(ctx, msgc) return c.Internal.StateSearchMsg(ctx, msgc)
} }
@ -932,10 +975,14 @@ func (c *FullNodeStruct) StateDealProviderCollateralBounds(ctx context.Context,
return c.Internal.StateDealProviderCollateralBounds(ctx, size, verified, tsk) return c.Internal.StateDealProviderCollateralBounds(ctx, size, verified, tsk)
} }
func (c *FullNodeStruct) StateCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (api.CirculatingSupply, error) { func (c *FullNodeStruct) StateCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (abi.TokenAmount, error) {
return c.Internal.StateCirculatingSupply(ctx, tsk) return c.Internal.StateCirculatingSupply(ctx, tsk)
} }
func (c *FullNodeStruct) StateVMCirculatingSupplyInternal(ctx context.Context, tsk types.TipSetKey) (api.CirculatingSupply, error) {
return c.Internal.StateVMCirculatingSupplyInternal(ctx, tsk)
}
func (c *FullNodeStruct) StateNetworkVersion(ctx context.Context, tsk types.TipSetKey) (stnetwork.Version, error) { func (c *FullNodeStruct) StateNetworkVersion(ctx context.Context, tsk types.TipSetKey) (stnetwork.Version, error) {
return c.Internal.StateNetworkVersion(ctx, tsk) return c.Internal.StateNetworkVersion(ctx, tsk)
} }
@ -944,6 +991,10 @@ func (c *FullNodeStruct) MsigGetAvailableBalance(ctx context.Context, a address.
return c.Internal.MsigGetAvailableBalance(ctx, a, tsk) return c.Internal.MsigGetAvailableBalance(ctx, a, tsk)
} }
func (c *FullNodeStruct) MsigGetVestingSchedule(ctx context.Context, a address.Address, tsk types.TipSetKey) (api.MsigVesting, error) {
return c.Internal.MsigGetVestingSchedule(ctx, a, tsk)
}
func (c *FullNodeStruct) MsigGetVested(ctx context.Context, a address.Address, sTsk types.TipSetKey, eTsk types.TipSetKey) (types.BigInt, error) { func (c *FullNodeStruct) MsigGetVested(ctx context.Context, a address.Address, sTsk types.TipSetKey, eTsk types.TipSetKey) (types.BigInt, error) {
return c.Internal.MsigGetVested(ctx, a, sTsk, eTsk) return c.Internal.MsigGetVested(ctx, a, sTsk, eTsk)
} }
@ -956,8 +1007,12 @@ func (c *FullNodeStruct) MsigPropose(ctx context.Context, msig address.Address,
return c.Internal.MsigPropose(ctx, msig, to, amt, src, method, params) return c.Internal.MsigPropose(ctx, msig, to, amt, src, method, params)
} }
func (c *FullNodeStruct) MsigApprove(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { func (c *FullNodeStruct) MsigApprove(ctx context.Context, msig address.Address, txID uint64, signer address.Address) (cid.Cid, error) {
return c.Internal.MsigApprove(ctx, msig, txID, proposer, to, amt, src, method, params) return c.Internal.MsigApprove(ctx, msig, txID, signer)
}
func (c *FullNodeStruct) MsigApproveTxnHash(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
return c.Internal.MsigApproveTxnHash(ctx, msig, txID, proposer, to, amt, src, method, params)
} }
func (c *FullNodeStruct) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { func (c *FullNodeStruct) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
@ -988,6 +1043,10 @@ func (c *FullNodeStruct) MsigSwapCancel(ctx context.Context, msig address.Addres
return c.Internal.MsigSwapCancel(ctx, msig, src, txID, oldAdd, newAdd) return c.Internal.MsigSwapCancel(ctx, msig, src, txID, oldAdd, newAdd)
} }
func (c *FullNodeStruct) MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) {
return c.Internal.MsigRemoveSigner(ctx, msig, proposer, toRemove, decrease)
}
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr, wallet address.Address, amt types.BigInt) (cid.Cid, error) { func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr, wallet address.Address, amt types.BigInt) (cid.Cid, error) {
return c.Internal.MarketEnsureAvailable(ctx, addr, wallet, amt) return c.Internal.MarketEnsureAvailable(ctx, addr, wallet, amt)
} }
@ -1422,7 +1481,81 @@ func (w *WorkerStruct) Closing(ctx context.Context) (<-chan struct{}, error) {
return w.Internal.Closing(ctx) return w.Internal.Closing(ctx)
} }
func (g GatewayStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {
return g.Internal.ChainHead(ctx)
}
func (g GatewayStruct) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
return g.Internal.ChainGetTipSet(ctx, tsk)
}
func (g GatewayStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
return g.Internal.ChainGetTipSetByHeight(ctx, h, tsk)
}
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)
}
func (g GatewayStruct) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) {
return g.Internal.MpoolPush(ctx, sm)
}
func (g GatewayStruct) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) {
return g.Internal.MsigGetAvailableBalance(ctx, addr, tsk)
}
func (g GatewayStruct) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) {
return g.Internal.MsigGetVested(ctx, addr, start, end)
}
func (g GatewayStruct) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
return g.Internal.StateAccountKey(ctx, addr, tsk)
}
func (g GatewayStruct) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) {
return g.Internal.StateGetActor(ctx, actor, ts)
}
func (g GatewayStruct) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
return g.Internal.StateLookupID(ctx, addr, tsk)
}
func (g GatewayStruct) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) {
return g.Internal.StateWaitMsg(ctx, msg, confidence)
}
func (c *WalletStruct) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) {
return c.Internal.WalletNew(ctx, typ)
}
func (c *WalletStruct) WalletHas(ctx context.Context, addr address.Address) (bool, error) {
return c.Internal.WalletHas(ctx, addr)
}
func (c *WalletStruct) WalletList(ctx context.Context) ([]address.Address, error) {
return c.Internal.WalletList(ctx)
}
func (c *WalletStruct) WalletSign(ctx context.Context, k address.Address, msg []byte, meta api.MsgMeta) (*crypto.Signature, error) {
return c.Internal.WalletSign(ctx, k, msg, meta)
}
func (c *WalletStruct) WalletExport(ctx context.Context, a address.Address) (*types.KeyInfo, error) {
return c.Internal.WalletExport(ctx, a)
}
func (c *WalletStruct) WalletImport(ctx context.Context, ki *types.KeyInfo) (address.Address, error) {
return c.Internal.WalletImport(ctx, ki)
}
func (c *WalletStruct) WalletDelete(ctx context.Context, addr address.Address) error {
return c.Internal.WalletDelete(ctx, addr)
}
var _ api.Common = &CommonStruct{} var _ api.Common = &CommonStruct{}
var _ api.FullNode = &FullNodeStruct{} var _ api.FullNode = &FullNodeStruct{}
var _ api.StorageMiner = &StorageMinerStruct{} var _ api.StorageMiner = &StorageMinerStruct{}
var _ api.WorkerAPI = &WorkerStruct{} var _ api.WorkerAPI = &WorkerStruct{}
var _ api.GatewayAPI = &GatewayStruct{}
var _ api.WalletAPI = &WalletStruct{}

View File

@ -82,3 +82,29 @@ func NewWorkerRPC(ctx context.Context, addr string, requestHeader http.Header) (
return &res, closer, err return &res, closer, err
} }
// NewGatewayRPC creates a new http jsonrpc client for a gateway node.
func NewGatewayRPC(ctx context.Context, addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.GatewayAPI, jsonrpc.ClientCloser, error) {
var res apistruct.GatewayStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
[]interface{}{
&res.Internal,
},
requestHeader,
opts...,
)
return &res, closer, err
}
func NewWalletRPC(ctx context.Context, addr string, requestHeader http.Header) (api.WalletAPI, jsonrpc.ClientCloser, error) {
var res apistruct.WalletStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
[]interface{}{
&res.Internal,
},
requestHeader,
)
return &res, closer, err
}

View File

@ -87,6 +87,7 @@ func init() {
addExample(abi.RegisteredPoStProof_StackedDrgWindow32GiBV1) addExample(abi.RegisteredPoStProof_StackedDrgWindow32GiBV1)
addExample(abi.ChainEpoch(10101)) addExample(abi.ChainEpoch(10101))
addExample(crypto.SigTypeBLS) addExample(crypto.SigTypeBLS)
addExample(types.KTBLS)
addExample(int64(9)) addExample(int64(9))
addExample(12.3) addExample(12.3)
addExample(123) addExample(123)

View File

@ -12,10 +12,7 @@ import (
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/impl" "github.com/filecoin-project/lotus/node/impl"
) )
@ -37,11 +34,7 @@ func TestCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) {
func testCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, upgradeHeight abi.ChainEpoch) { func testCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, upgradeHeight abi.ChainEpoch) {
ctx := context.Background() ctx := context.Background()
n, sn := b(t, 1, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{ n, sn := b(t, []FullNodeOpts{FullNodeWithUpgradeAt(upgradeHeight)}, OneMiner)
Network: build.ActorUpgradeNetworkVersion,
Height: upgradeHeight,
Migration: stmgr.UpgradeActorsV2,
}}))
client := n[0].FullNode.(*impl.FullNodeAPI) client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0] miner := sn[0]

View File

@ -48,7 +48,7 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport
_ = os.Setenv("BELLMAN_NO_GPU", "1") _ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background() ctx := context.Background()
n, sn := b(t, 1, OneMiner) n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI) client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0] miner := sn[0]
@ -85,7 +85,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
_ = os.Setenv("BELLMAN_NO_GPU", "1") _ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background() ctx := context.Background()
n, sn := b(t, 1, OneMiner) n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI) client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0] miner := sn[0]
@ -149,7 +149,7 @@ func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Durati
_ = os.Setenv("BELLMAN_NO_GPU", "1") _ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background() ctx := context.Background()
n, sn := b(t, 1, OneMiner) n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI) client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0] miner := sn[0]
@ -204,7 +204,7 @@ func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration
_ = os.Setenv("BELLMAN_NO_GPU", "1") _ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background() ctx := context.Background()
n, sn := b(t, 1, OneMiner) n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI) client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0] miner := sn[0]

View File

@ -25,7 +25,7 @@ var log = logging.Logger("apitest")
func (ts *testSuite) testMining(t *testing.T) { func (ts *testSuite) testMining(t *testing.T) {
ctx := context.Background() ctx := context.Background()
apis, sn := ts.makeNodes(t, 1, OneMiner) apis, sn := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0] api := apis[0]
newHeads, err := api.ChainNotify(ctx) newHeads, err := api.ChainNotify(ctx)
@ -54,7 +54,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
}() }()
ctx := context.Background() ctx := context.Background()
apis, sn := ts.makeNodes(t, 1, OneMiner) apis, sn := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0] api := apis[0]
newHeads, err := api.ChainNotify(ctx) newHeads, err := api.ChainNotify(ctx)
@ -93,7 +93,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo
// test making a deal with a fresh miner, and see if it starts to mine // test making a deal with a fresh miner, and see if it starts to mine
ctx := context.Background() ctx := context.Background()
n, sn := b(t, 1, []StorageMiner{ n, sn := b(t, OneFull, []StorageMiner{
{Full: 0, Preseal: PresealGenesis}, {Full: 0, Preseal: PresealGenesis},
{Full: 0, Preseal: 0}, // TODO: Add support for miners on non-first full node {Full: 0, Preseal: 0}, // TODO: Add support for miners on non-first full node
}) })

View File

@ -26,14 +26,13 @@ import (
"github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/chain/events/state" "github.com/filecoin-project/lotus/chain/events/state"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
) )
func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) { func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
_ = os.Setenv("BELLMAN_NO_GPU", "1") _ = os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background() ctx := context.Background()
n, sn := b(t, 2, OneMiner) n, sn := b(t, TwoFull, OneMiner)
paymentCreator := n[0] paymentCreator := n[0]
paymentReceiver := n[1] paymentReceiver := n[1]
@ -58,7 +57,7 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
bm.MineBlocks() bm.MineBlocks()
// send some funds to register the receiver // send some funds to register the receiver
receiverAddr, err := paymentReceiver.WalletNew(ctx, wallet.ActSigType("secp256k1")) receiverAddr, err := paymentReceiver.WalletNew(ctx, types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

114
api/test/tape.go Normal file
View File

@ -0,0 +1,114 @@
package test
import (
"context"
"fmt"
"testing"
"time"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/stmgr"
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/impl"
"github.com/stretchr/testify/require"
)
func TestTapeFix(t *testing.T, b APIBuilder, blocktime time.Duration) {
t.Run("before", func(t *testing.T) { testTapeFix(t, b, blocktime, false) })
t.Run("after", func(t *testing.T) { testTapeFix(t, b, blocktime, true) })
}
func testTapeFix(t *testing.T, b APIBuilder, blocktime time.Duration, after bool) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
upgradeSchedule := stmgr.UpgradeSchedule{{
Network: build.ActorUpgradeNetworkVersion,
Height: 1,
Migration: stmgr.UpgradeActorsV2,
}}
if after {
upgradeSchedule = append(upgradeSchedule, stmgr.Upgrade{
Network: network.Version5,
Height: 2,
})
}
n, sn := b(t, []FullNodeOpts{{Opts: func(_ []TestNode) node.Option {
return node.Override(new(stmgr.UpgradeSchedule), upgradeSchedule)
}}}, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0]
addrinfo, err := client.NetAddrsListen(ctx)
if err != nil {
t.Fatal(err)
}
if err := miner.NetConnect(ctx, addrinfo); err != nil {
t.Fatal(err)
}
build.Clock.Sleep(time.Second)
done := make(chan struct{})
go func() {
defer close(done)
for ctx.Err() == nil {
build.Clock.Sleep(blocktime)
if err := sn[0].MineOne(ctx, MineNext); err != nil {
if ctx.Err() != nil {
// context was canceled, ignore the error.
return
}
t.Error(err)
}
}
}()
defer func() {
cancel()
<-done
}()
err = miner.PledgeSector(ctx)
require.NoError(t, err)
// Wait till done.
var sectorNo abi.SectorNumber
for {
s, err := miner.SectorsList(ctx) // Note - the test builder doesn't import genesis sectors into FSM
require.NoError(t, err)
fmt.Printf("Sectors: %d\n", len(s))
if len(s) == 1 {
sectorNo = s[0]
break
}
build.Clock.Sleep(100 * time.Millisecond)
}
fmt.Printf("All sectors is fsm\n")
// If before, we expect the precommit to fail
successState := api.SectorState(sealing.CommitFailed)
failureState := api.SectorState(sealing.Proving)
if after {
// otherwise, it should succeed.
successState, failureState = failureState, successState
}
for {
st, err := miner.SectorsStatus(ctx, sectorNo, false)
require.NoError(t, err)
if st.State == successState {
break
}
require.NotEqual(t, failureState, st.State)
build.Clock.Sleep(100 * time.Millisecond)
fmt.Println("WaitSeal")
}
}

View File

@ -4,11 +4,15 @@ import (
"context" "context"
"testing" "testing"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/miner"
@ -35,17 +39,27 @@ var PresealGenesis = -1
const GenesisPreseals = 2 const GenesisPreseals = 2
// Options for setting up a mock storage miner
type StorageMiner struct { type StorageMiner struct {
Full int Full int
Preseal int Preseal int
} }
type OptionGenerator func([]TestNode) node.Option
// Options for setting up a mock full node
type FullNodeOpts struct {
Lite bool // run node in "lite" mode
Opts OptionGenerator // generate dependency injection options
}
// APIBuilder is a function which is invoked in test suite to provide // APIBuilder is a function which is invoked in test suite to provide
// test nodes and networks // test nodes and networks
// //
// fullOpts array defines options for each full node
// storage array defines storage nodes, numbers in the array specify full node // storage array defines storage nodes, numbers in the array specify full node
// index the storage node 'belongs' to // index the storage node 'belongs' to
type APIBuilder func(t *testing.T, nFull int, storage []StorageMiner, opts ...node.Option) ([]TestNode, []TestStorageNode) type APIBuilder func(t *testing.T, full []FullNodeOpts, storage []StorageMiner) ([]TestNode, []TestStorageNode)
type testSuite struct { type testSuite struct {
makeNodes APIBuilder makeNodes APIBuilder
} }
@ -63,13 +77,40 @@ func TestApis(t *testing.T, b APIBuilder) {
t.Run("testMiningReal", ts.testMiningReal) t.Run("testMiningReal", ts.testMiningReal)
} }
func DefaultFullOpts(nFull int) []FullNodeOpts {
full := make([]FullNodeOpts, nFull)
for i := range full {
full[i] = FullNodeOpts{
Opts: func(nodes []TestNode) node.Option {
return node.Options()
},
}
}
return full
}
var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}} var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}}
var OneFull = DefaultFullOpts(1)
var TwoFull = DefaultFullOpts(2)
var FullNodeWithUpgradeAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts {
return FullNodeOpts{
Opts: func(nodes []TestNode) node.Option {
return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
// Skip directly to tape height so precommits work.
Network: network.Version5,
Height: upgradeHeight,
Migration: stmgr.UpgradeActorsV2,
}})
},
}
}
func (ts *testSuite) testVersion(t *testing.T) { func (ts *testSuite) testVersion(t *testing.T) {
build.RunningNodeType = build.NodeFull build.RunningNodeType = build.NodeFull
ctx := context.Background() ctx := context.Background()
apis, _ := ts.makeNodes(t, 1, OneMiner) apis, _ := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0] api := apis[0]
v, err := api.Version(ctx) v, err := api.Version(ctx)
@ -81,7 +122,7 @@ func (ts *testSuite) testVersion(t *testing.T) {
func (ts *testSuite) testID(t *testing.T) { func (ts *testSuite) testID(t *testing.T) {
ctx := context.Background() ctx := context.Background()
apis, _ := ts.makeNodes(t, 1, OneMiner) apis, _ := ts.makeNodes(t, OneFull, OneMiner)
api := apis[0] api := apis[0]
id, err := api.ID(ctx) id, err := api.ID(ctx)
@ -93,7 +134,7 @@ func (ts *testSuite) testID(t *testing.T) {
func (ts *testSuite) testConnectTwo(t *testing.T) { func (ts *testSuite) testConnectTwo(t *testing.T) {
ctx := context.Background() ctx := context.Background()
apis, _ := ts.makeNodes(t, 2, OneMiner) apis, _ := ts.makeNodes(t, TwoFull, OneMiner)
p, err := apis[0].NetPeers(ctx) p, err := apis[0].NetPeers(ctx)
if err != nil { if err != nil {

View File

@ -3,6 +3,7 @@ package test
import ( import (
"context" "context"
"fmt" "fmt"
"sync/atomic"
"os" "os"
"strings" "strings"
@ -15,11 +16,9 @@ import (
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/extern/sector-storage/mock" "github.com/filecoin-project/lotus/extern/sector-storage/mock"
sealing "github.com/filecoin-project/lotus/extern/storage-sealing" sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
bminer "github.com/filecoin-project/lotus/miner" bminer "github.com/filecoin-project/lotus/miner"
"github.com/filecoin-project/lotus/node/impl" "github.com/filecoin-project/lotus/node/impl"
@ -33,8 +32,10 @@ func init() {
} }
func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) {
ctx := context.Background() ctx, cancel := context.WithCancel(context.Background())
n, sn := b(t, 1, OneMiner) defer cancel()
n, sn := b(t, OneFull, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI) client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0] miner := sn[0]
@ -48,11 +49,11 @@ func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSect
} }
build.Clock.Sleep(time.Second) build.Clock.Sleep(time.Second)
mine := true mine := int64(1)
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
defer close(done) defer close(done)
for mine { for atomic.LoadInt64(&mine) != 0 {
build.Clock.Sleep(blocktime) build.Clock.Sleep(blocktime)
if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) {
@ -64,7 +65,7 @@ func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSect
pledgeSectors(t, ctx, miner, nSectors, 0, nil) pledgeSectors(t, ctx, miner, nSectors, 0, nil)
mine = false atomic.StoreInt64(&mine, 0)
<-done <-done
} }
@ -133,11 +134,7 @@ func testWindowPostUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration,
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
n, sn := b(t, 1, OneMiner, node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{ n, sn := b(t, []FullNodeOpts{FullNodeWithUpgradeAt(upgradeHeight)}, OneMiner)
Network: build.ActorUpgradeNetworkVersion,
Height: upgradeHeight,
Migration: stmgr.UpgradeActorsV2,
}}))
client := n[0].FullNode.(*impl.FullNodeAPI) client := n[0].FullNode.(*impl.FullNodeAPI)
miner := sn[0] miner := sn[0]

View File

@ -17,9 +17,10 @@ const BreezeGasTampingDuration = 0
const UpgradeSmokeHeight = -1 const UpgradeSmokeHeight = -1
const UpgradeIgnitionHeight = -2 const UpgradeIgnitionHeight = -2
const UpgradeRefuelHeight = -3 const UpgradeRefuelHeight = -3
const UpgradeTapeHeight = -4
var UpgradeActorsV2Height = abi.ChainEpoch(10) var UpgradeActorsV2Height = abi.ChainEpoch(10)
var UpgradeLiftoffHeight = abi.ChainEpoch(-4) var UpgradeLiftoffHeight = abi.ChainEpoch(-5)
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
0: DrandMainnet, 0: DrandMainnet,

View File

@ -30,6 +30,8 @@ const UpgradeRefuelHeight = 130800
var UpgradeActorsV2Height = abi.ChainEpoch(138720) var UpgradeActorsV2Height = abi.ChainEpoch(138720)
const UpgradeTapeHeight = 140760
// This signals our tentative epoch for mainnet launch. Can make it later, but not earlier. // This signals our tentative epoch for mainnet launch. Can make it later, but not earlier.
// Miners, clients, developers, custodians all need time to prepare. // Miners, clients, developers, custodians all need time to prepare.
// We still have upgrades and state changes to do, but can happen after signaling timing here. // We still have upgrades and state changes to do, but can happen after signaling timing here.

View File

@ -25,7 +25,7 @@ const UnixfsLinksPerLevel = 1024
// Consensus / Network // Consensus / Network
const AllowableClockDriftSecs = uint64(1) const AllowableClockDriftSecs = uint64(1)
const NewestNetworkVersion = network.Version4 const NewestNetworkVersion = network.Version5
const ActorUpgradeNetworkVersion = network.Version4 const ActorUpgradeNetworkVersion = network.Version4
// Epochs // Epochs

View File

@ -83,14 +83,15 @@ var (
UpgradeSmokeHeight abi.ChainEpoch = -1 UpgradeSmokeHeight abi.ChainEpoch = -1
UpgradeIgnitionHeight abi.ChainEpoch = -2 UpgradeIgnitionHeight abi.ChainEpoch = -2
UpgradeRefuelHeight abi.ChainEpoch = -3 UpgradeRefuelHeight abi.ChainEpoch = -3
UpgradeTapeHeight abi.ChainEpoch = -4
UpgradeActorsV2Height abi.ChainEpoch = 10 UpgradeActorsV2Height abi.ChainEpoch = 10
UpgradeLiftoffHeight abi.ChainEpoch = -4 UpgradeLiftoffHeight abi.ChainEpoch = -5
DrandSchedule = map[abi.ChainEpoch]DrandEnum{ DrandSchedule = map[abi.ChainEpoch]DrandEnum{
0: DrandMainnet, 0: DrandMainnet,
} }
NewestNetworkVersion = network.Version4 NewestNetworkVersion = network.Version5
ActorUpgradeNetworkVersion = network.Version4 ActorUpgradeNetworkVersion = network.Version4
Devnet = true Devnet = true

View File

@ -29,7 +29,7 @@ func buildType() string {
} }
// BuildVersion is the local build version, set by build system // BuildVersion is the local build version, set by build system
const BuildVersion = "0.9.0" const BuildVersion = "0.10.0"
func UserVersion() string { func UserVersion() string {
return BuildVersion + buildType() + CurrentCommit return BuildVersion + buildType() + CurrentCommit

View File

@ -2,25 +2,28 @@ package builtin
import ( import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"golang.org/x/xerrors" "golang.org/x/xerrors"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/cbor"
"github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
proof0 "github.com/filecoin-project/specs-actors/actors/runtime/proof" proof0 "github.com/filecoin-project/specs-actors/actors/runtime/proof"
smoothing0 "github.com/filecoin-project/specs-actors/actors/util/smoothing" smoothing0 "github.com/filecoin-project/specs-actors/actors/util/smoothing"
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing"
) )
var SystemActorAddr = builtin0.SystemActorAddr var SystemActorAddr = builtin0.SystemActorAddr
var BurntFundsActorAddr = builtin0.BurntFundsActorAddr var BurntFundsActorAddr = builtin0.BurntFundsActorAddr
var CronActorAddr = builtin0.CronActorAddr
var SaftAddress = makeAddress("t0122")
var ReserveAddress = makeAddress("t090") var ReserveAddress = makeAddress("t090")
var RootVerifierAddress = makeAddress("t080") var RootVerifierAddress = makeAddress("t080")

View File

@ -1,6 +1,7 @@
package miner package miner
import ( import (
"github.com/filecoin-project/go-state-types/big"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
@ -148,6 +149,20 @@ type MinerInfo struct {
ConsensusFaultElapsed abi.ChainEpoch ConsensusFaultElapsed abi.ChainEpoch
} }
func (mi MinerInfo) IsController(addr address.Address) bool {
if addr == mi.Owner || addr == mi.Worker {
return true
}
for _, ca := range mi.ControlAddresses {
if addr == ca {
return true
}
}
return false
}
type SectorExpiration struct { type SectorExpiration struct {
OnTime abi.ChainEpoch OnTime abi.ChainEpoch
@ -182,3 +197,7 @@ type LockedFunds struct {
InitialPledgeRequirement abi.TokenAmount InitialPledgeRequirement abi.TokenAmount
PreCommitDeposits abi.TokenAmount PreCommitDeposits abi.TokenAmount
} }
func (lf LockedFunds) TotalLockedFunds() abi.TokenAmount {
return big.Add(lf.VestingFunds, big.Add(lf.InitialPledgeRequirement, lf.PreCommitDeposits))
}

View File

@ -47,8 +47,16 @@ type partition0 struct {
store adt.Store store adt.Store
} }
func (s *state0) AvailableBalance(bal abi.TokenAmount) (abi.TokenAmount, error) { func (s *state0) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) {
return s.GetAvailableBalance(bal), nil defer func() {
if r := recover(); r != nil {
err = xerrors.Errorf("failed to get available balance: %w", r)
available = abi.NewTokenAmount(0)
}
}()
// this panics if the miner doesnt have enough funds to cover their locked pledge
available = s.GetAvailableBalance(bal)
return available, err
} }
func (s *state0) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) { func (s *state0) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) {

View File

@ -45,8 +45,16 @@ type partition2 struct {
store adt.Store store adt.Store
} }
func (s *state2) AvailableBalance(bal abi.TokenAmount) (abi.TokenAmount, error) { func (s *state2) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) {
return s.GetAvailableBalance(bal) defer func() {
if r := recover(); r != nil {
err = xerrors.Errorf("failed to get available balance: %w", r)
available = abi.NewTokenAmount(0)
}
}()
// this panics if the miner doesnt have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal)
return available, err
} }
func (s *state2) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) { func (s *state2) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) {

View File

@ -51,6 +51,7 @@ type State interface {
MinerPower(address.Address) (Claim, bool, error) MinerPower(address.Address) (Claim, bool, error)
MinerNominalPowerMeetsConsensusMinimum(address.Address) (bool, error) MinerNominalPowerMeetsConsensusMinimum(address.Address) (bool, error)
ListAllMiners() ([]address.Address, error) ListAllMiners() ([]address.Address, error)
ForEachClaim(func(miner address.Address, claim Claim) error) error
} }
type Claim struct { type Claim struct {

View File

@ -96,3 +96,22 @@ func (s *state0) ListAllMiners() ([]address.Address, error) {
return miners, nil return miners, nil
} }
func (s *state0) ForEachClaim(cb func(miner address.Address, claim Claim) error) error {
claims, err := adt0.AsMap(s.store, s.Claims)
if err != nil {
return err
}
var claim power0.Claim
return claims.ForEach(&claim, func(k string) error {
a, err := address.NewFromBytes([]byte(k))
if err != nil {
return err
}
return cb(a, Claim{
RawBytePower: claim.RawBytePower,
QualityAdjPower: claim.QualityAdjPower,
})
})
}

View File

@ -96,3 +96,22 @@ func (s *state2) ListAllMiners() ([]address.Address, error) {
return miners, nil return miners, nil
} }
func (s *state2) ForEachClaim(cb func(miner address.Address, claim Claim) error) error {
claims, err := adt2.AsMap(s.store, s.Claims)
if err != nil {
return err
}
var claim power2.Claim
return claims.ForEach(&claim, func(k string) error {
a, err := address.NewFromBytes([]byte(k))
if err != nil {
return err
}
return cb(a, Claim{
RawBytePower: claim.RawBytePower,
QualityAdjPower: claim.QualityAdjPower,
})
})
}

View File

@ -57,7 +57,7 @@ func (s *state0) CumsumBaseline() (abi.StoragePower, error) {
} }
func (s *state0) CumsumRealized() (abi.StoragePower, error) { func (s *state0) CumsumRealized() (abi.StoragePower, error) {
return s.State.CumsumBaseline, nil return s.State.CumsumRealized, nil
} }
func (s *state0) InitialPledgeForPower(sectorWeight abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { func (s *state0) InitialPledgeForPower(sectorWeight abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) {

View File

@ -60,7 +60,7 @@ func (s *state2) CumsumBaseline() (abi.StoragePower, error) {
} }
func (s *state2) CumsumRealized() (abi.StoragePower, error) { func (s *state2) CumsumRealized() (abi.StoragePower, error) {
return s.State.CumsumBaseline, nil return s.State.CumsumRealized, nil
} }
func (s *state2) InitialPledgeForPower(qaPower abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { func (s *state2) InitialPledgeForPower(qaPower abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) {

View File

@ -18,7 +18,7 @@ func VersionForNetwork(version network.Version) Version {
switch version { switch version {
case network.Version0, network.Version1, network.Version2, network.Version3: case network.Version0, network.Version1, network.Version2, network.Version3:
return Version0 return Version0
case network.Version4: case network.Version4, network.Version5:
return Version2 return Version2
default: default:
panic(fmt.Sprintf("unsupported network version %d", version)) panic(fmt.Sprintf("unsupported network version %d", version))

View File

@ -60,6 +60,10 @@ func (bts *BadBlockCache) Remove(c cid.Cid) {
bts.badBlocks.Remove(c) bts.badBlocks.Remove(c)
} }
func (bts *BadBlockCache) Purge() {
bts.badBlocks.Purge()
}
func (bts *BadBlockCache) Has(c cid.Cid) (BadBlockReason, bool) { func (bts *BadBlockCache) Has(c cid.Cid) (BadBlockReason, bool) {
rval, ok := bts.badBlocks.Get(c) rval, ok := bts.badBlocks.Get(c)
if !ok { if !ok {

View File

@ -2,6 +2,7 @@ package events
import ( import (
"context" "context"
"sync"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"golang.org/x/xerrors" "golang.org/x/xerrors"
@ -17,6 +18,8 @@ type tsCacheAPI interface {
// tipSetCache implements a simple ring-buffer cache to keep track of recent // tipSetCache implements a simple ring-buffer cache to keep track of recent
// tipsets // tipsets
type tipSetCache struct { type tipSetCache struct {
mu sync.RWMutex
cache []*types.TipSet cache []*types.TipSet
start int start int
len int len int
@ -35,6 +38,9 @@ func newTSCache(cap abi.ChainEpoch, storage tsCacheAPI) *tipSetCache {
} }
func (tsc *tipSetCache) add(ts *types.TipSet) error { func (tsc *tipSetCache) add(ts *types.TipSet) error {
tsc.mu.Lock()
defer tsc.mu.Unlock()
if tsc.len > 0 { if tsc.len > 0 {
if tsc.cache[tsc.start].Height() >= ts.Height() { if tsc.cache[tsc.start].Height() >= ts.Height() {
return xerrors.Errorf("tipSetCache.add: expected new tipset height to be at least %d, was %d", tsc.cache[tsc.start].Height()+1, ts.Height()) return xerrors.Errorf("tipSetCache.add: expected new tipset height to be at least %d, was %d", tsc.cache[tsc.start].Height()+1, ts.Height())
@ -65,6 +71,13 @@ func (tsc *tipSetCache) add(ts *types.TipSet) error {
} }
func (tsc *tipSetCache) revert(ts *types.TipSet) error { func (tsc *tipSetCache) revert(ts *types.TipSet) error {
tsc.mu.Lock()
defer tsc.mu.Unlock()
return tsc.revertUnlocked(ts)
}
func (tsc *tipSetCache) revertUnlocked(ts *types.TipSet) error {
if tsc.len == 0 { if tsc.len == 0 {
return nil // this can happen, and it's fine return nil // this can happen, and it's fine
} }
@ -77,7 +90,7 @@ func (tsc *tipSetCache) revert(ts *types.TipSet) error {
tsc.start = normalModulo(tsc.start-1, len(tsc.cache)) tsc.start = normalModulo(tsc.start-1, len(tsc.cache))
tsc.len-- tsc.len--
_ = tsc.revert(nil) // revert null block gap _ = tsc.revertUnlocked(nil) // revert null block gap
return nil return nil
} }
@ -95,7 +108,10 @@ func (tsc *tipSetCache) getNonNull(height abi.ChainEpoch) (*types.TipSet, error)
} }
func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) { func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
tsc.mu.RLock()
if tsc.len == 0 { if tsc.len == 0 {
tsc.mu.RUnlock()
log.Warnf("tipSetCache.get: cache is empty, requesting from storage (h=%d)", height) log.Warnf("tipSetCache.get: cache is empty, requesting from storage (h=%d)", height)
return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, types.EmptyTSK) return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, types.EmptyTSK)
} }
@ -103,6 +119,7 @@ func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
headH := tsc.cache[tsc.start].Height() headH := tsc.cache[tsc.start].Height()
if height > headH { if height > headH {
tsc.mu.RUnlock()
return nil, xerrors.Errorf("tipSetCache.get: requested tipset not in cache (req: %d, cache head: %d)", height, headH) return nil, xerrors.Errorf("tipSetCache.get: requested tipset not in cache (req: %d, cache head: %d)", height, headH)
} }
@ -116,15 +133,20 @@ func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
} }
if height < tail.Height() { if height < tail.Height() {
tsc.mu.RUnlock()
log.Warnf("tipSetCache.get: requested tipset not in cache, requesting from storage (h=%d; tail=%d)", height, tail.Height()) log.Warnf("tipSetCache.get: requested tipset not in cache, requesting from storage (h=%d; tail=%d)", height, tail.Height())
return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, tail.Key()) return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, tail.Key())
} }
return tsc.cache[normalModulo(tsc.start-int(headH-height), clen)], nil ts := tsc.cache[normalModulo(tsc.start-int(headH-height), clen)]
tsc.mu.RUnlock()
return ts, nil
} }
func (tsc *tipSetCache) best() (*types.TipSet, error) { func (tsc *tipSetCache) best() (*types.TipSet, error) {
tsc.mu.RLock()
best := tsc.cache[tsc.start] best := tsc.cache[tsc.start]
tsc.mu.RUnlock()
if best == nil { if best == nil {
return tsc.storage.ChainHead(context.TODO()) return tsc.storage.ChainHead(context.TODO())
} }

View File

@ -39,6 +39,7 @@ import (
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed" "github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
"github.com/filecoin-project/lotus/genesis" "github.com/filecoin-project/lotus/genesis"
"github.com/filecoin-project/lotus/journal"
"github.com/filecoin-project/lotus/lib/blockstore" "github.com/filecoin-project/lotus/lib/blockstore"
"github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/lotus/lib/sigs"
"github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/node/repo"
@ -71,7 +72,7 @@ type ChainGen struct {
GetMessages func(*ChainGen) ([]*types.SignedMessage, error) GetMessages func(*ChainGen) ([]*types.SignedMessage, error)
w *wallet.Wallet w *wallet.LocalWallet
eppProvs map[address.Address]WinningPoStProver eppProvs map[address.Address]WinningPoStProver
Miners []address.Address Miners []address.Address
@ -122,6 +123,7 @@ var DefaultRemainderAccountActor = genesis.Actor{
} }
func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) { func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
j := journal.NilJournal()
// TODO: we really shouldn't modify a global variable here. // TODO: we really shouldn't modify a global variable here.
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1) policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
@ -153,14 +155,14 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
return nil, xerrors.Errorf("creating memrepo wallet failed: %w", err) return nil, xerrors.Errorf("creating memrepo wallet failed: %w", err)
} }
banker, err := w.GenerateKey(crypto.SigTypeSecp256k1) banker, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to generate banker key: %w", err) return nil, xerrors.Errorf("failed to generate banker key: %w", err)
} }
receievers := make([]address.Address, msgsPerBlock) receievers := make([]address.Address, msgsPerBlock)
for r := range receievers { for r := range receievers {
receievers[r], err = w.GenerateKey(crypto.SigTypeBLS) receievers[r], err = w.WalletNew(context.Background(), types.KTBLS)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to generate receiver key: %w", err) return nil, xerrors.Errorf("failed to generate receiver key: %w", err)
} }
@ -190,11 +192,11 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
return nil, err return nil, err
} }
mk1, err := w.Import(k1) mk1, err := w.WalletImport(context.Background(), k1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mk2, err := w.Import(k2) mk2, err := w.WalletImport(context.Background(), k2)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -229,12 +231,12 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
Timestamp: uint64(build.Clock.Now().Add(-500 * time.Duration(build.BlockDelaySecs) * time.Second).Unix()), Timestamp: uint64(build.Clock.Now().Add(-500 * time.Duration(build.BlockDelaySecs) * time.Second).Unix()),
} }
genb, err := genesis2.MakeGenesisBlock(context.TODO(), bs, sys, tpl) genb, err := genesis2.MakeGenesisBlock(context.TODO(), j, bs, sys, tpl)
if err != nil { if err != nil {
return nil, xerrors.Errorf("make genesis block failed: %w", err) return nil, xerrors.Errorf("make genesis block failed: %w", err)
} }
cs := store.NewChainStore(bs, ds, sys) cs := store.NewChainStore(bs, ds, sys, j)
genfb := &types.FullBlock{Header: genb.Genesis} genfb := &types.FullBlock{Header: genb.Genesis}
gents := store.NewFullTipSet([]*types.FullBlock{genfb}) gents := store.NewFullTipSet([]*types.FullBlock{genfb})
@ -374,7 +376,13 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m add
return nil, nil, nil, xerrors.Errorf("get miner worker: %w", err) return nil, nil, nil, xerrors.Errorf("get miner worker: %w", err)
} }
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, ticketRand) sf := func(ctx context.Context, a address.Address, i []byte) (*crypto.Signature, error) {
return cg.w.WalletSign(ctx, a, i, api.MsgMeta{
Type: api.MTUnknown,
})
}
vrfout, err := ComputeVRF(ctx, sf, worker, ticketRand)
if err != nil { if err != nil {
return nil, nil, nil, xerrors.Errorf("compute VRF: %w", err) return nil, nil, nil, xerrors.Errorf("compute VRF: %w", err)
} }
@ -506,7 +514,7 @@ func (cg *ChainGen) Banker() address.Address {
return cg.banker return cg.banker
} }
func (cg *ChainGen) Wallet() *wallet.Wallet { func (cg *ChainGen) Wallet() *wallet.LocalWallet {
return cg.w return cg.w
} }
@ -528,7 +536,9 @@ func getRandomMessages(cg *ChainGen) ([]*types.SignedMessage, error) {
GasPremium: types.NewInt(0), GasPremium: types.NewInt(0),
} }
sig, err := cg.w.Sign(context.TODO(), cg.banker, msg.Cid().Bytes()) sig, err := cg.w.WalletSign(context.TODO(), cg.banker, msg.Cid().Bytes(), api.MsgMeta{
Type: api.MTUnknown, // testing
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -559,7 +569,7 @@ type MiningCheckAPI interface {
} }
type mca struct { type mca struct {
w *wallet.Wallet w *wallet.LocalWallet
sm *stmgr.StateManager sm *stmgr.StateManager
pv ffiwrapper.Verifier pv ffiwrapper.Verifier
bcn beacon.Schedule bcn beacon.Schedule
@ -588,7 +598,9 @@ func (mca mca) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoc
} }
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*crypto.Signature, error) { func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*crypto.Signature, error) {
return mca.w.Sign(ctx, a, v) return mca.w.WalletSign(ctx, a, v, api.MsgMeta{
Type: api.MTUnknown,
})
} }
type WinningPoStProver interface { type WinningPoStProver interface {

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/journal"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore"
@ -466,7 +467,10 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot ci
return st, nil return st, nil
} }
func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys vm.SyscallBuilder, template genesis.Template) (*GenesisBootstrap, error) { func MakeGenesisBlock(ctx context.Context, j journal.Journal, bs bstore.Blockstore, sys vm.SyscallBuilder, template genesis.Template) (*GenesisBootstrap, error) {
if j == nil {
j = journal.NilJournal()
}
st, keyIDs, err := MakeInitialStateTree(ctx, bs, template) st, keyIDs, err := MakeInitialStateTree(ctx, bs, template)
if err != nil { if err != nil {
return nil, xerrors.Errorf("make initial state tree failed: %w", err) return nil, xerrors.Errorf("make initial state tree failed: %w", err)
@ -478,7 +482,7 @@ func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys vm.SyscallB
} }
// temp chainstore // temp chainstore
cs := store.NewChainStore(bs, datastore.NewMapDatastore(), sys) cs := store.NewChainStore(bs, datastore.NewMapDatastore(), sys, j)
// Verify PreSealed Data // Verify PreSealed Data
stateroot, err = VerifyPreSealedData(ctx, cs, stateroot, template, keyIDs) stateroot, err = VerifyPreSealedData(ctx, cs, stateroot, template, keyIDs)

View File

@ -15,11 +15,10 @@ import (
"github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/lib/sigs/bls" "github.com/filecoin-project/lotus/lib/sigs/bls"
) )
func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, bt *api.BlockTemplate) (*types.FullBlock, error) { func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.WalletAPI, bt *api.BlockTemplate) (*types.FullBlock, error) {
pts, err := sm.ChainStore().LoadTipSet(bt.Parents) pts, err := sm.ChainStore().LoadTipSet(bt.Parents)
if err != nil { if err != nil {
@ -131,7 +130,9 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal
return nil, xerrors.Errorf("failed to get signing bytes for block: %w", err) return nil, xerrors.Errorf("failed to get signing bytes for block: %w", err)
} }
sig, err := w.Sign(ctx, waddr, nosigbytes) sig, err := w.WalletSign(ctx, waddr, nosigbytes, api.MsgMeta{
Type: api.MTBlock,
})
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to sign new block: %w", err) return nil, xerrors.Errorf("failed to sign new block: %w", err)
} }

View File

@ -120,9 +120,10 @@ func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr, wallet address.Add
return cid.Undef, err return cid.Undef, err
} }
fm.lk.Lock() fm.lk.Lock()
defer fm.lk.Unlock()
bal, err := fm.api.StateMarketBalance(ctx, addr, types.EmptyTSK) bal, err := fm.api.StateMarketBalance(ctx, addr, types.EmptyTSK)
if err != nil { if err != nil {
fm.lk.Unlock()
return cid.Undef, err return cid.Undef, err
} }
@ -138,7 +139,6 @@ func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr, wallet address.Add
toAdd = types.NewInt(0) toAdd = types.NewInt(0)
} }
fm.available[idAddr] = big.Add(avail, toAdd) fm.available[idAddr] = big.Add(avail, toAdd)
fm.lk.Unlock()
log.Infof("Funds operation w/ Expected Balance: %s, In State: %s, Requested: %s, Adding: %s", avail.String(), stateAvail.String(), amt.String(), toAdd.String()) log.Infof("Funds operation w/ Expected Balance: %s, In State: %s, Requested: %s, Adding: %s", avail.String(), stateAvail.String(), amt.String(), toAdd.String())
@ -148,6 +148,7 @@ func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr, wallet address.Add
params, err := actors.SerializeParams(&addr) params, err := actors.SerializeParams(&addr)
if err != nil { if err != nil {
fm.available[idAddr] = avail
return cid.Undef, err return cid.Undef, err
} }
@ -159,6 +160,7 @@ func (fm *FundMgr) EnsureAvailable(ctx context.Context, addr, wallet address.Add
Params: params, Params: params,
}, nil) }, nil)
if err != nil { if err != nil {
fm.available[idAddr] = avail
return cid.Undef, err return cid.Undef, err
} }

View File

@ -60,6 +60,7 @@ type expectedResult struct {
addAmt abi.TokenAmount addAmt abi.TokenAmount
shouldAdd bool shouldAdd bool
err error err error
cachedAvailable abi.TokenAmount
} }
func TestAddFunds(t *testing.T) { func TestAddFunds(t *testing.T) {
@ -90,6 +91,7 @@ func TestAddFunds(t *testing.T) {
{ {
shouldAdd: false, shouldAdd: false,
err: nil, err: nil,
cachedAvailable: abi.NewTokenAmount(100),
}, },
}, },
}, },
@ -105,15 +107,18 @@ func TestAddFunds(t *testing.T) {
addAmt: abi.NewTokenAmount(100), addAmt: abi.NewTokenAmount(100),
shouldAdd: true, shouldAdd: true,
err: nil, err: nil,
cachedAvailable: abi.NewTokenAmount(200),
}, },
{ {
addAmt: abi.NewTokenAmount(50), addAmt: abi.NewTokenAmount(50),
shouldAdd: true, shouldAdd: true,
err: nil, err: nil,
cachedAvailable: abi.NewTokenAmount(250),
}, },
{ {
shouldAdd: false, shouldAdd: false,
err: nil, err: nil,
cachedAvailable: abi.NewTokenAmount(250),
}, },
}, },
}, },
@ -133,6 +138,7 @@ func TestAddFunds(t *testing.T) {
expectedResults: []expectedResult{ expectedResults: []expectedResult{
{ {
err: errors.New("something went wrong"), err: errors.New("something went wrong"),
cachedAvailable: abi.NewTokenAmount(0),
}, },
}, },
}, },
@ -183,6 +189,10 @@ func TestAddFunds(t *testing.T) {
} else { } else {
require.EqualError(t, err, expected.err.Error()) require.EqualError(t, err, expected.err.Error())
} }
if !expected.cachedAvailable.Nil() {
require.Equal(t, expected.cachedAvailable, fundMgr.available[addr])
}
} }
}) })
} }

View File

@ -159,6 +159,7 @@ type MessagePool struct {
sigValCache *lru.TwoQueueCache sigValCache *lru.TwoQueueCache
evtTypes [3]journal.EventType evtTypes [3]journal.EventType
journal journal.Journal
} }
type msgSet struct { type msgSet struct {
@ -316,7 +317,7 @@ func (ms *msgSet) getRequiredFunds(nonce uint64) types.BigInt {
return types.BigInt{Int: requiredFunds} return types.BigInt{Int: requiredFunds}
} }
func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*MessagePool, error) { func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) {
cache, _ := lru.New2Q(build.BlsSignatureCacheSize) cache, _ := lru.New2Q(build.BlsSignatureCacheSize)
verifcache, _ := lru.New2Q(build.VerifSigCacheSize) verifcache, _ := lru.New2Q(build.VerifSigCacheSize)
@ -325,6 +326,10 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
return nil, xerrors.Errorf("error loading mpool config: %w", err) return nil, xerrors.Errorf("error loading mpool config: %w", err)
} }
if j == nil {
j = journal.NilJournal()
}
mp := &MessagePool{ mp := &MessagePool{
ds: ds, ds: ds,
addSema: make(chan struct{}, 1), addSema: make(chan struct{}, 1),
@ -344,10 +349,11 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
netName: netName, netName: netName,
cfg: cfg, cfg: cfg,
evtTypes: [...]journal.EventType{ evtTypes: [...]journal.EventType{
evtTypeMpoolAdd: journal.J.RegisterEventType("mpool", "add"), evtTypeMpoolAdd: j.RegisterEventType("mpool", "add"),
evtTypeMpoolRemove: journal.J.RegisterEventType("mpool", "remove"), evtTypeMpoolRemove: j.RegisterEventType("mpool", "remove"),
evtTypeMpoolRepub: journal.J.RegisterEventType("mpool", "repub"), evtTypeMpoolRepub: j.RegisterEventType("mpool", "repub"),
}, },
journal: j,
} }
// enable initial prunes // enable initial prunes
@ -744,7 +750,7 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage, strict, untrusted bool)
Message: m, Message: m,
}, localUpdates) }, localUpdates)
journal.J.RecordEvent(mp.evtTypes[evtTypeMpoolAdd], func() interface{} { mp.journal.RecordEvent(mp.evtTypes[evtTypeMpoolAdd], func() interface{} {
return MessagePoolEvt{ return MessagePoolEvt{
Action: "add", Action: "add",
Messages: []MessagePoolEvtMessage{{Message: m.Message, CID: m.Cid()}}, Messages: []MessagePoolEvtMessage{{Message: m.Message, CID: m.Cid()}},
@ -865,7 +871,7 @@ func (mp *MessagePool) remove(from address.Address, nonce uint64, applied bool)
Message: m, Message: m,
}, localUpdates) }, localUpdates)
journal.J.RecordEvent(mp.evtTypes[evtTypeMpoolRemove], func() interface{} { mp.journal.RecordEvent(mp.evtTypes[evtTypeMpoolRemove], func() interface{} {
return MessagePoolEvt{ return MessagePoolEvt{
Action: "remove", Action: "remove",
Messages: []MessagePoolEvtMessage{{Message: m.Message, CID: m.Cid()}}} Messages: []MessagePoolEvtMessage{{Message: m.Message, CID: m.Cid()}}}

View File

@ -8,7 +8,6 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/chain/messagepool/gasguess" "github.com/filecoin-project/lotus/chain/messagepool/gasguess"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock" "github.com/filecoin-project/lotus/chain/types/mock"
@ -225,14 +224,14 @@ func TestMessagePool(t *testing.T) {
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
a := tma.nextBlock() a := tma.nextBlock()
sender, err := w.GenerateKey(crypto.SigTypeBLS) sender, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -266,14 +265,14 @@ func TestMessagePoolMessagesInEachBlock(t *testing.T) {
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
a := tma.nextBlock() a := tma.nextBlock()
sender, err := w.GenerateKey(crypto.SigTypeBLS) sender, err := w.WalletNew(context.Background(), types.KTBLS)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -315,7 +314,7 @@ func TestRevertMessages(t *testing.T) {
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -323,7 +322,7 @@ func TestRevertMessages(t *testing.T) {
a := tma.nextBlock() a := tma.nextBlock()
b := tma.nextBlock() b := tma.nextBlock()
sender, err := w.GenerateKey(crypto.SigTypeBLS) sender, err := w.WalletNew(context.Background(), types.KTBLS)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -378,7 +377,7 @@ func TestPruningSimple(t *testing.T) {
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -386,7 +385,7 @@ func TestPruningSimple(t *testing.T) {
a := tma.nextBlock() a := tma.nextBlock()
tma.applyBlock(t, a) tma.applyBlock(t, a)
sender, err := w.GenerateKey(crypto.SigTypeBLS) sender, err := w.WalletNew(context.Background(), types.KTBLS)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -422,7 +421,7 @@ func TestLoadLocal(t *testing.T) {
tma := newTestMpoolAPI() tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -433,7 +432,7 @@ func TestLoadLocal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -443,7 +442,7 @@ func TestLoadLocal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -465,7 +464,7 @@ func TestLoadLocal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
mp, err = New(tma, ds, "mptest") mp, err = New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -494,7 +493,7 @@ func TestClearAll(t *testing.T) {
tma := newTestMpoolAPI() tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -505,7 +504,7 @@ func TestClearAll(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -515,7 +514,7 @@ func TestClearAll(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -548,7 +547,7 @@ func TestClearNonLocal(t *testing.T) {
tma := newTestMpoolAPI() tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -559,7 +558,7 @@ func TestClearNonLocal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -569,7 +568,7 @@ func TestClearNonLocal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -609,7 +608,7 @@ func TestUpdates(t *testing.T) {
tma := newTestMpoolAPI() tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -620,7 +619,7 @@ func TestUpdates(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -630,7 +629,7 @@ func TestUpdates(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -11,7 +11,6 @@ import (
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/messagepool/gasguess" "github.com/filecoin-project/lotus/chain/messagepool/gasguess"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/journal"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
) )
@ -148,14 +147,14 @@ loop:
} }
if len(msgs) > 0 { if len(msgs) > 0 {
journal.J.RecordEvent(mp.evtTypes[evtTypeMpoolRepub], func() interface{} { mp.journal.RecordEvent(mp.evtTypes[evtTypeMpoolRepub], func() interface{} {
msgs := make([]MessagePoolEvtMessage, 0, len(msgs)) msgsEv := make([]MessagePoolEvtMessage, 0, len(msgs))
for _, m := range msgs { for _, m := range msgs {
msgs = append(msgs, MessagePoolEvtMessage{Message: m.Message, CID: m.Cid()}) msgsEv = append(msgsEv, MessagePoolEvtMessage{Message: m.Message, CID: m.Cid()})
} }
return MessagePoolEvt{ return MessagePoolEvt{
Action: "repub", Action: "repub",
Messages: msgs, Messages: msgsEv,
} }
}) })
} }

View File

@ -1,11 +1,12 @@
package messagepool package messagepool
import ( import (
"context"
"testing" "testing"
"time" "time"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/chain/messagepool/gasguess" "github.com/filecoin-project/lotus/chain/messagepool/gasguess"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore"
@ -21,7 +22,7 @@ func TestRepubMessages(t *testing.T) {
tma := newTestMpoolAPI() tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest") mp, err := New(tma, ds, "mptest", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -32,7 +33,7 @@ func TestRepubMessages(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -42,7 +43,7 @@ func TestRepubMessages(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -13,20 +13,21 @@ import (
"sort" "sort"
"testing" "testing"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
logging "github.com/ipfs/go-log"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/messagepool/gasguess" "github.com/filecoin-project/lotus/chain/messagepool/gasguess"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock" "github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/filecoin-project/lotus/api"
_ "github.com/filecoin-project/lotus/lib/sigs/bls" _ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp" _ "github.com/filecoin-project/lotus/lib/sigs/secp"
logging "github.com/ipfs/go-log"
) )
func init() { func init() {
@ -34,7 +35,7 @@ func init() {
MaxActorPendingMessages = 1000000 MaxActorPendingMessages = 1000000
} }
func makeTestMessage(w *wallet.Wallet, from, to address.Address, nonce uint64, gasLimit int64, gasPrice uint64) *types.SignedMessage { func makeTestMessage(w *wallet.LocalWallet, from, to address.Address, nonce uint64, gasLimit int64, gasPrice uint64) *types.SignedMessage {
msg := &types.Message{ msg := &types.Message{
From: from, From: from,
To: to, To: to,
@ -45,7 +46,7 @@ func makeTestMessage(w *wallet.Wallet, from, to address.Address, nonce uint64, g
GasFeeCap: types.NewInt(100 + gasPrice), GasFeeCap: types.NewInt(100 + gasPrice),
GasPremium: types.NewInt(gasPrice), GasPremium: types.NewInt(gasPrice),
} }
sig, err := w.Sign(context.TODO(), from, msg.Cid().Bytes()) sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -58,7 +59,7 @@ func makeTestMessage(w *wallet.Wallet, from, to address.Address, nonce uint64, g
func makeTestMpool() (*MessagePool, *testMpoolAPI) { func makeTestMpool() (*MessagePool, *testMpoolAPI) {
tma := newTestMpoolAPI() tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore() ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "test") mp, err := New(tma, ds, "test", nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -75,7 +76,7 @@ func TestMessageChains(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -85,7 +86,7 @@ func TestMessageChains(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -313,7 +314,7 @@ func TestMessageChainSkipping(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -323,7 +324,7 @@ func TestMessageChainSkipping(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -389,7 +390,7 @@ func TestBasicMessageSelection(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -399,7 +400,7 @@ func TestBasicMessageSelection(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -533,7 +534,7 @@ func TestMessageSelectionTrimming(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -543,7 +544,7 @@ func TestMessageSelectionTrimming(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -596,7 +597,7 @@ func TestPriorityMessageSelection(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -606,7 +607,7 @@ func TestPriorityMessageSelection(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -675,7 +676,7 @@ func TestPriorityMessageSelection2(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -685,7 +686,7 @@ func TestPriorityMessageSelection2(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -744,7 +745,7 @@ func TestPriorityMessageSelection3(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -754,7 +755,7 @@ func TestPriorityMessageSelection3(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -841,7 +842,7 @@ func TestOptimalMessageSelection1(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -851,7 +852,7 @@ func TestOptimalMessageSelection1(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -908,7 +909,7 @@ func TestOptimalMessageSelection2(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -918,7 +919,7 @@ func TestOptimalMessageSelection2(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -984,7 +985,7 @@ func TestOptimalMessageSelection3(t *testing.T) {
nActors := 10 nActors := 10
// the actors // the actors
var actors []address.Address var actors []address.Address
var wallets []*wallet.Wallet var wallets []*wallet.LocalWallet
for i := 0; i < nActors; i++ { for i := 0; i < nActors; i++ {
w, err := wallet.NewWallet(wallet.NewMemKeyStore()) w, err := wallet.NewWallet(wallet.NewMemKeyStore())
@ -992,7 +993,7 @@ func TestOptimalMessageSelection3(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
a, err := w.GenerateKey(crypto.SigTypeSecp256k1) a, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1064,7 +1065,7 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
nActors := 300 nActors := 300
// the actors // the actors
var actors []address.Address var actors []address.Address
var wallets []*wallet.Wallet var wallets []*wallet.LocalWallet
for i := 0; i < nActors; i++ { for i := 0; i < nActors; i++ {
w, err := wallet.NewWallet(wallet.NewMemKeyStore()) w, err := wallet.NewWallet(wallet.NewMemKeyStore())
@ -1072,7 +1073,7 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
t.Fatal(err) t.Fatal(err)
} }
a, err := w.GenerateKey(crypto.SigTypeSecp256k1) a, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1330,7 +1331,7 @@ readLoop:
} }
actorMap := make(map[address.Address]address.Address) actorMap := make(map[address.Address]address.Address)
actorWallets := make(map[address.Address]*wallet.Wallet) actorWallets := make(map[address.Address]api.WalletAPI)
for _, m := range msgs { for _, m := range msgs {
baseNonce := baseNonces[m.Message.From] baseNonce := baseNonces[m.Message.From]
@ -1342,7 +1343,7 @@ readLoop:
t.Fatal(err) t.Fatal(err)
} }
a, err := w.GenerateKey(crypto.SigTypeSecp256k1) a, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1360,7 +1361,7 @@ readLoop:
m.Message.From = localActor m.Message.From = localActor
m.Message.Nonce -= baseNonce m.Message.Nonce -= baseNonce
sig, err := w.Sign(context.TODO(), localActor, m.Message.Cid().Bytes()) sig, err := w.WalletSign(context.TODO(), localActor, m.Message.Cid().Bytes(), api.MsgMeta{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -5,40 +5,37 @@ import (
"context" "context"
"sync" "sync"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace" "github.com/ipfs/go-datastore/namespace"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes"
) )
const dsKeyActorNonce = "ActorNextNonce" const dsKeyActorNonce = "ActorNextNonce"
var log = logging.Logger("messagesigner") var log = logging.Logger("messagesigner")
type mpoolAPI interface { type MpoolNonceAPI interface {
GetNonce(address.Address) (uint64, error) GetNonce(address.Address) (uint64, error)
} }
// MessageSigner keeps track of nonces per address, and increments the nonce // MessageSigner keeps track of nonces per address, and increments the nonce
// when signing a message // when signing a message
type MessageSigner struct { type MessageSigner struct {
wallet *wallet.Wallet wallet api.WalletAPI
lk sync.Mutex lk sync.Mutex
mpool mpoolAPI mpool MpoolNonceAPI
ds datastore.Batching ds datastore.Batching
} }
func NewMessageSigner(wallet *wallet.Wallet, mpool *messagepool.MessagePool, ds dtypes.MetadataDS) *MessageSigner { func NewMessageSigner(wallet api.WalletAPI, mpool MpoolNonceAPI, ds dtypes.MetadataDS) *MessageSigner {
return newMessageSigner(wallet, mpool, ds)
}
func newMessageSigner(wallet *wallet.Wallet, mpool mpoolAPI, ds dtypes.MetadataDS) *MessageSigner {
ds = namespace.Wrap(ds, datastore.NewKey("/message-signer/")) ds = namespace.Wrap(ds, datastore.NewKey("/message-signer/"))
return &MessageSigner{ return &MessageSigner{
wallet: wallet, wallet: wallet,
@ -61,7 +58,16 @@ func (ms *MessageSigner) SignMessage(ctx context.Context, msg *types.Message, cb
// Sign the message with the nonce // Sign the message with the nonce
msg.Nonce = nonce msg.Nonce = nonce
sig, err := ms.wallet.Sign(ctx, msg.From, msg.Cid().Bytes())
mb, err := msg.ToStorageBlock()
if err != nil {
return nil, xerrors.Errorf("serializing message: %w", err)
}
sig, err := ms.wallet.WalletSign(ctx, msg.From, mb.Cid().Bytes(), api.MsgMeta{
Type: api.MTChainMsg,
Extra: mb.RawData(),
})
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to sign message: %w", err) return nil, xerrors.Errorf("failed to sign message: %w", err)
} }

View File

@ -9,7 +9,6 @@ import (
"github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
ds_sync "github.com/ipfs/go-datastore/sync" ds_sync "github.com/ipfs/go-datastore/sync"
@ -47,13 +46,13 @@ func TestMessageSignerSignMessage(t *testing.T) {
ctx := context.Background() ctx := context.Background()
w, _ := wallet.NewWallet(wallet.NewMemKeyStore()) w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
from1, err := w.GenerateKey(crypto.SigTypeSecp256k1) from1, err := w.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err) require.NoError(t, err)
from2, err := w.GenerateKey(crypto.SigTypeSecp256k1) from2, err := w.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err) require.NoError(t, err)
to1, err := w.GenerateKey(crypto.SigTypeSecp256k1) to1, err := w.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err) require.NoError(t, err)
to2, err := w.GenerateKey(crypto.SigTypeSecp256k1) to2, err := w.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err) require.NoError(t, err)
type msgSpec struct { type msgSpec struct {
@ -177,7 +176,7 @@ func TestMessageSignerSignMessage(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
mpool := newMockMpool() mpool := newMockMpool()
ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) ds := ds_sync.MutexWrap(datastore.NewMapDatastore())
ms := newMessageSigner(w, mpool, ds) ms := NewMessageSigner(w, mpool, ds)
for _, m := range tt.msgs { for _, m := range tt.msgs {
if len(m.mpoolNonce) == 1 { if len(m.mpoolNonce) == 1 {

View File

@ -61,7 +61,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
Rand: store.NewChainRand(sm.cs, ts.Cids()), Rand: store.NewChainRand(sm.cs, ts.Cids()),
Bstore: sm.cs.Blockstore(), Bstore: sm.cs.Blockstore(),
Syscalls: sm.cs.VMSys(), Syscalls: sm.cs.VMSys(),
CircSupplyCalc: sm.GetCirculatingSupply, CircSupplyCalc: sm.GetVMCirculatingSupply,
NtwkVersion: sm.GetNtwkVersion, NtwkVersion: sm.GetNtwkVersion,
BaseFee: types.NewInt(0), BaseFee: types.NewInt(0),
} }
@ -174,7 +174,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
Rand: r, Rand: r,
Bstore: sm.cs.Blockstore(), Bstore: sm.cs.Blockstore(),
Syscalls: sm.cs.VMSys(), Syscalls: sm.cs.VMSys(),
CircSupplyCalc: sm.GetCirculatingSupply, CircSupplyCalc: sm.GetVMCirculatingSupply,
NtwkVersion: sm.GetNtwkVersion, NtwkVersion: sm.GetNtwkVersion,
BaseFee: ts.Blocks()[0].ParentBaseFee, BaseFee: ts.Blocks()[0].ParentBaseFee,
} }

View File

@ -24,7 +24,6 @@ import (
"github.com/filecoin-project/specs-actors/actors/migration/nv3" "github.com/filecoin-project/specs-actors/actors/migration/nv3"
m2 "github.com/filecoin-project/specs-actors/v2/actors/migration" m2 "github.com/filecoin-project/specs-actors/v2/actors/migration"
states2 "github.com/filecoin-project/specs-actors/v2/actors/states"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/adt"
@ -80,9 +79,12 @@ func DefaultUpgradeSchedule() UpgradeSchedule {
Network: network.Version4, Network: network.Version4,
Expensive: true, Expensive: true,
Migration: UpgradeActorsV2, Migration: UpgradeActorsV2,
}, {
Height: build.UpgradeTapeHeight,
Network: network.Version5,
}, { }, {
Height: build.UpgradeLiftoffHeight, Height: build.UpgradeLiftoffHeight,
Network: network.Version4, Network: network.Version5,
Migration: UpgradeLiftoff, Migration: UpgradeLiftoff,
}} }}
@ -263,11 +265,6 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
return cid.Undef, xerrors.Errorf("loading state tree failed: %w", err) return cid.Undef, xerrors.Errorf("loading state tree failed: %w", err)
} }
ReserveAddress, err := address.NewFromString("t090")
if err != nil {
return cid.Undef, xerrors.Errorf("failed to parse reserve address: %w", err)
}
tree, err := sm.StateTree(root) tree, err := sm.StateTree(root)
if err != nil { if err != nil {
return cid.Undef, xerrors.Errorf("getting state tree: %w", err) return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
@ -293,7 +290,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
if !sysAcc { if !sysAcc {
transfers = append(transfers, transfer{ transfers = append(transfers, transfer{
From: addr, From: addr,
To: ReserveAddress, To: builtin.ReserveAddress,
Amt: act.Balance, Amt: act.Balance,
}) })
} }
@ -317,7 +314,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
transfers = append(transfers, transfer{ transfers = append(transfers, transfer{
From: addr, From: addr,
To: ReserveAddress, To: builtin.ReserveAddress,
Amt: available, Amt: available,
}) })
} }
@ -368,7 +365,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
nbalance := big.Min(prevBalance, AccountCap) nbalance := big.Min(prevBalance, AccountCap)
if nbalance.Sign() != 0 { if nbalance.Sign() != 0 {
transfersBack = append(transfersBack, transfer{ transfersBack = append(transfersBack, transfer{
From: ReserveAddress, From: builtin.ReserveAddress,
To: addr, To: addr,
Amt: nbalance, Amt: nbalance,
}) })
@ -395,7 +392,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
mfunds := minerFundsAlloc(power, totalPower) mfunds := minerFundsAlloc(power, totalPower)
transfersBack = append(transfersBack, transfer{ transfersBack = append(transfersBack, transfer{
From: ReserveAddress, From: builtin.ReserveAddress,
To: minfo.Worker, To: minfo.Worker,
Amt: mfunds, Amt: mfunds,
}) })
@ -415,7 +412,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
if lbsectors.Length() > 0 { if lbsectors.Length() > 0 {
transfersBack = append(transfersBack, transfer{ transfersBack = append(transfersBack, transfer{
From: ReserveAddress, From: builtin.ReserveAddress,
To: minfo.Worker, To: minfo.Worker,
Amt: BaseMinerBalance, Amt: BaseMinerBalance,
}) })
@ -442,7 +439,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
if err != nil { if err != nil {
return cid.Undef, xerrors.Errorf("failed to load burnt funds actor: %w", err) return cid.Undef, xerrors.Errorf("failed to load burnt funds actor: %w", err)
} }
if err := doTransfer(cb, tree, builtin0.BurntFundsActorAddr, ReserveAddress, burntAct.Balance); err != nil { if err := doTransfer(cb, tree, builtin0.BurntFundsActorAddr, builtin.ReserveAddress, burntAct.Balance); err != nil {
return cid.Undef, xerrors.Errorf("failed to unburn funds: %w", err) return cid.Undef, xerrors.Errorf("failed to unburn funds: %w", err)
} }
@ -458,7 +455,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
} }
difference := types.BigSub(DesiredReimbursementBalance, reimb.Balance) difference := types.BigSub(DesiredReimbursementBalance, reimb.Balance)
if err := doTransfer(cb, tree, ReserveAddress, reimbAddr, difference); err != nil { if err := doTransfer(cb, tree, builtin.ReserveAddress, reimbAddr, difference); err != nil {
return cid.Undef, xerrors.Errorf("failed to top up reimbursement account: %w", err) return cid.Undef, xerrors.Errorf("failed to top up reimbursement account: %w", err)
} }
@ -543,12 +540,7 @@ func UpgradeRefuel(ctx context.Context, sm *StateManager, cb ExecCallback, root
return cid.Undef, xerrors.Errorf("getting state tree: %w", err) return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
} }
addr, err := address.NewFromString("t0122") err = resetMultisigVesting(ctx, store, tree, builtin.SaftAddress, 0, 0, big.Zero())
if err != nil {
return cid.Undef, xerrors.Errorf("getting address: %w", err)
}
err = resetMultisigVesting(ctx, store, tree, addr, 0, 0, big.Zero())
if err != nil { if err != nil {
return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err) return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err)
} }
@ -580,19 +572,6 @@ func UpgradeActorsV2(ctx context.Context, sm *StateManager, cb ExecCallback, roo
return cid.Undef, xerrors.Errorf("upgrading to actors v2: %w", err) return cid.Undef, xerrors.Errorf("upgrading to actors v2: %w", err)
} }
newStateTree, err := states2.LoadTree(store, newHamtRoot)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to load new state tree: %w", err)
}
// Check all state-tree invariants.
if msgs, err := states2.CheckStateInvariants(newStateTree, types.TotalFilecoinInt); err != nil {
return cid.Undef, xerrors.Errorf("failed to check new state tree: %w", err)
} else if !msgs.IsEmpty() {
// This error is going to be really nasty.
return cid.Undef, xerrors.Errorf("network upgrade failed: %v", msgs.Messages())
}
newRoot, err := store.Put(ctx, &types.StateRoot{ newRoot, err := store.Put(ctx, &types.StateRoot{
Version: types.StateTreeVersion1, Version: types.StateTreeVersion1,
Actors: newHamtRoot, Actors: newHamtRoot,

View File

@ -6,15 +6,21 @@ import (
"io" "io"
"testing" "testing"
"github.com/ipfs/go-cid"
ipldcbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log"
"github.com/stretchr/testify/require"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/cbor"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
init0 "github.com/filecoin-project/specs-actors/actors/builtin/init" init0 "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/runtime" "github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/actors/aerrors"
lotusinit "github.com/filecoin-project/lotus/chain/actors/builtin/init" lotusinit "github.com/filecoin-project/lotus/chain/actors/builtin/init"
@ -25,11 +31,6 @@ import (
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
_ "github.com/filecoin-project/lotus/lib/sigs/bls" _ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp" _ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/ipfs/go-cid"
ipldcbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log"
cbg "github.com/whyrusleeping/cbor-gen"
) )
func init() { func init() {
@ -186,7 +187,7 @@ func TestForkHeightTriggers(t *testing.T) {
Params: enc, Params: enc,
GasLimit: types.TestGasLimit, GasLimit: types.TestGasLimit,
} }
sig, err := cg.Wallet().Sign(ctx, cg.Banker(), m.Cid().Bytes()) sig, err := cg.Wallet().WalletSign(ctx, cg.Banker(), m.Cid().Bytes(), api.MsgMeta{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -214,7 +215,7 @@ func TestForkHeightTriggers(t *testing.T) {
} }
nonce++ nonce++
sig, err := cg.Wallet().Sign(ctx, cg.Banker(), m.Cid().Bytes()) sig, err := cg.Wallet().WalletSign(ctx, cg.Banker(), m.Cid().Bytes(), api.MsgMeta{})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,10 +2,16 @@ package stmgr
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"sync" "sync"
"github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
_init "github.com/filecoin-project/lotus/chain/actors/builtin/init"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
@ -37,8 +43,16 @@ import (
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
) )
const LookbackNoLimit = abi.ChainEpoch(-1)
var log = logging.Logger("statemgr") var log = logging.Logger("statemgr")
type StateManagerAPI interface {
LoadActorTsk(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error)
LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error)
ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error)
}
type versionSpec struct { type versionSpec struct {
networkVersion network.Version networkVersion network.Version
atOrBelow abi.ChainEpoch atOrBelow abi.ChainEpoch
@ -220,7 +234,7 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEp
Rand: r, Rand: r,
Bstore: sm.cs.Blockstore(), Bstore: sm.cs.Blockstore(),
Syscalls: sm.cs.VMSys(), Syscalls: sm.cs.VMSys(),
CircSupplyCalc: sm.GetCirculatingSupply, CircSupplyCalc: sm.GetVMCirculatingSupply,
NtwkVersion: sm.GetNtwkVersion, NtwkVersion: sm.GetNtwkVersion,
BaseFee: baseFee, BaseFee: baseFee,
} }
@ -507,16 +521,7 @@ func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.T
return nil, fmt.Errorf("failed to load message: %w", err) return nil, fmt.Errorf("failed to load message: %w", err)
} }
r, _, err := sm.tipsetExecutedMessage(ts, msg, m.VMMessage()) _, r, _, err := sm.searchBackForMsg(ctx, ts, m, LookbackNoLimit)
if err != nil {
return nil, err
}
if r != nil {
return r, nil
}
_, r, _, err = sm.searchBackForMsg(ctx, ts, m)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to look back through chain for message: %w", err) return nil, fmt.Errorf("failed to look back through chain for message: %w", err)
} }
@ -525,9 +530,9 @@ func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.T
} }
// WaitForMessage blocks until a message appears on chain. It looks backwards in the chain to see if this has already // WaitForMessage blocks until a message appears on chain. It looks backwards in the chain to see if this has already
// happened. It guarantees that the message has been on chain for at least confidence epochs without being reverted // happened, with an optional limit to how many epochs it will search. It guarantees that the message has been on
// before returning. // chain for at least confidence epochs without being reverted before returning.
func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
@ -565,7 +570,7 @@ func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confid
var backFm cid.Cid var backFm cid.Cid
backSearchWait := make(chan struct{}) backSearchWait := make(chan struct{})
go func() { go func() {
fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head[0].Val, msg) fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head[0].Val, msg, lookbackLimit)
if err != nil { if err != nil {
log.Warnf("failed to look back through chain for message: %w", err) log.Warnf("failed to look back through chain for message: %w", err)
return return
@ -657,7 +662,7 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, mcid cid.Cid) (*ty
return head, r, foundMsg, nil return head, r, foundMsg, nil
} }
fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head, msg) fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head, msg, LookbackNoLimit)
if err != nil { if err != nil {
log.Warnf("failed to look back through chain for message %s", mcid) log.Warnf("failed to look back through chain for message %s", mcid)
@ -671,11 +676,33 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, mcid cid.Cid) (*ty
return fts, r, foundMsg, nil return fts, r, foundMsg, nil
} }
func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { // searchBackForMsg searches up to limit tipsets backwards from the given
// tipset for a message receipt.
// If limit is
// - 0 then no tipsets are searched
// - 5 then five tipset are searched
// - LookbackNoLimit then there is no limit
func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg, limit abi.ChainEpoch) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) {
limitHeight := from.Height() - limit
noLimit := limit == LookbackNoLimit
cur := from cur := from
curActor, err := sm.LoadActor(ctx, m.VMMessage().From, cur)
if err != nil {
return nil, nil, cid.Undef, xerrors.Errorf("failed to load initital tipset")
}
mFromId, err := sm.LookupID(ctx, m.VMMessage().From, from)
if err != nil {
return nil, nil, cid.Undef, xerrors.Errorf("looking up From id address: %w", err)
}
mNonce := m.VMMessage().Nonce
for { for {
if cur.Height() == 0 { // If we've reached the genesis block, or we've reached the limit of
// how far back to look
if cur.Height() == 0 || !noLimit && cur.Height() <= limitHeight {
// it ain't here! // it ain't here!
return nil, nil, cid.Undef, nil return nil, nil, cid.Undef, nil
} }
@ -686,32 +713,37 @@ func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet
default: default:
} }
act, err := sm.LoadActor(ctx, m.VMMessage().From, cur)
if err != nil {
return nil, nil, cid.Cid{}, err
}
// we either have no messages from the sender, or the latest message we found has a lower nonce than the one being searched for, // we either have no messages from the sender, or the latest message we found has a lower nonce than the one being searched for,
// either way, no reason to lookback, it ain't there // either way, no reason to lookback, it ain't there
if act.Nonce == 0 || act.Nonce < m.VMMessage().Nonce { if curActor == nil || curActor.Nonce == 0 || curActor.Nonce < mNonce {
return nil, nil, cid.Undef, nil return nil, nil, cid.Undef, nil
} }
ts, err := sm.cs.LoadTipSet(cur.Parents()) pts, err := sm.cs.LoadTipSet(cur.Parents())
if err != nil { if err != nil {
return nil, nil, cid.Undef, fmt.Errorf("failed to load tipset during msg wait searchback: %w", err) return nil, nil, cid.Undef, xerrors.Errorf("failed to load tipset during msg wait searchback: %w", err)
} }
r, foundMsg, err := sm.tipsetExecutedMessage(ts, m.Cid(), m.VMMessage()) act, err := sm.LoadActor(ctx, mFromId, pts)
actorNoExist := errors.Is(err, types.ErrActorNotFound)
if err != nil && !actorNoExist {
return nil, nil, cid.Cid{}, xerrors.Errorf("failed to load the actor: %w", err)
}
// check that between cur and parent tipset the nonce fell into range of our message
if actorNoExist || (curActor.Nonce > mNonce && act.Nonce <= mNonce) {
r, foundMsg, err := sm.tipsetExecutedMessage(cur, m.Cid(), m.VMMessage())
if err != nil { if err != nil {
return nil, nil, cid.Undef, fmt.Errorf("checking for message execution during lookback: %w", err) return nil, nil, cid.Undef, xerrors.Errorf("checking for message execution during lookback: %w", err)
} }
if r != nil { if r != nil {
return ts, r, foundMsg, nil return pts, r, foundMsg, nil
}
} }
cur = ts cur = pts
curActor = act
} }
} }
@ -1267,7 +1299,16 @@ func GetFilBurnt(ctx context.Context, st *state.StateTree) (abi.TokenAmount, err
return burnt.Balance, nil return burnt.Balance, nil
} }
func (sm *StateManager) GetCirculatingSupplyDetailed(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) { func (sm *StateManager) GetVMCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) {
cs, err := sm.GetVMCirculatingSupplyDetailed(ctx, height, st)
if err != nil {
return types.EmptyInt, err
}
return cs.FilCirculating, err
}
func (sm *StateManager) GetVMCirculatingSupplyDetailed(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) {
sm.genesisMsigLk.Lock() sm.genesisMsigLk.Lock()
defer sm.genesisMsigLk.Unlock() defer sm.genesisMsigLk.Unlock()
if sm.preIgnitionGenInfos == nil { if sm.preIgnitionGenInfos == nil {
@ -1330,12 +1371,91 @@ func (sm *StateManager) GetCirculatingSupplyDetailed(ctx context.Context, height
} }
func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) { func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) {
csi, err := sm.GetCirculatingSupplyDetailed(ctx, height, st) circ := big.Zero()
unCirc := big.Zero()
err := st.ForEach(func(a address.Address, actor *types.Actor) error {
switch {
case actor.Balance.IsZero():
// Do nothing for zero-balance actors
break
case a == _init.Address ||
a == reward.Address ||
a == verifreg.Address ||
// The power actor itself should never receive funds
a == power.Address ||
a == builtin.SystemActorAddr ||
a == builtin.CronActorAddr ||
a == builtin.BurntFundsActorAddr ||
a == builtin.SaftAddress ||
a == builtin.ReserveAddress:
unCirc = big.Add(unCirc, actor.Balance)
case a == market.Address:
mst, err := market.Load(sm.cs.Store(ctx), actor)
if err != nil { if err != nil {
return big.Zero(), err return err
} }
return csi.FilCirculating, nil lb, err := mst.TotalLocked()
if err != nil {
return err
}
circ = big.Add(circ, big.Sub(actor.Balance, lb))
unCirc = big.Add(unCirc, lb)
case builtin.IsAccountActor(actor.Code) || builtin.IsPaymentChannelActor(actor.Code):
circ = big.Add(circ, actor.Balance)
case builtin.IsStorageMinerActor(actor.Code):
mst, err := miner.Load(sm.cs.Store(ctx), actor)
if err != nil {
return err
}
ab, err := mst.AvailableBalance(actor.Balance)
if err == nil {
circ = big.Add(circ, ab)
unCirc = big.Add(unCirc, big.Sub(actor.Balance, ab))
} else {
// Assume any error is because the miner state is "broken" (lower actor balance than locked funds)
// In this case, the actor's entire balance is considered "uncirculating"
unCirc = big.Add(unCirc, actor.Balance)
}
case builtin.IsMultisigActor(actor.Code):
mst, err := multisig.Load(sm.cs.Store(ctx), actor)
if err != nil {
return err
}
lb, err := mst.LockedBalance(height)
if err != nil {
return err
}
ab := big.Sub(actor.Balance, lb)
circ = big.Add(circ, big.Max(ab, big.Zero()))
unCirc = big.Add(unCirc, big.Min(actor.Balance, lb))
default:
return xerrors.Errorf("unexpected actor: %s", a)
}
return nil
})
if err != nil {
return types.EmptyInt, err
}
total := big.Add(circ, unCirc)
if !total.Equals(types.TotalFilecoinInt) {
return types.EmptyInt, xerrors.Errorf("total filecoin didn't add to expected amount: %s != %s", total, types.TotalFilecoinInt)
}
return circ, nil
} }
func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoch) network.Version { func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoch) network.Version {
@ -1384,3 +1504,5 @@ func (sm *StateManager) GetMarketState(ctx context.Context, ts *types.TipSet) (m
} }
return actState, nil return actState, nil
} }
var _ StateManagerAPI = (*StateManager)(nil)

View File

@ -383,7 +383,7 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch,
Rand: r, Rand: r,
Bstore: sm.cs.Blockstore(), Bstore: sm.cs.Blockstore(),
Syscalls: sm.cs.VMSys(), Syscalls: sm.cs.VMSys(),
CircSupplyCalc: sm.GetCirculatingSupply, CircSupplyCalc: sm.GetVMCirculatingSupply,
NtwkVersion: sm.GetNtwkVersion, NtwkVersion: sm.GetNtwkVersion,
BaseFee: ts.Blocks()[0].ParentBaseFee, BaseFee: ts.Blocks()[0].ParentBaseFee,
} }

View File

@ -31,7 +31,7 @@ func TestIndexSeeks(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
nbs := blockstore.NewTemporarySync() nbs := blockstore.NewTemporarySync()
cs := store.NewChainStore(nbs, syncds.MutexWrap(datastore.NewMapDatastore()), nil) cs := store.NewChainStore(nbs, syncds.MutexWrap(datastore.NewMapDatastore()), nil, nil)
_, err = cs.Import(bytes.NewReader(gencar)) _, err = cs.Import(bytes.NewReader(gencar))
if err != nil { if err != nil {

View File

@ -8,6 +8,7 @@ import (
"io" "io"
"os" "os"
"strconv" "strconv"
"strings"
"sync" "sync"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -38,7 +39,9 @@ import (
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
block "github.com/ipfs/go-block-format" block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
dstore "github.com/ipfs/go-datastore" dstore "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/query"
cbor "github.com/ipfs/go-ipld-cbor" cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
car "github.com/ipld/go-car" car "github.com/ipld/go-car"
@ -102,7 +105,7 @@ type HeadChangeEvt struct {
// 2. a block => messages references cache. // 2. a block => messages references cache.
type ChainStore struct { type ChainStore struct {
bs bstore.Blockstore bs bstore.Blockstore
ds dstore.Datastore ds dstore.Batching
heaviestLk sync.Mutex heaviestLk sync.Mutex
heaviest *types.TipSet heaviest *types.TipSet
@ -124,11 +127,15 @@ type ChainStore struct {
vmcalls vm.SyscallBuilder vmcalls vm.SyscallBuilder
evtTypes [1]journal.EventType evtTypes [1]journal.EventType
journal journal.Journal
} }
func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls vm.SyscallBuilder) *ChainStore { func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls vm.SyscallBuilder, j journal.Journal) *ChainStore {
c, _ := lru.NewARC(DefaultMsgMetaCacheSize) c, _ := lru.NewARC(DefaultMsgMetaCacheSize)
tsc, _ := lru.NewARC(DefaultTipSetCacheSize) tsc, _ := lru.NewARC(DefaultTipSetCacheSize)
if j == nil {
j = journal.NilJournal()
}
cs := &ChainStore{ cs := &ChainStore{
bs: bs, bs: bs,
ds: ds, ds: ds,
@ -137,10 +144,11 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls vm.SyscallB
mmCache: c, mmCache: c,
tsCache: tsc, tsCache: tsc,
vmcalls: vmcalls, vmcalls: vmcalls,
journal: j,
} }
cs.evtTypes = [1]journal.EventType{ cs.evtTypes = [1]journal.EventType{
evtTypeHeadChange: journal.J.RegisterEventType("sync", "head_change"), evtTypeHeadChange: j.RegisterEventType("sync", "head_change"),
} }
ci := NewChainIndex(cs.LoadTipSet) ci := NewChainIndex(cs.LoadTipSet)
@ -379,7 +387,7 @@ func (cs *ChainStore) reorgWorker(ctx context.Context, initialNotifees []ReorgNo
continue continue
} }
journal.J.RecordEvent(cs.evtTypes[evtTypeHeadChange], func() interface{} { cs.journal.RecordEvent(cs.evtTypes[evtTypeHeadChange], func() interface{} {
return HeadChangeEvt{ return HeadChangeEvt{
From: r.old.Key(), From: r.old.Key(),
FromHeight: r.old.Height(), FromHeight: r.old.Height(),
@ -441,6 +449,53 @@ func (cs *ChainStore) takeHeaviestTipSet(ctx context.Context, ts *types.TipSet)
return nil return nil
} }
// FlushValidationCache removes all results of block validation from the
// chain metadata store. Usually the first step after a new chain import.
func (cs *ChainStore) FlushValidationCache() error {
log.Infof("clearing block validation cache...")
dsWalk, err := cs.ds.Query(query.Query{
// Potential TODO: the validation cache is not a namespace on its own
// but is rather constructed as prefixed-key `foo:bar` via .Instance(), which
// in turn does not work with the filter, which can match only on `foo/bar`
//
// If this is addressed (blockcache goes into its own sub-namespace) then
// strings.HasPrefix(...) below can be skipped
//
//Prefix: blockValidationCacheKeyPrefix.String()
KeysOnly: true,
})
if err != nil {
return xerrors.Errorf("failed to initialize key listing query: %w", err)
}
allKeys, err := dsWalk.Rest()
if err != nil {
return xerrors.Errorf("failed to run key listing query: %w", err)
}
batch, err := cs.ds.Batch()
if err != nil {
return xerrors.Errorf("failed to open a DS batch: %w", err)
}
delCnt := 0
for _, k := range allKeys {
if strings.HasPrefix(k.Key, blockValidationCacheKeyPrefix.String()) {
delCnt++
batch.Delete(datastore.RawKey(k.Key)) // nolint:errcheck
}
}
if err := batch.Commit(); err != nil {
return xerrors.Errorf("failed to commit the DS batch: %w", err)
}
log.Infof("%d block validation entries cleared.", delCnt)
return nil
}
// SetHead sets the chainstores current 'best' head node. // SetHead sets the chainstores current 'best' head node.
// This should only be called if something is broken and needs fixing // This should only be called if something is broken and needs fixing
func (cs *ChainStore) SetHead(ts *types.TipSet) error { func (cs *ChainStore) SetHead(ts *types.TipSet) error {

View File

@ -62,7 +62,7 @@ func BenchmarkGetRandomness(b *testing.B) {
bs := blockstore.NewBlockstore(bds) bs := blockstore.NewBlockstore(bds)
cs := store.NewChainStore(bs, mds, nil) cs := store.NewChainStore(bs, mds, nil, nil)
b.ResetTimer() b.ResetTimer()
@ -96,7 +96,7 @@ func TestChainExportImport(t *testing.T) {
} }
nbs := blockstore.NewTemporary() nbs := blockstore.NewTemporary()
cs := store.NewChainStore(nbs, datastore.NewMapDatastore(), nil) cs := store.NewChainStore(nbs, datastore.NewMapDatastore(), nil, nil)
root, err := cs.Import(buf) root, err := cs.Import(buf)
if err != nil { if err != nil {

View File

@ -120,12 +120,6 @@ func FetchMessagesByCids(
return err return err
} }
// FIXME: We already sort in `fetchCids`, we are duplicating too much work,
// we don't need to pass the index.
if out[i] != nil {
return fmt.Errorf("received duplicate message")
}
out[i] = msg out[i] = msg
return nil return nil
}) })
@ -149,10 +143,6 @@ func FetchSignedMessagesByCids(
return err return err
} }
if out[i] != nil {
return fmt.Errorf("received duplicate message")
}
out[i] = smsg out[i] = smsg
return nil return nil
}) })
@ -172,37 +162,41 @@ func fetchCids(
cids []cid.Cid, cids []cid.Cid,
cb func(int, blocks.Block) error, cb func(int, blocks.Block) error,
) error { ) error {
fetchedBlocks := bserv.GetBlocks(ctx, cids)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
cidIndex := make(map[cid.Cid]int) cidIndex := make(map[cid.Cid]int)
for i, c := range cids { for i, c := range cids {
cidIndex[c] = i cidIndex[c] = i
} }
if len(cids) != len(cidIndex) {
for i := 0; i < len(cids); i++ { return fmt.Errorf("duplicate CIDs in fetchCids input")
select {
case block, ok := <-fetchedBlocks:
if !ok {
// Closed channel, no more blocks fetched, check if we have all
// of the CIDs requested.
// FIXME: Review this check. We don't call the callback on the
// last index?
if i == len(cids)-1 {
break
}
return fmt.Errorf("failed to fetch all messages")
} }
for block := range bserv.GetBlocks(ctx, cids) {
ix, ok := cidIndex[block.Cid()] ix, ok := cidIndex[block.Cid()]
if !ok { if !ok {
return fmt.Errorf("received message we didnt ask for") // Ignore duplicate/unexpected blocks. This shouldn't
// happen, but we can be safe.
log.Errorw("received duplicate/unexpected block when syncing", "cid", block.Cid())
continue
} }
// Record that we've received the block.
delete(cidIndex, block.Cid())
if err := cb(ix, block); err != nil { if err := cb(ix, block); err != nil {
return err return err
} }
} }
if len(cidIndex) > 0 {
err := ctx.Err()
if err == nil {
err = fmt.Errorf("failed to fetch %d messages for unknown reasons", len(cidIndex))
}
return err
} }
return nil return nil

View File

@ -0,0 +1,63 @@
package sub
import (
"context"
"testing"
address "github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
)
type getter struct {
msgs []*types.Message
}
func (g *getter) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { panic("NYI") }
func (g *getter) GetBlocks(ctx context.Context, ks []cid.Cid) <-chan blocks.Block {
ch := make(chan blocks.Block, len(g.msgs))
for _, m := range g.msgs {
by, err := m.Serialize()
if err != nil {
panic(err)
}
b, err := blocks.NewBlockWithCid(by, m.Cid())
if err != nil {
panic(err)
}
ch <- b
}
close(ch)
return ch
}
func TestFetchCidsWithDedup(t *testing.T) {
msgs := []*types.Message{}
for i := 0; i < 10; i++ {
msgs = append(msgs, &types.Message{
From: address.TestAddress,
To: address.TestAddress,
Nonce: uint64(i),
})
}
cids := []cid.Cid{}
for _, m := range msgs {
cids = append(cids, m.Cid())
}
g := &getter{msgs}
// the cids have a duplicate
res, err := FetchMessagesByCids(context.TODO(), g, append(cids, cids[0]))
t.Logf("err: %+v", err)
t.Logf("res: %+v", res)
if err == nil {
t.Errorf("there should be an error")
}
if err == nil && (res[0] == nil || res[len(res)-1] == nil) {
t.Fatalf("there is a nil message: first %p, last %p", res[0], res[len(res)-1])
}
}

View File

@ -217,6 +217,12 @@ func (syncer *Syncer) Stop() {
// This should be called when connecting to new peers, and additionally // This should be called when connecting to new peers, and additionally
// when receiving new blocks from the network // when receiving new blocks from the network
func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool { func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool {
defer func() {
if err := recover(); err != nil {
log.Errorf("panic in InformNewHead: ", err)
}
}()
ctx := context.Background() ctx := context.Background()
if fts == nil { if fts == nil {
log.Errorf("got nil tipset in InformNewHead") log.Errorf("got nil tipset in InformNewHead")
@ -731,7 +737,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock, use
lbst, _, err := syncer.sm.TipSetState(ctx, lbts) lbst, _, err := syncer.sm.TipSetState(ctx, lbts)
if err != nil { if err != nil {
return xerrors.Errorf("failed to compute lookback tipset state: %w", err) return xerrors.Errorf("failed to compute lookback tipset state (epoch %d): %w", lbts.Height(), err)
} }
prevBeacon, err := syncer.store.GetLatestBeaconEntry(baseTs) prevBeacon, err := syncer.store.GetLatestBeaconEntry(baseTs)
@ -1281,9 +1287,11 @@ func (syncer *Syncer) collectHeaders(ctx context.Context, incoming *types.TipSet
blockSet := []*types.TipSet{incoming} blockSet := []*types.TipSet{incoming}
// Parent of the new (possibly better) tipset that we need to fetch next.
at := incoming.Parents() at := incoming.Parents()
// we want to sync all the blocks until the height above the block we have // we want to sync all the blocks until the height above our
// best tipset so far
untilHeight := known.Height() + 1 untilHeight := known.Height() + 1
ss.SetHeight(blockSet[len(blockSet)-1].Height()) ss.SetHeight(blockSet[len(blockSet)-1].Height())
@ -1377,13 +1385,17 @@ loop:
} }
base := blockSet[len(blockSet)-1] base := blockSet[len(blockSet)-1]
if base.Parents() == known.Parents() { if base.IsChildOf(known) {
// common case: receiving a block thats potentially part of the same tipset as our best block // common case: receiving blocks that are building on top of our best tipset
return blockSet, nil return blockSet, nil
} }
if types.CidArrsEqual(base.Parents().Cids(), known.Cids()) { knownParent, err := syncer.store.LoadTipSet(known.Parents())
// common case: receiving blocks that are building on top of our best tipset if err != nil {
return nil, xerrors.Errorf("failed to load next local tipset: %w", err)
}
if base.IsChildOf(knownParent) {
// common case: receiving a block thats potentially part of the same tipset as our best block
return blockSet, nil return blockSet, nil
} }
@ -1466,7 +1478,7 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know
func (syncer *Syncer) syncMessagesAndCheckState(ctx context.Context, headers []*types.TipSet) error { func (syncer *Syncer) syncMessagesAndCheckState(ctx context.Context, headers []*types.TipSet) error {
ss := extractSyncState(ctx) ss := extractSyncState(ctx)
ss.SetHeight(0) ss.SetHeight(headers[len(headers)-1].Height())
return syncer.iterFullTipsets(ctx, headers, func(ctx context.Context, fts *store.FullTipSet) error { return syncer.iterFullTipsets(ctx, headers, func(ctx context.Context, fts *store.FullTipSet) error {
log.Debugw("validating tipset", "height", fts.TipSet().Height(), "size", len(fts.TipSet().Cids())) log.Debugw("validating tipset", "height", fts.TipSet().Height(), "size", len(fts.TipSet().Cids()))
@ -1514,7 +1526,7 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
ss.SetStage(api.StageMessages) ss.SetStage(api.StageMessages)
if batchErr != nil { if batchErr != nil {
return xerrors.Errorf("failed to fetch messages: %w", err) return xerrors.Errorf("failed to fetch messages: %w", batchErr)
} }
for bsi := 0; bsi < len(bstout); bsi++ { for bsi := 0; bsi < len(bstout); bsi++ {
@ -1714,7 +1726,7 @@ func VerifyElectionPoStVRF(ctx context.Context, worker address.Address, rand []b
return gen.VerifyVRF(ctx, worker, rand, evrf) return gen.VerifyVRF(ctx, worker, rand, evrf)
} }
func (syncer *Syncer) State() []SyncerState { func (syncer *Syncer) State() []SyncerStateSnapshot {
return syncer.syncmgr.State() return syncer.syncmgr.State()
} }
@ -1728,6 +1740,10 @@ func (syncer *Syncer) UnmarkBad(blk cid.Cid) {
syncer.bad.Remove(blk) syncer.bad.Remove(blk)
} }
func (syncer *Syncer) UnmarkAllBad() {
syncer.bad.Purge()
}
func (syncer *Syncer) CheckBadBlockCache(blk cid.Cid) (string, bool) { func (syncer *Syncer) CheckBadBlockCache(blk cid.Cid) (string, bool) {
bbr, ok := syncer.bad.Has(blk) bbr, ok := syncer.bad.Has(blk)
return bbr.String(), ok return bbr.String(), ok

View File

@ -38,7 +38,7 @@ type SyncManager interface {
SetPeerHead(ctx context.Context, p peer.ID, ts *types.TipSet) SetPeerHead(ctx context.Context, p peer.ID, ts *types.TipSet)
// State retrieves the state of the sync workers. // State retrieves the state of the sync workers.
State() []SyncerState State() []SyncerStateSnapshot
} }
type syncManager struct { type syncManager struct {
@ -79,7 +79,7 @@ type syncResult struct {
const syncWorkerCount = 3 const syncWorkerCount = 3
func NewSyncManager(sync SyncFunc) SyncManager { func NewSyncManager(sync SyncFunc) SyncManager {
return &syncManager{ sm := &syncManager{
bspThresh: 1, bspThresh: 1,
peerHeads: make(map[peer.ID]*types.TipSet), peerHeads: make(map[peer.ID]*types.TipSet),
syncTargets: make(chan *types.TipSet), syncTargets: make(chan *types.TipSet),
@ -90,6 +90,10 @@ func NewSyncManager(sync SyncFunc) SyncManager {
doSync: sync, doSync: sync,
stop: make(chan struct{}), stop: make(chan struct{}),
} }
for i := range sm.syncStates {
sm.syncStates[i] = new(SyncerState)
}
return sm
} }
func (sm *syncManager) Start() { func (sm *syncManager) Start() {
@ -128,8 +132,8 @@ func (sm *syncManager) SetPeerHead(ctx context.Context, p peer.ID, ts *types.Tip
sm.incomingTipSets <- ts sm.incomingTipSets <- ts
} }
func (sm *syncManager) State() []SyncerState { func (sm *syncManager) State() []SyncerStateSnapshot {
ret := make([]SyncerState, 0, len(sm.syncStates)) ret := make([]SyncerStateSnapshot, 0, len(sm.syncStates))
for _, s := range sm.syncStates { for _, s := range sm.syncStates {
ret = append(ret, s.Snapshot()) ret = append(ret, s.Snapshot())
} }
@ -405,8 +409,7 @@ func (sm *syncManager) scheduleWorkSent() {
} }
func (sm *syncManager) syncWorker(id int) { func (sm *syncManager) syncWorker(id int) {
ss := &SyncerState{} ss := sm.syncStates[id]
sm.syncStates[id] = ss
for { for {
select { select {
case ts, ok := <-sm.syncTargets: case ts, ok := <-sm.syncTargets:

View File

@ -221,8 +221,7 @@ func (tu *syncTestUtil) addSourceNode(gen int) {
sourceRepo, genesis, blocks := tu.repoWithChain(tu.t, gen) sourceRepo, genesis, blocks := tu.repoWithChain(tu.t, gen)
var out api.FullNode var out api.FullNode
// TODO: Don't ignore stop stop, err := node.New(tu.ctx,
_, err := node.New(tu.ctx,
node.FullAPI(&out), node.FullAPI(&out),
node.Online(), node.Online(),
node.Repo(sourceRepo), node.Repo(sourceRepo),
@ -232,6 +231,7 @@ func (tu *syncTestUtil) addSourceNode(gen int) {
node.Override(new(modules.Genesis), modules.LoadGenesis(genesis)), node.Override(new(modules.Genesis), modules.LoadGenesis(genesis)),
) )
require.NoError(tu.t, err) require.NoError(tu.t, err)
tu.t.Cleanup(func() { _ = stop(context.Background()) })
lastTs := blocks[len(blocks)-1].Blocks lastTs := blocks[len(blocks)-1].Blocks
for _, lastB := range lastTs { for _, lastB := range lastTs {
@ -253,8 +253,7 @@ func (tu *syncTestUtil) addClientNode() int {
var out api.FullNode var out api.FullNode
// TODO: Don't ignore stop stop, err := node.New(tu.ctx,
_, err := node.New(tu.ctx,
node.FullAPI(&out), node.FullAPI(&out),
node.Online(), node.Online(),
node.Repo(repo.NewMemory(nil)), node.Repo(repo.NewMemory(nil)),
@ -264,6 +263,7 @@ func (tu *syncTestUtil) addClientNode() int {
node.Override(new(modules.Genesis), modules.LoadGenesis(tu.genesis)), node.Override(new(modules.Genesis), modules.LoadGenesis(tu.genesis)),
) )
require.NoError(tu.t, err) require.NoError(tu.t, err)
tu.t.Cleanup(func() { _ = stop(context.Background()) })
tu.nds = append(tu.nds, out) tu.nds = append(tu.nds, out)
return len(tu.nds) - 1 return len(tu.nds) - 1
@ -593,7 +593,7 @@ func TestDuplicateNonce(t *testing.T) {
GasPremium: types.NewInt(0), GasPremium: types.NewInt(0),
} }
sig, err := tu.g.Wallet().Sign(context.TODO(), tu.g.Banker(), msg.Cid().Bytes()) sig, err := tu.g.Wallet().WalletSign(context.TODO(), tu.g.Banker(), msg.Cid().Bytes(), api.MsgMeta{})
require.NoError(t, err) require.NoError(t, err)
return &types.SignedMessage{ return &types.SignedMessage{
@ -685,7 +685,7 @@ func TestBadNonce(t *testing.T) {
GasPremium: types.NewInt(0), GasPremium: types.NewInt(0),
} }
sig, err := tu.g.Wallet().Sign(context.TODO(), tu.g.Banker(), msg.Cid().Bytes()) sig, err := tu.g.Wallet().WalletSign(context.TODO(), tu.g.Banker(), msg.Cid().Bytes(), api.MsgMeta{})
require.NoError(t, err) require.NoError(t, err)
return &types.SignedMessage{ return &types.SignedMessage{

View File

@ -11,8 +11,7 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
type SyncerState struct { type SyncerStateSnapshot struct {
lk sync.Mutex
Target *types.TipSet Target *types.TipSet
Base *types.TipSet Base *types.TipSet
Stage api.SyncStateStage Stage api.SyncStateStage
@ -22,6 +21,11 @@ type SyncerState struct {
End time.Time End time.Time
} }
type SyncerState struct {
lk sync.Mutex
data SyncerStateSnapshot
}
func (ss *SyncerState) SetStage(v api.SyncStateStage) { func (ss *SyncerState) SetStage(v api.SyncStateStage) {
if ss == nil { if ss == nil {
return return
@ -29,9 +33,9 @@ func (ss *SyncerState) SetStage(v api.SyncStateStage) {
ss.lk.Lock() ss.lk.Lock()
defer ss.lk.Unlock() defer ss.lk.Unlock()
ss.Stage = v ss.data.Stage = v
if v == api.StageSyncComplete { if v == api.StageSyncComplete {
ss.End = build.Clock.Now() ss.data.End = build.Clock.Now()
} }
} }
@ -42,13 +46,13 @@ func (ss *SyncerState) Init(base, target *types.TipSet) {
ss.lk.Lock() ss.lk.Lock()
defer ss.lk.Unlock() defer ss.lk.Unlock()
ss.Target = target ss.data.Target = target
ss.Base = base ss.data.Base = base
ss.Stage = api.StageHeaders ss.data.Stage = api.StageHeaders
ss.Height = 0 ss.data.Height = 0
ss.Message = "" ss.data.Message = ""
ss.Start = build.Clock.Now() ss.data.Start = build.Clock.Now()
ss.End = time.Time{} ss.data.End = time.Time{}
} }
func (ss *SyncerState) SetHeight(h abi.ChainEpoch) { func (ss *SyncerState) SetHeight(h abi.ChainEpoch) {
@ -58,7 +62,7 @@ func (ss *SyncerState) SetHeight(h abi.ChainEpoch) {
ss.lk.Lock() ss.lk.Lock()
defer ss.lk.Unlock() defer ss.lk.Unlock()
ss.Height = h ss.data.Height = h
} }
func (ss *SyncerState) Error(err error) { func (ss *SyncerState) Error(err error) {
@ -68,21 +72,13 @@ func (ss *SyncerState) Error(err error) {
ss.lk.Lock() ss.lk.Lock()
defer ss.lk.Unlock() defer ss.lk.Unlock()
ss.Message = err.Error() ss.data.Message = err.Error()
ss.Stage = api.StageSyncErrored ss.data.Stage = api.StageSyncErrored
ss.End = build.Clock.Now() ss.data.End = build.Clock.Now()
} }
func (ss *SyncerState) Snapshot() SyncerState { func (ss *SyncerState) Snapshot() SyncerStateSnapshot {
ss.lk.Lock() ss.lk.Lock()
defer ss.lk.Unlock() defer ss.lk.Unlock()
return SyncerState{ return ss.data
Base: ss.Base,
Target: ss.Target,
Stage: ss.Stage,
Height: ss.Height,
Message: ss.Message,
Start: ss.Start,
End: ss.End,
}
} }

View File

@ -12,11 +12,15 @@ import (
type FIL BigInt type FIL BigInt
func (f FIL) String() string { func (f FIL) String() string {
return f.Unitless() + " FIL"
}
func (f FIL) Unitless() string {
r := new(big.Rat).SetFrac(f.Int, big.NewInt(int64(build.FilecoinPrecision))) r := new(big.Rat).SetFrac(f.Int, big.NewInt(int64(build.FilecoinPrecision)))
if r.Sign() == 0 { if r.Sign() == 0 {
return "0 FIL" return "0"
} }
return strings.TrimRight(strings.TrimRight(r.FloatString(18), "0"), ".") + " FIL" return strings.TrimRight(strings.TrimRight(r.FloatString(18), "0"), ".")
} }
func (f FIL) Format(s fmt.State, ch rune) { func (f FIL) Format(s fmt.State, ch rune) {

View File

@ -1,7 +1,10 @@
package types package types
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/filecoin-project/go-state-types/crypto"
) )
var ( var (
@ -9,9 +12,50 @@ var (
ErrKeyExists = fmt.Errorf("key already exists") ErrKeyExists = fmt.Errorf("key already exists")
) )
// KeyType defines a type of a key
type KeyType string
func (kt *KeyType) UnmarshalJSON(bb []byte) error {
{
// first option, try unmarshaling as string
var s string
err := json.Unmarshal(bb, &s)
if err == nil {
*kt = KeyType(s)
return nil
}
}
{
var b byte
err := json.Unmarshal(bb, &b)
if err != nil {
return fmt.Errorf("could not unmarshal KeyType either as string nor integer: %w", err)
}
bst := crypto.SigType(b)
switch bst {
case crypto.SigTypeBLS:
*kt = KTBLS
case crypto.SigTypeSecp256k1:
*kt = KTSecp256k1
default:
return fmt.Errorf("unknown sigtype: %d", bst)
}
log.Warnf("deprecation: integer style 'KeyType' is deprecated, switch to string style")
return nil
}
}
const (
KTBLS KeyType = "bls"
KTSecp256k1 KeyType = "secp256k1"
KTSecp256k1Ledger KeyType = "secp256k1-ledger"
)
// KeyInfo is used for storing keys in KeyStore // KeyInfo is used for storing keys in KeyStore
type KeyInfo struct { type KeyInfo struct {
Type string Type KeyType
PrivateKey []byte PrivateKey []byte
} }

View File

@ -2,6 +2,7 @@ package types
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
@ -106,6 +107,20 @@ func (m *Message) Cid() cid.Cid {
return b.Cid() return b.Cid()
} }
type mCid struct {
*RawMessage
CID cid.Cid
}
type RawMessage Message
func (m *Message) MarshalJSON() ([]byte, error) {
return json.Marshal(&mCid{
RawMessage: (*RawMessage)(m),
CID: m.Cid(),
})
}
func (m *Message) RequiredFunds() BigInt { func (m *Message) RequiredFunds() BigInt {
return BigMul(m.GasFeeCap, NewInt(uint64(m.GasLimit))) return BigMul(m.GasFeeCap, NewInt(uint64(m.GasLimit)))
} }

View File

@ -1,11 +1,14 @@
package types package types
import ( import (
"encoding/json"
"fmt"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
) )
@ -70,3 +73,66 @@ func TestEqualCall(t *testing.T) {
require.True(t, m1.EqualCall(m3)) require.True(t, m1.EqualCall(m3))
require.False(t, m1.EqualCall(m4)) require.False(t, m1.EqualCall(m4))
} }
func TestMessageJson(t *testing.T) {
m := &Message{
To: builtin.StoragePowerActorAddr,
From: builtin.SystemActorAddr,
Nonce: 34,
Value: big.Zero(),
GasLimit: 123,
GasFeeCap: big.NewInt(234),
GasPremium: big.NewInt(234),
Method: 6,
Params: []byte("hai"),
}
b, err := json.Marshal(m)
require.NoError(t, err)
exp := []byte("{\"Version\":0,\"To\":\"f04\",\"From\":\"f00\",\"Nonce\":34,\"Value\":\"0\",\"GasLimit\":123,\"GasFeeCap\":\"234\",\"GasPremium\":\"234\",\"Method\":6,\"Params\":\"aGFp\",\"CID\":{\"/\":\"bafy2bzaced5rdpz57e64sc7mdwjn3blicglhpialnrph2dlbufhf6iha63dmc\"}}")
fmt.Println(string(b))
require.Equal(t, exp, b)
var um Message
require.NoError(t, json.Unmarshal(b, &um))
require.EqualValues(t, *m, um)
}
func TestSignedMessageJson(t *testing.T) {
m := Message{
To: builtin.StoragePowerActorAddr,
From: builtin.SystemActorAddr,
Nonce: 34,
Value: big.Zero(),
GasLimit: 123,
GasFeeCap: big.NewInt(234),
GasPremium: big.NewInt(234),
Method: 6,
Params: []byte("hai"),
}
sm := &SignedMessage{
Message: m,
Signature: crypto.Signature{},
}
b, err := json.Marshal(sm)
require.NoError(t, err)
exp := []byte("{\"Message\":{\"Version\":0,\"To\":\"f04\",\"From\":\"f00\",\"Nonce\":34,\"Value\":\"0\",\"GasLimit\":123,\"GasFeeCap\":\"234\",\"GasPremium\":\"234\",\"Method\":6,\"Params\":\"aGFp\",\"CID\":{\"/\":\"bafy2bzaced5rdpz57e64sc7mdwjn3blicglhpialnrph2dlbufhf6iha63dmc\"}},\"Signature\":{\"Type\":0,\"Data\":null},\"CID\":{\"/\":\"bafy2bzacea5ainifngxj3rygaw2hppnyz2cw72x5pysqty2x6dxmjs5qg2uus\"}}")
fmt.Println(string(b))
require.Equal(t, exp, b)
var um SignedMessage
require.NoError(t, json.Unmarshal(b, &um))
require.EqualValues(t, *sm, um)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/crypto"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/chain/wallet"
@ -22,7 +23,7 @@ func Address(i uint64) address.Address {
return a return a
} }
func MkMessage(from, to address.Address, nonce uint64, w *wallet.Wallet) *types.SignedMessage { func MkMessage(from, to address.Address, nonce uint64, w *wallet.LocalWallet) *types.SignedMessage {
msg := &types.Message{ msg := &types.Message{
To: to, To: to,
From: from, From: from,
@ -33,7 +34,7 @@ func MkMessage(from, to address.Address, nonce uint64, w *wallet.Wallet) *types.
GasPremium: types.NewInt(1), GasPremium: types.NewInt(1),
} }
sig, err := w.Sign(context.TODO(), from, msg.Cid().Bytes()) sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -59,9 +60,11 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types
var pcids []cid.Cid var pcids []cid.Cid
var height abi.ChainEpoch var height abi.ChainEpoch
weight := types.NewInt(weightInc) weight := types.NewInt(weightInc)
var timestamp uint64
if parents != nil { if parents != nil {
pcids = parents.Cids() pcids = parents.Cids()
height = parents.Height() + 1 height = parents.Height() + 1
timestamp = parents.MinTimestamp() + build.BlockDelaySecs
weight = types.BigAdd(parents.Blocks()[0].ParentWeight, weight) weight = types.BigAdd(parents.Blocks()[0].ParentWeight, weight)
} }
@ -79,6 +82,7 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types
ParentWeight: weight, ParentWeight: weight,
Messages: c, Messages: c,
Height: height, Height: height,
Timestamp: timestamp,
ParentStateRoot: pstateRoot, ParentStateRoot: pstateRoot,
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")}, BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")},
ParentBaseFee: types.NewInt(uint64(build.MinimumBaseFee)), ParentBaseFee: types.NewInt(uint64(build.MinimumBaseFee)),

View File

@ -2,6 +2,7 @@ package types
import ( import (
"bytes" "bytes"
"encoding/json"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/crypto"
@ -62,6 +63,20 @@ func (sm *SignedMessage) Serialize() ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
type smCid struct {
*RawSignedMessage
CID cid.Cid
}
type RawSignedMessage SignedMessage
func (sm *SignedMessage) MarshalJSON() ([]byte, error) {
return json.Marshal(&smCid{
RawSignedMessage: (*RawSignedMessage)(sm),
CID: sm.Cid(),
})
}
func (sm *SignedMessage) ChainLength() int { func (sm *SignedMessage) ChainLength() int {
ser, err := sm.Serialize() ser, err := sm.Serialize()
if err != nil { if err != nil {

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
@ -10,7 +11,6 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
@ -61,11 +61,11 @@ func MakeMessageSigningVectors() []vectors.MessageSigningVector {
panic(err) panic(err)
} }
blsk, err := w.GenerateKey(crypto.SigTypeBLS) blsk, err := w.WalletNew(context.Background(), types.KTBLS)
if err != nil { if err != nil {
panic(err) panic(err)
} }
bki, err := w.Export(blsk) bki, err := w.WalletExport(context.Background(), blsk)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -85,11 +85,11 @@ func MakeMessageSigningVectors() []vectors.MessageSigningVector {
Signature: &bmsg.Signature, Signature: &bmsg.Signature,
} }
secpk, err := w.GenerateKey(crypto.SigTypeBLS) secpk, err := w.WalletNew(context.Background(), types.KTBLS)
if err != nil { if err != nil {
panic(err) panic(err)
} }
ski, err := w.Export(secpk) ski, err := w.WalletExport(context.Background(), secpk)
if err != nil { if err != nil {
panic(err) panic(err)
} }

74
chain/wallet/key.go Normal file
View File

@ -0,0 +1,74 @@
package wallet
import (
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs"
)
func GenerateKey(typ types.KeyType) (*Key, error) {
ctyp := ActSigType(typ)
if ctyp == crypto.SigTypeUnknown {
return nil, xerrors.Errorf("unknown sig type: %s", typ)
}
pk, err := sigs.Generate(ctyp)
if err != nil {
return nil, err
}
ki := types.KeyInfo{
Type: typ,
PrivateKey: pk,
}
return NewKey(ki)
}
type Key struct {
types.KeyInfo
PublicKey []byte
Address address.Address
}
func NewKey(keyinfo types.KeyInfo) (*Key, error) {
k := &Key{
KeyInfo: keyinfo,
}
var err error
k.PublicKey, err = sigs.ToPublic(ActSigType(k.Type), k.PrivateKey)
if err != nil {
return nil, err
}
switch k.Type {
case types.KTSecp256k1:
k.Address, err = address.NewSecp256k1Address(k.PublicKey)
if err != nil {
return nil, xerrors.Errorf("converting Secp256k1 to address: %w", err)
}
case types.KTBLS:
k.Address, err = address.NewBLSAddress(k.PublicKey)
if err != nil {
return nil, xerrors.Errorf("converting BLS to address: %w", err)
}
default:
return nil, xerrors.Errorf("unsupported key type: %s", k.Type)
}
return k, nil
}
func ActSigType(typ types.KeyType) crypto.SigType {
switch typ {
case types.KTBLS:
return crypto.SigTypeBLS
case types.KTSecp256k1:
return crypto.SigTypeSecp256k1
default:
return crypto.SigTypeUnknown
}
}

View File

@ -0,0 +1,242 @@
package ledgerwallet
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/query"
logging "github.com/ipfs/go-log"
ledgerfil "github.com/whyrusleeping/ledger-filecoin-go"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
var log = logging.Logger("wallet-ledger")
type LedgerWallet struct {
ds datastore.Datastore
}
func NewWallet(ds dtypes.MetadataDS) *LedgerWallet {
return &LedgerWallet{ds}
}
type LedgerKeyInfo struct {
Address address.Address
Path []uint32
}
var _ api.WalletAPI = (*LedgerWallet)(nil)
func (lw LedgerWallet) WalletSign(ctx context.Context, signer address.Address, toSign []byte, meta api.MsgMeta) (*crypto.Signature, error) {
ki, err := lw.getKeyInfo(signer)
if err != nil {
return nil, err
}
fl, err := ledgerfil.FindLedgerFilecoinApp()
if err != nil {
return nil, err
}
defer fl.Close() // nolint:errcheck
if meta.Type != api.MTChainMsg {
return nil, fmt.Errorf("ledger can only sign chain messages")
}
{
var cmsg types.Message
if err := cmsg.UnmarshalCBOR(bytes.NewReader(meta.Extra)); err != nil {
return nil, xerrors.Errorf("unmarshalling message: %w", err)
}
_, bc, err := cid.CidFromBytes(toSign)
if err != nil {
return nil, xerrors.Errorf("getting cid from signing bytes: %w", err)
}
if !cmsg.Cid().Equals(bc) {
return nil, xerrors.Errorf("cid(meta.Extra).bytes() != toSign")
}
}
sig, err := fl.SignSECP256K1(ki.Path, meta.Extra)
if err != nil {
return nil, err
}
return &crypto.Signature{
Type: crypto.SigTypeSecp256k1,
Data: sig.SignatureBytes(),
}, nil
}
func (lw LedgerWallet) getKeyInfo(addr address.Address) (*LedgerKeyInfo, error) {
kib, err := lw.ds.Get(keyForAddr(addr))
if err != nil {
return nil, err
}
var out LedgerKeyInfo
if err := json.Unmarshal(kib, &out); err != nil {
return nil, xerrors.Errorf("unmarshalling ledger key info: %w", err)
}
return &out, nil
}
func (lw LedgerWallet) WalletDelete(ctx context.Context, k address.Address) error {
return lw.ds.Delete(keyForAddr(k))
}
func (lw LedgerWallet) WalletExport(ctx context.Context, k address.Address) (*types.KeyInfo, error) {
return nil, fmt.Errorf("cannot export keys from ledger wallets")
}
func (lw LedgerWallet) WalletHas(ctx context.Context, k address.Address) (bool, error) {
_, err := lw.ds.Get(keyForAddr(k))
if err == nil {
return true, nil
}
if err == datastore.ErrNotFound {
return false, nil
}
return false, err
}
func (lw LedgerWallet) WalletImport(ctx context.Context, kinfo *types.KeyInfo) (address.Address, error) {
var ki LedgerKeyInfo
if err := json.Unmarshal(kinfo.PrivateKey, &ki); err != nil {
return address.Undef, err
}
return lw.importKey(ki)
}
func (lw LedgerWallet) importKey(ki LedgerKeyInfo) (address.Address, error) {
if ki.Address == address.Undef {
return address.Undef, fmt.Errorf("no address given in imported key info")
}
if len(ki.Path) != filHdPathLen {
return address.Undef, fmt.Errorf("bad hd path len: %d, expected: %d", len(ki.Path), filHdPathLen)
}
bb, err := json.Marshal(ki)
if err != nil {
return address.Undef, xerrors.Errorf("marshaling key info: %w", err)
}
if err := lw.ds.Put(keyForAddr(ki.Address), bb); err != nil {
return address.Undef, err
}
return ki.Address, nil
}
func (lw LedgerWallet) WalletList(ctx context.Context) ([]address.Address, error) {
res, err := lw.ds.Query(query.Query{Prefix: dsLedgerPrefix})
if err != nil {
return nil, err
}
defer res.Close() // nolint:errcheck
var out []address.Address
for {
res, ok := res.NextSync()
if !ok {
break
}
var ki LedgerKeyInfo
if err := json.Unmarshal(res.Value, &ki); err != nil {
return nil, err
}
out = append(out, ki.Address)
}
return out, nil
}
const hdHard = 0x80000000
var filHDBasePath = []uint32{hdHard | 44, hdHard | 461, hdHard, 0}
var filHdPathLen = 5
func (lw LedgerWallet) WalletNew(ctx context.Context, t types.KeyType) (address.Address, error) {
if t != types.KTSecp256k1Ledger {
return address.Undef, fmt.Errorf("unsupported key type: '%s', only '%s' supported",
t, types.KTSecp256k1Ledger)
}
res, err := lw.ds.Query(query.Query{Prefix: dsLedgerPrefix})
if err != nil {
return address.Undef, err
}
defer res.Close() // nolint:errcheck
var maxi int64 = -1
for {
res, ok := res.NextSync()
if !ok {
break
}
var ki LedgerKeyInfo
if err := json.Unmarshal(res.Value, &ki); err != nil {
return address.Undef, err
}
if i := ki.Path[filHdPathLen-1]; maxi == -1 || maxi < int64(i) {
maxi = int64(i)
}
}
fl, err := ledgerfil.FindLedgerFilecoinApp()
if err != nil {
return address.Undef, xerrors.Errorf("finding ledger: %w", err)
}
defer fl.Close() // nolint:errcheck
path := append(append([]uint32(nil), filHDBasePath...), uint32(maxi+1))
_, _, addr, err := fl.GetAddressPubKeySECP256K1(path)
if err != nil {
return address.Undef, xerrors.Errorf("getting public key from ledger: %w", err)
}
log.Warnf("creating key: %s, accept the key in ledger device", addr)
_, _, addr, err = fl.ShowAddressPubKeySECP256K1(path)
if err != nil {
return address.Undef, xerrors.Errorf("verifying public key with ledger: %w", err)
}
a, err := address.NewFromString(addr)
if err != nil {
return address.Undef, fmt.Errorf("parsing address: %w", err)
}
var lki LedgerKeyInfo
lki.Address = a
lki.Path = path
return lw.importKey(lki)
}
func (lw *LedgerWallet) Get() api.WalletAPI {
if lw == nil {
return nil
}
return lw
}
var dsLedgerPrefix = "/ledgerkey/"
func keyForAddr(addr address.Address) datastore.Key {
return datastore.NewKey(dsLedgerPrefix + addr.String())
}

170
chain/wallet/multi.go Normal file
View File

@ -0,0 +1,170 @@
package wallet
import (
"context"
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
ledgerwallet "github.com/filecoin-project/lotus/chain/wallet/ledger"
"github.com/filecoin-project/lotus/chain/wallet/remotewallet"
)
type MultiWallet struct {
fx.In // "constructed" with fx.In instead of normal constructor
Local *LocalWallet `optional:"true"`
Remote *remotewallet.RemoteWallet `optional:"true"`
Ledger *ledgerwallet.LedgerWallet `optional:"true"`
}
type getif interface {
api.WalletAPI
// workaround for the fact that iface(*struct(nil)) != nil
Get() api.WalletAPI
}
func firstNonNil(wallets ...getif) api.WalletAPI {
for _, w := range wallets {
if w.Get() != nil {
return w
}
}
return nil
}
func nonNil(wallets ...getif) []api.WalletAPI {
var out []api.WalletAPI
for _, w := range wallets {
if w.Get() == nil {
continue
}
out = append(out, w)
}
return out
}
func (m MultiWallet) find(ctx context.Context, address address.Address, wallets ...getif) (api.WalletAPI, error) {
ws := nonNil(wallets...)
for _, w := range ws {
have, err := w.WalletHas(ctx, address)
if err != nil {
return nil, err
}
if have {
return w, nil
}
}
return nil, nil
}
func (m MultiWallet) WalletNew(ctx context.Context, keyType types.KeyType) (address.Address, error) {
var local getif = m.Local
if keyType == types.KTSecp256k1Ledger {
local = m.Ledger
}
w := firstNonNil(m.Remote, local)
if w == nil {
return address.Undef, xerrors.Errorf("no wallet backends supporting key type: %s", keyType)
}
return w.WalletNew(ctx, keyType)
}
func (m MultiWallet) WalletHas(ctx context.Context, address address.Address) (bool, error) {
w, err := m.find(ctx, address, m.Remote, m.Ledger, m.Local)
return w != nil, err
}
func (m MultiWallet) WalletList(ctx context.Context) ([]address.Address, error) {
var out []address.Address
seen := map[address.Address]struct{}{}
ws := nonNil(m.Remote, m.Ledger, m.Local)
for _, w := range ws {
l, err := w.WalletList(ctx)
if err != nil {
return nil, err
}
for _, a := range l {
if _, ok := seen[a]; ok {
continue
}
seen[a] = struct{}{}
out = append(out, a)
}
}
return out, nil
}
func (m MultiWallet) WalletSign(ctx context.Context, signer address.Address, toSign []byte, meta api.MsgMeta) (*crypto.Signature, error) {
w, err := m.find(ctx, signer, m.Remote, m.Ledger, m.Local)
if err != nil {
return nil, err
}
if w == nil {
return nil, xerrors.Errorf("key not found")
}
return w.WalletSign(ctx, signer, toSign, meta)
}
func (m MultiWallet) WalletExport(ctx context.Context, address address.Address) (*types.KeyInfo, error) {
w, err := m.find(ctx, address, m.Remote, m.Local)
if err != nil {
return nil, err
}
if w == nil {
return nil, xerrors.Errorf("key not found")
}
return w.WalletExport(ctx, address)
}
func (m MultiWallet) WalletImport(ctx context.Context, info *types.KeyInfo) (address.Address, error) {
var local getif = m.Local
if info.Type == types.KTSecp256k1Ledger {
local = m.Ledger
}
w := firstNonNil(m.Remote, local)
if w == nil {
return address.Undef, xerrors.Errorf("no wallet backends configured")
}
return w.WalletImport(ctx, info)
}
func (m MultiWallet) WalletDelete(ctx context.Context, address address.Address) error {
for {
w, err := m.find(ctx, address, m.Remote, m.Ledger, m.Local)
if err != nil {
return err
}
if w == nil {
return nil
}
if err := w.WalletDelete(ctx, address); err != nil {
return err
}
}
}
var _ api.WalletAPI = MultiWallet{}

View File

@ -0,0 +1,50 @@
package remotewallet
import (
"context"
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/client"
cliutil "github.com/filecoin-project/lotus/cli/util"
"github.com/filecoin-project/lotus/node/modules/helpers"
)
type RemoteWallet struct {
api.WalletAPI
}
func SetupRemoteWallet(info string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle) (*RemoteWallet, error) {
return func(mctx helpers.MetricsCtx, lc fx.Lifecycle) (*RemoteWallet, error) {
ai := cliutil.ParseApiInfo(info)
url, err := ai.DialArgs()
if err != nil {
return nil, err
}
wapi, closer, err := client.NewWalletRPC(mctx, url, ai.AuthHeader())
if err != nil {
return nil, xerrors.Errorf("creating jsonrpc client: %w", err)
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
closer()
return nil
},
})
return &RemoteWallet{wapi}, nil
}
}
func (w *RemoteWallet) Get() api.WalletAPI {
if w == nil {
return nil
}
return w
}

View File

@ -12,6 +12,7 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
_ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures _ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
_ "github.com/filecoin-project/lotus/lib/sigs/secp" // enable secp signatures _ "github.com/filecoin-project/lotus/lib/sigs/secp" // enable secp signatures
@ -25,19 +26,22 @@ const (
KNamePrefix = "wallet-" KNamePrefix = "wallet-"
KTrashPrefix = "trash-" KTrashPrefix = "trash-"
KDefault = "default" KDefault = "default"
KTBLS = "bls"
KTSecp256k1 = "secp256k1"
) )
type Wallet struct { type LocalWallet struct {
keys map[address.Address]*Key keys map[address.Address]*Key
keystore types.KeyStore keystore types.KeyStore
lk sync.Mutex lk sync.Mutex
} }
func NewWallet(keystore types.KeyStore) (*Wallet, error) { type Default interface {
w := &Wallet{ GetDefault() (address.Address, error)
SetDefault(a address.Address) error
}
func NewWallet(keystore types.KeyStore) (*LocalWallet, error) {
w := &LocalWallet{
keys: make(map[address.Address]*Key), keys: make(map[address.Address]*Key),
keystore: keystore, keystore: keystore,
} }
@ -45,18 +49,18 @@ func NewWallet(keystore types.KeyStore) (*Wallet, error) {
return w, nil return w, nil
} }
func KeyWallet(keys ...*Key) *Wallet { func KeyWallet(keys ...*Key) *LocalWallet {
m := make(map[address.Address]*Key) m := make(map[address.Address]*Key)
for _, key := range keys { for _, key := range keys {
m[key.Address] = key m[key.Address] = key
} }
return &Wallet{ return &LocalWallet{
keys: m, keys: m,
} }
} }
func (w *Wallet) Sign(ctx context.Context, addr address.Address, msg []byte) (*crypto.Signature, error) { func (w *LocalWallet) WalletSign(ctx context.Context, addr address.Address, msg []byte, meta api.MsgMeta) (*crypto.Signature, error) {
ki, err := w.findKey(addr) ki, err := w.findKey(addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -68,7 +72,7 @@ func (w *Wallet) Sign(ctx context.Context, addr address.Address, msg []byte) (*c
return sigs.Sign(ActSigType(ki.Type), ki.PrivateKey, msg) return sigs.Sign(ActSigType(ki.Type), ki.PrivateKey, msg)
} }
func (w *Wallet) findKey(addr address.Address) (*Key, error) { func (w *LocalWallet) findKey(addr address.Address) (*Key, error) {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
@ -96,7 +100,7 @@ func (w *Wallet) findKey(addr address.Address) (*Key, error) {
return k, nil return k, nil
} }
func (w *Wallet) tryFind(addr address.Address) (types.KeyInfo, error) { func (w *LocalWallet) tryFind(addr address.Address) (types.KeyInfo, error) {
ki, err := w.keystore.Get(KNamePrefix + addr.String()) ki, err := w.keystore.Get(KNamePrefix + addr.String())
if err == nil { if err == nil {
@ -110,14 +114,12 @@ func (w *Wallet) tryFind(addr address.Address) (types.KeyInfo, error) {
// We got an ErrKeyInfoNotFound error // We got an ErrKeyInfoNotFound error
// Try again, this time with the testnet prefix // Try again, this time with the testnet prefix
aChars := []rune(addr.String()) tAddress, err := swapMainnetForTestnetPrefix(addr.String())
prefixRunes := []rune(address.TestnetPrefix) if err != nil {
if len(prefixRunes) != 1 { return types.KeyInfo{}, err
return types.KeyInfo{}, xerrors.Errorf("unexpected prefix length: %d", len(prefixRunes))
} }
aChars[0] = prefixRunes[0] ki, err = w.keystore.Get(KNamePrefix + tAddress)
ki, err = w.keystore.Get(KNamePrefix + string(aChars))
if err != nil { if err != nil {
return types.KeyInfo{}, err return types.KeyInfo{}, err
} }
@ -132,16 +134,19 @@ func (w *Wallet) tryFind(addr address.Address) (types.KeyInfo, error) {
return ki, nil return ki, nil
} }
func (w *Wallet) Export(addr address.Address) (*types.KeyInfo, error) { func (w *LocalWallet) WalletExport(ctx context.Context, addr address.Address) (*types.KeyInfo, error) {
k, err := w.findKey(addr) k, err := w.findKey(addr)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to find key to export: %w", err) return nil, xerrors.Errorf("failed to find key to export: %w", err)
} }
if k == nil {
return nil, xerrors.Errorf("key not found")
}
return &k.KeyInfo, nil return &k.KeyInfo, nil
} }
func (w *Wallet) Import(ki *types.KeyInfo) (address.Address, error) { func (w *LocalWallet) WalletImport(ctx context.Context, ki *types.KeyInfo) (address.Address, error) {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
@ -157,7 +162,7 @@ func (w *Wallet) Import(ki *types.KeyInfo) (address.Address, error) {
return k.Address, nil return k.Address, nil
} }
func (w *Wallet) ListAddrs() ([]address.Address, error) { func (w *LocalWallet) WalletList(ctx context.Context) ([]address.Address, error) {
all, err := w.keystore.List() all, err := w.keystore.List()
if err != nil { if err != nil {
return nil, xerrors.Errorf("listing keystore: %w", err) return nil, xerrors.Errorf("listing keystore: %w", err)
@ -190,7 +195,7 @@ func (w *Wallet) ListAddrs() ([]address.Address, error) {
return out, nil return out, nil
} }
func (w *Wallet) GetDefault() (address.Address, error) { func (w *LocalWallet) GetDefault() (address.Address, error) {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
@ -207,7 +212,7 @@ func (w *Wallet) GetDefault() (address.Address, error) {
return k.Address, nil return k.Address, nil
} }
func (w *Wallet) SetDefault(a address.Address) error { func (w *LocalWallet) SetDefault(a address.Address) error {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
@ -229,19 +234,7 @@ func (w *Wallet) SetDefault(a address.Address) error {
return nil return nil
} }
func GenerateKey(typ crypto.SigType) (*Key, error) { func (w *LocalWallet) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) {
pk, err := sigs.Generate(typ)
if err != nil {
return nil, err
}
ki := types.KeyInfo{
Type: kstoreSigType(typ),
PrivateKey: pk,
}
return NewKey(ki)
}
func (w *Wallet) GenerateKey(typ crypto.SigType) (address.Address, error) {
w.lk.Lock() w.lk.Lock()
defer w.lk.Unlock() defer w.lk.Unlock()
@ -269,7 +262,7 @@ func (w *Wallet) GenerateKey(typ crypto.SigType) (address.Address, error) {
return k.Address, nil return k.Address, nil
} }
func (w *Wallet) HasKey(addr address.Address) (bool, error) { func (w *LocalWallet) WalletHas(ctx context.Context, addr address.Address) (bool, error) {
k, err := w.findKey(addr) k, err := w.findKey(addr)
if err != nil { if err != nil {
return false, err return false, err
@ -277,11 +270,14 @@ func (w *Wallet) HasKey(addr address.Address) (bool, error) {
return k != nil, nil return k != nil, nil
} }
func (w *Wallet) DeleteKey(addr address.Address) error { func (w *LocalWallet) WalletDelete(ctx context.Context, addr address.Address) error {
k, err := w.findKey(addr) k, err := w.findKey(addr)
if err != nil { if err != nil {
return xerrors.Errorf("failed to delete key %s : %w", addr, err) return xerrors.Errorf("failed to delete key %s : %w", addr, err)
} }
if k == nil {
return nil // already not there
}
if err := w.keystore.Put(KTrashPrefix+k.Address.String(), k.KeyInfo); err != nil { if err := w.keystore.Put(KTrashPrefix+k.Address.String(), k.KeyInfo); err != nil {
return xerrors.Errorf("failed to mark key %s as trashed: %w", addr, err) return xerrors.Errorf("failed to mark key %s as trashed: %w", addr, err)
@ -291,63 +287,47 @@ func (w *Wallet) DeleteKey(addr address.Address) error {
return xerrors.Errorf("failed to delete key %s: %w", addr, err) return xerrors.Errorf("failed to delete key %s: %w", addr, err)
} }
tAddr, err := swapMainnetForTestnetPrefix(addr.String())
if err != nil {
return xerrors.Errorf("failed to swap prefixes: %w", err)
}
// TODO: Does this always error in the not-found case? Just ignoring an error return for now.
_ = w.keystore.Delete(KNamePrefix + tAddr)
return nil return nil
} }
type Key struct { func (w *LocalWallet) Get() api.WalletAPI {
types.KeyInfo if w == nil {
return nil
PublicKey []byte
Address address.Address
} }
func NewKey(keyinfo types.KeyInfo) (*Key, error) { return w
k := &Key{
KeyInfo: keyinfo,
} }
var err error var _ api.WalletAPI = &LocalWallet{}
k.PublicKey, err = sigs.ToPublic(ActSigType(k.Type), k.PrivateKey)
if err != nil { func swapMainnetForTestnetPrefix(addr string) (string, error) {
return nil, err aChars := []rune(addr)
prefixRunes := []rune(address.TestnetPrefix)
if len(prefixRunes) != 1 {
return "", xerrors.Errorf("unexpected prefix length: %d", len(prefixRunes))
} }
switch k.Type { aChars[0] = prefixRunes[0]
case KTSecp256k1: return string(aChars), nil
k.Address, err = address.NewSecp256k1Address(k.PublicKey)
if err != nil {
return nil, xerrors.Errorf("converting Secp256k1 to address: %w", err)
}
case KTBLS:
k.Address, err = address.NewBLSAddress(k.PublicKey)
if err != nil {
return nil, xerrors.Errorf("converting BLS to address: %w", err)
}
default:
return nil, xerrors.Errorf("unknown key type")
}
return k, nil
} }
func kstoreSigType(typ crypto.SigType) string { type nilDefault struct{}
switch typ {
case crypto.SigTypeBLS: func (n nilDefault) GetDefault() (address.Address, error) {
return KTBLS return address.Undef, nil
case crypto.SigTypeSecp256k1:
return KTSecp256k1
default:
return ""
}
} }
func ActSigType(typ string) crypto.SigType { func (n nilDefault) SetDefault(a address.Address) error {
switch typ { return xerrors.Errorf("not supported; local wallet disabled")
case KTBLS:
return crypto.SigTypeBLS
case KTSecp256k1:
return crypto.SigTypeSecp256k1
default:
return 0
}
} }
var NilDefault nilDefault
var _ Default = NilDefault

View File

@ -3,6 +3,7 @@ package cli
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
@ -53,6 +54,7 @@ var chainCmd = &cli.Command{
slashConsensusFault, slashConsensusFault,
chainGasPriceCmd, chainGasPriceCmd,
chainInspectUsage, chainInspectUsage,
chainDecodeCmd,
}, },
} }
@ -449,11 +451,16 @@ var chainInspectUsage = &cli.Command{
bySender := make(map[string]int64) bySender := make(map[string]int64)
byDest := make(map[string]int64) byDest := make(map[string]int64)
byMethod := make(map[string]int64) byMethod := make(map[string]int64)
bySenderC := make(map[string]int64)
byDestC := make(map[string]int64)
byMethodC := make(map[string]int64)
var sum int64 var sum int64
for _, m := range msgs { for _, m := range msgs {
bySender[m.Message.From.String()] += m.Message.GasLimit bySender[m.Message.From.String()] += m.Message.GasLimit
bySenderC[m.Message.From.String()]++
byDest[m.Message.To.String()] += m.Message.GasLimit byDest[m.Message.To.String()] += m.Message.GasLimit
byDestC[m.Message.To.String()]++
sum += m.Message.GasLimit sum += m.Message.GasLimit
code, err := lookupActorCode(m.Message.To) code, err := lookupActorCode(m.Message.To)
@ -464,7 +471,7 @@ var chainInspectUsage = &cli.Command{
mm := stmgr.MethodsMap[code][m.Message.Method] mm := stmgr.MethodsMap[code][m.Message.Method]
byMethod[mm.Name] += m.Message.GasLimit byMethod[mm.Name] += m.Message.GasLimit
byMethodC[mm.Name]++
} }
type keyGasPair struct { type keyGasPair struct {
@ -496,19 +503,19 @@ var chainInspectUsage = &cli.Command{
fmt.Printf("By Sender:\n") fmt.Printf("By Sender:\n")
for i := 0; i < numRes && i < len(senderVals); i++ { for i := 0; i < numRes && i < len(senderVals); i++ {
sv := senderVals[i] sv := senderVals[i]
fmt.Printf("%s\t%0.2f%%\t(%d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas) fmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, bySenderC[sv.Key])
} }
fmt.Println() fmt.Println()
fmt.Printf("By Receiver:\n") fmt.Printf("By Receiver:\n")
for i := 0; i < numRes && i < len(destVals); i++ { for i := 0; i < numRes && i < len(destVals); i++ {
sv := destVals[i] sv := destVals[i]
fmt.Printf("%s\t%0.2f%%\t(%d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas) fmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byDestC[sv.Key])
} }
fmt.Println() fmt.Println()
fmt.Printf("By Method:\n") fmt.Printf("By Method:\n")
for i := 0; i < numRes && i < len(methodVals); i++ { for i := 0; i < numRes && i < len(methodVals); i++ {
sv := methodVals[i] sv := methodVals[i]
fmt.Printf("%s\t%0.2f%%\t(%d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas) fmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byMethodC[sv.Key])
} }
return nil return nil
@ -517,6 +524,7 @@ var chainInspectUsage = &cli.Command{
var chainListCmd = &cli.Command{ var chainListCmd = &cli.Command{
Name: "list", Name: "list",
Aliases: []string{"love"},
Usage: "View a segment of the chain", Usage: "View a segment of the chain",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.Uint64Flag{Name: "height"}, &cli.Uint64Flag{Name: "height"},
@ -1228,3 +1236,68 @@ var chainGasPriceCmd = &cli.Command{
return nil return nil
}, },
} }
var chainDecodeCmd = &cli.Command{
Name: "decode",
Usage: "decode various types",
Subcommands: []*cli.Command{
chainDecodeParamsCmd,
},
}
var chainDecodeParamsCmd = &cli.Command{
Name: "params",
Usage: "Decode message params",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "tipset",
},
},
ArgsUsage: "[toAddr method hexParams]",
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
if cctx.Args().Len() != 3 {
return ShowHelp(cctx, fmt.Errorf("incorrect number of arguments"))
}
to, err := address.NewFromString(cctx.Args().First())
if err != nil {
return xerrors.Errorf("parsing toAddr: %w", err)
}
method, err := strconv.ParseInt(cctx.Args().Get(1), 10, 64)
if err != nil {
return xerrors.Errorf("parsing method id: %w", err)
}
params, err := hex.DecodeString(cctx.Args().Get(2))
if err != nil {
return xerrors.Errorf("parsing hex params: %w", err)
}
ts, err := LoadTipSet(ctx, cctx, api)
if err != nil {
return err
}
act, err := api.StateGetActor(ctx, to, ts.Key())
if err != nil {
return xerrors.Errorf("getting actor: %w", err)
}
pstr, err := jsonParams(act.Code, abi.MethodNum(method), params)
if err != nil {
return err
}
fmt.Println(pstr)
return nil
},
}

View File

@ -536,7 +536,7 @@ func interactiveDeal(cctx *cli.Context) error {
state = "miner" state = "miner"
case "miner": case "miner":
fmt.Print("Miner Address (t0..): ") fmt.Print("Miner Address (f0..): ")
var maddrStr string var maddrStr string
_, err := fmt.Scan(&maddrStr) _, err := fmt.Scan(&maddrStr)
@ -1039,6 +1039,10 @@ var clientListDeals = &cli.Command{
Usage: "use color in display output", Usage: "use color in display output",
Value: true, Value: true,
}, },
&cli.BoolFlag{
Name: "show-failed",
Usage: "show failed/failing deals",
},
&cli.BoolFlag{ &cli.BoolFlag{
Name: "watch", Name: "watch",
Usage: "watch deal updates in real-time, rather than a one time list", Usage: "watch deal updates in real-time, rather than a one time list",
@ -1055,6 +1059,7 @@ var clientListDeals = &cli.Command{
verbose := cctx.Bool("verbose") verbose := cctx.Bool("verbose")
color := cctx.Bool("color") color := cctx.Bool("color")
watch := cctx.Bool("watch") watch := cctx.Bool("watch")
showFailed := cctx.Bool("show-failed")
localDeals, err := api.ClientListDeals(ctx) localDeals, err := api.ClientListDeals(ctx)
if err != nil { if err != nil {
@ -1071,7 +1076,7 @@ var clientListDeals = &cli.Command{
tm.Clear() tm.Clear()
tm.MoveCursor(1, 1) tm.MoveCursor(1, 1)
err = outputStorageDeals(ctx, tm.Screen, api, localDeals, verbose, color) err = outputStorageDeals(ctx, tm.Screen, api, localDeals, verbose, color, showFailed)
if err != nil { if err != nil {
return err return err
} }
@ -1097,7 +1102,7 @@ var clientListDeals = &cli.Command{
} }
} }
return outputStorageDeals(ctx, os.Stdout, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color")) return outputStorageDeals(ctx, os.Stdout, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color"), showFailed)
}, },
} }
@ -1120,7 +1125,7 @@ func dealFromDealInfo(ctx context.Context, full api.FullNode, head *types.TipSet
} }
} }
func outputStorageDeals(ctx context.Context, out io.Writer, full api.FullNode, localDeals []api.DealInfo, verbose bool, color bool) error { func outputStorageDeals(ctx context.Context, out io.Writer, full lapi.FullNode, localDeals []lapi.DealInfo, verbose bool, color bool, showFailed bool) error {
sort.Slice(localDeals, func(i, j int) bool { sort.Slice(localDeals, func(i, j int) bool {
return localDeals[i].CreationTime.Before(localDeals[j].CreationTime) return localDeals[i].CreationTime.Before(localDeals[j].CreationTime)
}) })
@ -1132,12 +1137,14 @@ func outputStorageDeals(ctx context.Context, out io.Writer, full api.FullNode, l
var deals []deal var deals []deal
for _, localDeal := range localDeals { for _, localDeal := range localDeals {
if showFailed || localDeal.State != storagemarket.StorageDealError {
deals = append(deals, dealFromDealInfo(ctx, full, head, localDeal)) deals = append(deals, dealFromDealInfo(ctx, full, head, localDeal))
} }
}
if verbose { if verbose {
w := tabwriter.NewWriter(out, 2, 4, 2, ' ', 0) w := tabwriter.NewWriter(out, 2, 4, 2, ' ', 0)
fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tMessage\n") fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tVerified\tMessage\n")
for _, d := range deals { for _, d := range deals {
onChain := "N" onChain := "N"
if d.OnChainDealState.SectorStartEpoch != -1 { if d.OnChainDealState.SectorStartEpoch != -1 {
@ -1150,7 +1157,7 @@ func outputStorageDeals(ctx context.Context, out io.Writer, full api.FullNode, l
} }
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration))) price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.CreationTime.Format(time.Stamp), d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Message) fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%v\t%s\n", d.LocalDeal.CreationTime.Format(time.Stamp), d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Verified, d.LocalDeal.Message)
} }
return w.Flush() return w.Flush()
} }
@ -1165,6 +1172,7 @@ func outputStorageDeals(ctx context.Context, out io.Writer, full api.FullNode, l
tablewriter.Col("Size"), tablewriter.Col("Size"),
tablewriter.Col("Price"), tablewriter.Col("Price"),
tablewriter.Col("Duration"), tablewriter.Col("Duration"),
tablewriter.Col("Verified"),
tablewriter.NewLineCol("Message")) tablewriter.NewLineCol("Message"))
for _, d := range deals { for _, d := range deals {
@ -1194,6 +1202,7 @@ func outputStorageDeals(ctx context.Context, out io.Writer, full api.FullNode, l
"PieceCID": piece, "PieceCID": piece,
"Size": types.SizeStr(types.NewInt(d.LocalDeal.Size)), "Size": types.SizeStr(types.NewInt(d.LocalDeal.Size)),
"Price": price, "Price": price,
"Verified": d.LocalDeal.Verified,
"Duration": d.LocalDeal.Duration, "Duration": d.LocalDeal.Duration,
"Message": d.LocalDeal.Message, "Message": d.LocalDeal.Message,
}) })

View File

@ -11,8 +11,6 @@ import (
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/xerrors" "golang.org/x/xerrors"
@ -20,6 +18,7 @@ import (
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/client" "github.com/filecoin-project/lotus/api/client"
cliutil "github.com/filecoin-project/lotus/cli/util"
"github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/node/repo"
) )
@ -46,37 +45,16 @@ func NewCliError(s string) error {
// ApiConnector returns API instance // ApiConnector returns API instance
type ApiConnector func() api.FullNode type ApiConnector func() api.FullNode
type APIInfo struct {
Addr multiaddr.Multiaddr
Token []byte
}
func (a APIInfo) DialArgs() (string, error) {
_, addr, err := manet.DialArgs(a.Addr)
return "ws://" + addr + "/rpc/v0", err
}
func (a APIInfo) AuthHeader() http.Header {
if len(a.Token) != 0 {
headers := http.Header{}
headers.Add("Authorization", "Bearer "+string(a.Token))
return headers
}
log.Warn("API Token not set and requested, capabilities might be limited.")
return nil
}
// The flag passed on the command line with the listen address of the API // The flag passed on the command line with the listen address of the API
// server (only used by the tests) // server (only used by the tests)
func flagForAPI(t repo.RepoType) string { func flagForAPI(t repo.RepoType) string {
switch t { switch t {
case repo.FullNode: case repo.FullNode:
return "api" return "api-url"
case repo.StorageMiner: case repo.StorageMiner:
return "miner-api" return "miner-api-url"
case repo.Worker: case repo.Worker:
return "worker-api" return "worker-api-url"
default: default:
panic(fmt.Sprintf("Unknown repo type: %v", t)) panic(fmt.Sprintf("Unknown repo type: %v", t))
} }
@ -122,7 +100,7 @@ func envForRepoDeprecation(t repo.RepoType) string {
} }
} }
func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) { func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (cliutil.APIInfo, error) {
// Check if there was a flag passed with the listen address of the API // Check if there was a flag passed with the listen address of the API
// server (only used by the tests) // server (only used by the tests)
apiFlag := flagForAPI(t) apiFlag := flagForAPI(t)
@ -130,11 +108,7 @@ func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) {
strma := ctx.String(apiFlag) strma := ctx.String(apiFlag)
strma = strings.TrimSpace(strma) strma = strings.TrimSpace(strma)
apima, err := multiaddr.NewMultiaddr(strma) return cliutil.APIInfo{Addr: strma}, nil
if err != nil {
return APIInfo{}, err
}
return APIInfo{Addr: apima}, nil
} }
envKey := envForRepo(t) envKey := envForRepo(t)
@ -148,36 +122,24 @@ func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) {
} }
} }
if ok { if ok {
sp := strings.SplitN(env, ":", 2) return cliutil.ParseApiInfo(env), nil
if len(sp) != 2 {
log.Warnf("invalid env(%s) value, missing token or address", envKey)
} else {
ma, err := multiaddr.NewMultiaddr(sp[1])
if err != nil {
return APIInfo{}, xerrors.Errorf("could not parse multiaddr from env(%s): %w", envKey, err)
}
return APIInfo{
Addr: ma,
Token: []byte(sp[0]),
}, nil
}
} }
repoFlag := flagForRepo(t) repoFlag := flagForRepo(t)
p, err := homedir.Expand(ctx.String(repoFlag)) p, err := homedir.Expand(ctx.String(repoFlag))
if err != nil { if err != nil {
return APIInfo{}, xerrors.Errorf("could not expand home dir (%s): %w", repoFlag, err) return cliutil.APIInfo{}, xerrors.Errorf("could not expand home dir (%s): %w", repoFlag, err)
} }
r, err := repo.NewFS(p) r, err := repo.NewFS(p)
if err != nil { if err != nil {
return APIInfo{}, xerrors.Errorf("could not open repo at path: %s; %w", p, err) return cliutil.APIInfo{}, xerrors.Errorf("could not open repo at path: %s; %w", p, err)
} }
ma, err := r.APIEndpoint() ma, err := r.APIEndpoint()
if err != nil { if err != nil {
return APIInfo{}, xerrors.Errorf("could not get api endpoint: %w", err) return cliutil.APIInfo{}, xerrors.Errorf("could not get api endpoint: %w", err)
} }
token, err := r.APIToken() token, err := r.APIToken()
@ -185,8 +147,8 @@ func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) {
log.Warnf("Couldn't load CLI token, capabilities may be limited: %v", err) log.Warnf("Couldn't load CLI token, capabilities may be limited: %v", err)
} }
return APIInfo{ return cliutil.APIInfo{
Addr: ma, Addr: ma.String(),
Token: token, Token: token,
}, nil }, nil
} }
@ -266,6 +228,15 @@ func GetWorkerAPI(ctx *cli.Context) (api.WorkerAPI, jsonrpc.ClientCloser, error)
return client.NewWorkerRPC(ctx.Context, addr, headers) return client.NewWorkerRPC(ctx.Context, addr, headers)
} }
func GetGatewayAPI(ctx *cli.Context) (api.GatewayAPI, jsonrpc.ClientCloser, error) {
addr, headers, err := GetRawAPI(ctx, repo.FullNode)
if err != nil {
return nil, nil, err
}
return client.NewGatewayRPC(ctx.Context, addr, headers)
}
func DaemonContext(cctx *cli.Context) context.Context { func DaemonContext(cctx *cli.Context) context.Context {
if mtCtx, ok := cctx.App.Metadata[metadataTraceContext]; ok { if mtCtx, ok := cctx.App.Metadata[metadataTraceContext]; ok {
return mtCtx.(context.Context) return mtCtx.(context.Context)

View File

@ -35,7 +35,7 @@ func RunApp(app *cli.App) {
if os.Getenv("LOTUS_DEV") != "" { if os.Getenv("LOTUS_DEV") != "" {
log.Warnf("%+v", err) log.Warnf("%+v", err)
} else { } else {
fmt.Printf("ERROR: %s\n\n", err) fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err) // nolint:errcheck
} }
var phe *PrintHelpErr var phe *PrintHelpErr
if xerrors.As(err, &phe) { if xerrors.As(err, &phe) {

View File

@ -3,8 +3,10 @@ package cli
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"os" "os"
"reflect"
"sort" "sort"
"strconv" "strconv"
"text/tabwriter" "text/tabwriter"
@ -14,6 +16,8 @@ import (
"github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/stmgr"
cbg "github.com/whyrusleeping/cbor-gen"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
@ -174,6 +178,10 @@ var msigInspectCmd = &cli.Command{
Name: "vesting", Name: "vesting",
Usage: "Include vesting details", Usage: "Include vesting details",
}, },
&cli.BoolFlag{
Name: "decode-params",
Usage: "Decode parameters of transaction proposals",
},
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() { if !cctx.Args().Present() {
@ -204,6 +212,11 @@ var msigInspectCmd = &cli.Command{
return err return err
} }
ownId, err := api.StateLookupID(ctx, maddr, types.EmptyTSK)
if err != nil {
return err
}
mstate, err := multisig.Load(store, act) mstate, err := multisig.Load(store, act)
if err != nil { if err != nil {
return err return err
@ -256,6 +269,7 @@ var msigInspectCmd = &cli.Command{
return xerrors.Errorf("reading pending transactions: %w", err) return xerrors.Errorf("reading pending transactions: %w", err)
} }
decParams := cctx.Bool("decode-params")
fmt.Println("Transactions: ", len(pending)) fmt.Println("Transactions: ", len(pending))
if len(pending) > 0 { if len(pending) > 0 {
var txids []int64 var txids []int64
@ -266,11 +280,36 @@ var msigInspectCmd = &cli.Command{
return txids[i] < txids[j] return txids[i] < txids[j]
}) })
w := tabwriter.NewWriter(os.Stdout, 8, 4, 0, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 8, 4, 2, ' ', 0)
fmt.Fprintf(w, "ID\tState\tApprovals\tTo\tValue\tMethod\tParams\n") fmt.Fprintf(w, "ID\tState\tApprovals\tTo\tValue\tMethod\tParams\n")
for _, txid := range txids { for _, txid := range txids {
tx := pending[txid] tx := pending[txid]
fmt.Fprintf(w, "%d\t%s\t%d\t%s\t%s\t%d\t%x\n", txid, "pending", len(tx.Approved), tx.To, types.FIL(tx.Value), tx.Method, tx.Params) target := tx.To.String()
if tx.To == ownId {
target += " (self)"
}
targAct, err := api.StateGetActor(ctx, tx.To, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("failed to resolve 'To' address of multisig transaction %d: %w", txid, err)
}
method := stmgr.MethodsMap[targAct.Code][tx.Method]
paramStr := fmt.Sprintf("%x", tx.Params)
if decParams && tx.Method != 0 {
ptyp := reflect.New(method.Params.Elem()).Interface().(cbg.CBORUnmarshaler)
if err := ptyp.UnmarshalCBOR(bytes.NewReader(tx.Params)); err != nil {
return xerrors.Errorf("failed to decode parameters of transaction %d: %w", txid, err)
}
b, err := json.Marshal(ptyp)
if err != nil {
return xerrors.Errorf("could not json marshal parameter type: %w", err)
}
paramStr = string(b)
}
fmt.Fprintf(w, "%d\t%s\t%d\t%s\t%s\t%s(%d)\t%s\n", txid, "pending", len(tx.Approved), target, types.FIL(tx.Value), method.Name, tx.Method, paramStr)
} }
if err := w.Flush(); err != nil { if err := w.Flush(); err != nil {
return xerrors.Errorf("flushing output: %+v", err) return xerrors.Errorf("flushing output: %+v", err)
@ -398,7 +437,7 @@ var msigProposeCmd = &cli.Command{
var msigApproveCmd = &cli.Command{ var msigApproveCmd = &cli.Command{
Name: "approve", Name: "approve",
Usage: "Approve a multisig message", Usage: "Approve a multisig message",
ArgsUsage: "[multisigAddress messageId proposerAddress destination value <methodId methodParams> (optional)]", ArgsUsage: "<multisigAddress messageId> [proposerAddress destination value [methodId methodParams]]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "from", Name: "from",
@ -484,7 +523,7 @@ var msigApproveCmd = &cli.Command{
from = defaddr from = defaddr
} }
msgCid, err := api.MsigApprove(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params) msgCid, err := api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params)
if err != nil { if err != nil {
return err return err
} }
@ -1129,7 +1168,7 @@ var msigLockApproveCmd = &cli.Command{
return actErr return actErr
} }
msgCid, err := api.MsigApprove(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(builtin2.MethodsMultisig.LockBalance), params) msgCid, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(builtin2.MethodsMultisig.LockBalance), params)
if err != nil { if err != nil {
return err return err
} }

View File

@ -29,7 +29,6 @@ import (
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
builder "github.com/filecoin-project/lotus/node/test" builder "github.com/filecoin-project/lotus/node/test"
) )
@ -121,13 +120,13 @@ func TestPaymentChannelStatus(t *testing.T) {
create := make(chan string) create := make(chan string)
go func() { go func() {
// creator: paych add-funds <creator> <receiver> <amount> // creator: paych add-funds <creator> <receiver> <amount>
cmd = []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)} cmd := []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
create <- creatorCLI.runCmd(paychAddFundsCmd, cmd) create <- creatorCLI.runCmd(paychAddFundsCmd, cmd)
}() }()
// Wait for the output to stop being "Channel does not exist" // Wait for the output to stop being "Channel does not exist"
for regexp.MustCompile(noChannelState).MatchString(out) { for regexp.MustCompile(noChannelState).MatchString(out) {
cmd = []string{creatorAddr.String(), receiverAddr.String()} cmd := []string{creatorAddr.String(), receiverAddr.String()}
out = creatorCLI.runCmd(paychStatusByFromToCmd, cmd) out = creatorCLI.runCmd(paychStatusByFromToCmd, cmd)
} }
fmt.Println(out) fmt.Println(out)
@ -390,7 +389,7 @@ func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) {
} }
func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) { func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) {
n, sn := builder.RPCMockSbBuilder(t, 2, test.OneMiner) n, sn := builder.RPCMockSbBuilder(t, test.TwoFull, test.OneMiner)
paymentCreator := n[0] paymentCreator := n[0]
paymentReceiver := n[1] paymentReceiver := n[1]
@ -415,7 +414,7 @@ func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Dur
bm.MineBlocks() bm.MineBlocks()
// Send some funds to register the receiver // Send some funds to register the receiver
receiverAddr, err := paymentReceiver.WalletNew(ctx, wallet.ActSigType("secp256k1")) receiverAddr, err := paymentReceiver.WalletNew(ctx, types.KTSecp256k1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -439,12 +438,12 @@ type mockCLI struct {
} }
func newMockCLI(t *testing.T) *mockCLI { func newMockCLI(t *testing.T) *mockCLI {
// Create a CLI App with an --api flag so that we can specify which node // Create a CLI App with an --api-url flag so that we can specify which node
// the command should be executed against // the command should be executed against
app := cli.NewApp() app := cli.NewApp()
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "api", Name: "api-url",
Hidden: true, Hidden: true,
}, },
} }
@ -476,8 +475,8 @@ func (c *mockCLIClient) runCmd(cmd *cli.Command, input []string) string {
} }
func (c *mockCLIClient) runCmdRaw(cmd *cli.Command, input []string) (string, error) { func (c *mockCLIClient) runCmdRaw(cmd *cli.Command, input []string) (string, error) {
// prepend --api=<node api listener address> // prepend --api-url=<node api listener address>
apiFlag := "--api=" + c.addr.String() apiFlag := "--api-url=" + c.addr.String()
input = append([]string{apiFlag}, input...) input = append([]string{apiFlag}, input...)
fs := c.flagSet(cmd) fs := c.flagSet(cmd)
@ -493,7 +492,7 @@ func (c *mockCLIClient) runCmdRaw(cmd *cli.Command, input []string) (string, err
} }
func (c *mockCLIClient) flagSet(cmd *cli.Command) *flag.FlagSet { func (c *mockCLIClient) flagSet(cmd *cli.Command) *flag.FlagSet {
// Apply app level flags (so we can process --api flag) // Apply app level flags (so we can process --api-url flag)
fs := &flag.FlagSet{} fs := &flag.FlagSet{}
for _, f := range c.cctx.App.Flags { for _, f := range c.cctx.App.Flags {
err := f.Apply(fs) err := f.Apply(fs)

View File

@ -9,7 +9,6 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/node/repo"
manet "github.com/multiformats/go-multiaddr/net"
) )
var pprofCmd = &cli.Command{ var pprofCmd = &cli.Command{
@ -37,7 +36,7 @@ var PprofGoroutines = &cli.Command{
if err != nil { if err != nil {
return xerrors.Errorf("could not get API info: %w", err) return xerrors.Errorf("could not get API info: %w", err)
} }
_, addr, err := manet.DialArgs(ainfo.Addr) addr, err := ainfo.Host()
if err != nil { if err != nil {
return err return err
} }

View File

@ -1688,7 +1688,14 @@ func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, er
var stateCircSupplyCmd = &cli.Command{ var stateCircSupplyCmd = &cli.Command{
Name: "circulating-supply", Name: "circulating-supply",
Usage: "Get the current circulating supply of filecoin", Usage: "Get the exact current circulating supply of Filecoin",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "vm-supply",
Usage: "calculates the approximation of the circulating supply used internally by the VM (instead of the exact amount)",
Value: false,
},
},
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx) api, closer, err := GetFullNodeAPI(cctx)
if err != nil { if err != nil {
@ -1703,7 +1710,8 @@ var stateCircSupplyCmd = &cli.Command{
return err return err
} }
circ, err := api.StateCirculatingSupply(ctx, ts.Key()) if cctx.IsSet("vm-supply") {
circ, err := api.StateVMCirculatingSupplyInternal(ctx, ts.Key())
if err != nil { if err != nil {
return err return err
} }
@ -1713,6 +1721,15 @@ var stateCircSupplyCmd = &cli.Command{
fmt.Println("Vested: ", types.FIL(circ.FilVested)) fmt.Println("Vested: ", types.FIL(circ.FilVested))
fmt.Println("Burnt: ", types.FIL(circ.FilBurnt)) fmt.Println("Burnt: ", types.FIL(circ.FilBurnt))
fmt.Println("Locked: ", types.FIL(circ.FilLocked)) fmt.Println("Locked: ", types.FIL(circ.FilLocked))
} else {
circ, err := api.StateCirculatingSupply(ctx, ts.Key())
if err != nil {
return err
}
fmt.Println("Exact circulating supply: ", types.FIL(circ))
return nil
}
return nil return nil
}, },

View File

@ -124,6 +124,12 @@ var syncMarkBadCmd = &cli.Command{
var syncUnmarkBadCmd = &cli.Command{ var syncUnmarkBadCmd = &cli.Command{
Name: "unmark-bad", Name: "unmark-bad",
Usage: "Unmark the given block as bad, makes it possible to sync to a chain containing it", Usage: "Unmark the given block as bad, makes it possible to sync to a chain containing it",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "all",
Usage: "drop the entire bad block cache",
},
},
ArgsUsage: "[blockCid]", ArgsUsage: "[blockCid]",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
napi, closer, err := GetFullNodeAPI(cctx) napi, closer, err := GetFullNodeAPI(cctx)
@ -133,6 +139,10 @@ var syncUnmarkBadCmd = &cli.Command{
defer closer() defer closer()
ctx := ReqContext(cctx) ctx := ReqContext(cctx)
if cctx.Bool("all") {
return napi.SyncUnmarkAllBad(ctx)
}
if !cctx.Args().Present() { if !cctx.Args().Present() {
return fmt.Errorf("must specify block cid to unmark") return fmt.Errorf("must specify block cid to unmark")
} }
@ -233,7 +243,13 @@ func SyncWait(ctx context.Context, napi api.FullNode) error {
samples := 8 samples := 8
i := 0 i := 0
var app, lastApp uint64 var firstApp, app, lastApp uint64
state, err := napi.SyncState(ctx)
if err != nil {
return err
}
firstApp = state.VMApplied
for { for {
state, err := napi.SyncState(ctx) state, err := napi.SyncState(ctx)
@ -286,10 +302,10 @@ func SyncWait(ctx context.Context, napi api.FullNode) error {
if i%samples == 0 { if i%samples == 0 {
lastApp = app lastApp = app
app = state.VMApplied app = state.VMApplied - firstApp
} }
if i > 0 { if i > 0 {
fmt.Printf("Validated %d messages (%d per second)\n", state.VMApplied, (app-lastApp)*uint64(time.Second/tick)/uint64(samples)) fmt.Printf("Validated %d messages (%d per second)\n", state.VMApplied-firstApp, (app-lastApp)*uint64(time.Second/tick)/uint64(samples))
lastLines++ lastLines++
} }

83
cli/util/apiinfo.go Normal file
View File

@ -0,0 +1,83 @@
package cliutil
import (
"net/http"
"net/url"
"regexp"
"strings"
logging "github.com/ipfs/go-log/v2"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
var log = logging.Logger("cliutil")
var (
infoWithToken = regexp.MustCompile("^[a-zA-Z0-9\\-_]+?\\.[a-zA-Z0-9\\-_]+?\\.([a-zA-Z0-9\\-_]+)?:.+$")
)
type APIInfo struct {
Addr string
Token []byte
}
func ParseApiInfo(s string) APIInfo {
var tok []byte
if infoWithToken.Match([]byte(s)) {
sp := strings.SplitN(s, ":", 2)
tok = []byte(sp[0])
s = sp[1]
}
return APIInfo{
Addr: s,
Token: tok,
}
}
func (a APIInfo) DialArgs() (string, error) {
ma, err := multiaddr.NewMultiaddr(a.Addr)
if err == nil {
_, addr, err := manet.DialArgs(ma)
if err != nil {
return "", err
}
return "ws://" + addr + "/rpc/v0", nil
}
_, err = url.Parse(a.Addr)
if err != nil {
return "", err
}
return a.Addr + "/rpc/v0", nil
}
func (a APIInfo) Host() (string, error) {
ma, err := multiaddr.NewMultiaddr(a.Addr)
if err == nil {
_, addr, err := manet.DialArgs(ma)
if err != nil {
return "", err
}
return addr, nil
}
spec, err := url.Parse(a.Addr)
if err != nil {
return "", err
}
return spec.Host, nil
}
func (a APIInfo) AuthHeader() http.Header {
if len(a.Token) != 0 {
headers := http.Header{}
headers.Add("Authorization", "Bearer "+string(a.Token))
return headers
}
log.Warn("API Token not set and requested, capabilities might be limited.")
return nil
}

View File

@ -17,7 +17,6 @@ import (
"github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/crypto"
types "github.com/filecoin-project/lotus/chain/types" types "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/lib/tablewriter" "github.com/filecoin-project/lotus/lib/tablewriter"
) )
@ -55,7 +54,7 @@ var walletNew = &cli.Command{
t = "secp256k1" t = "secp256k1"
} }
nk, err := api.WalletNew(ctx, wallet.ActSigType(t)) nk, err := api.WalletNew(ctx, types.KeyType(t))
if err != nil { if err != nil {
return err return err
} }
@ -329,9 +328,9 @@ var walletImport = &cli.Command{
ki.PrivateKey = gk.PrivateKey ki.PrivateKey = gk.PrivateKey
switch gk.SigType { switch gk.SigType {
case 1: case 1:
ki.Type = wallet.KTSecp256k1 ki.Type = types.KTSecp256k1
case 2: case 2:
ki.Type = wallet.KTBLS ki.Type = types.KTBLS
default: default:
return fmt.Errorf("unrecognized key type: %d", gk.SigType) return fmt.Errorf("unrecognized key type: %d", gk.SigType)
} }

View File

@ -7,8 +7,6 @@ import (
"os" "os"
"time" "time"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
@ -61,7 +59,7 @@ var runCmd = &cli.Command{
func sendSmallFundsTxs(ctx context.Context, api api.FullNode, from address.Address, rate int) error { func sendSmallFundsTxs(ctx context.Context, api api.FullNode, from address.Address, rate int) error {
var sendSet []address.Address var sendSet []address.Address
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
naddr, err := api.WalletNew(ctx, crypto.SigTypeSecp256k1) naddr, err := api.WalletNew(ctx, types.KTSecp256k1)
if err != nil { if err != nil {
return err return err
} }

View File

@ -183,7 +183,7 @@ var importBenchCmd = &cli.Command{
return nil return nil
} }
cs := store.NewChainStore(bs, ds, vm.Syscalls(verifier)) cs := store.NewChainStore(bs, ds, vm.Syscalls(verifier), nil)
stm := stmgr.NewStateManager(cs) stm := stmgr.NewStateManager(cs)
if cctx.Bool("global-profile") { if cctx.Bool("global-profile") {

View File

@ -316,7 +316,7 @@ limit 1
} }
func (s *Syncer) storeCirculatingSupply(ctx context.Context, tipset *types.TipSet) error { func (s *Syncer) storeCirculatingSupply(ctx context.Context, tipset *types.TipSet) error {
supply, err := s.node.StateCirculatingSupply(ctx, tipset.Key()) supply, err := s.node.StateVMCirculatingSupplyInternal(ctx, tipset.Key())
if err != nil { if err != nil {
return err return err
} }

View File

@ -6,85 +6,174 @@ import (
"time" "time"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"go.opencensus.io/trace"
) )
const LookbackCap = time.Hour const (
LookbackCap = time.Hour
stateWaitLookbackLimit = abi.ChainEpoch(20)
)
var ( var (
ErrLookbackTooLong = fmt.Errorf("lookbacks of more than %s are disallowed", LookbackCap) ErrLookbackTooLong = fmt.Errorf("lookbacks of more than %s are disallowed", LookbackCap)
) )
type GatewayAPI struct { // gatewayDepsAPI defines the API methods that the GatewayAPI depends on
api api.FullNode // (to make it easy to mock for tests)
type gatewayDepsAPI interface {
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)
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)
MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
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)
} }
func (a *GatewayAPI) getTipsetTimestamp(ctx context.Context, tsk types.TipSetKey) (time.Time, error) { type GatewayAPI struct {
api gatewayDepsAPI
}
func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) error {
if tsk.IsEmpty() { if tsk.IsEmpty() {
return time.Now(), nil return nil
} }
ts, err := a.api.ChainGetTipSet(ctx, tsk) ts, err := a.api.ChainGetTipSet(ctx, tsk)
if err != nil {
return time.Time{}, err
}
return time.Unix(int64(ts.Blocks()[0].Timestamp), 0), nil
}
func (a *GatewayAPI) checkTipset(ctx context.Context, ts types.TipSetKey) error {
when, err := a.getTipsetTimestamp(ctx, ts)
if err != nil { if err != nil {
return err return err
} }
if time.Since(when) > time.Hour { return a.checkTipset(ts)
}
func (a *GatewayAPI) checkTipset(ts *types.TipSet) error {
at := time.Unix(int64(ts.Blocks()[0].Timestamp), 0)
if err := a.checkTimestamp(at); err != nil {
return fmt.Errorf("bad tipset: %w", err)
}
return nil
}
func (a *GatewayAPI) checkTipsetHeight(ts *types.TipSet, h abi.ChainEpoch) error {
tsBlock := ts.Blocks()[0]
heightDelta := time.Duration(uint64(tsBlock.Height-h)*build.BlockDelaySecs) * time.Second
timeAtHeight := time.Unix(int64(tsBlock.Timestamp), 0).Add(-heightDelta)
if err := a.checkTimestamp(timeAtHeight); err != nil {
return fmt.Errorf("bad tipset height: %w", err)
}
return nil
}
func (a *GatewayAPI) checkTimestamp(at time.Time) error {
if time.Since(at) > LookbackCap {
return ErrLookbackTooLong return ErrLookbackTooLong
} }
return nil return nil
} }
func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) {
ctx, span := trace.StartSpan(ctx, "StateGetActor")
defer span.End()
if err := a.checkTipset(ctx, ts); err != nil {
return nil, fmt.Errorf("bad tipset: %w", err)
}
return a.api.StateGetActor(ctx, actor, ts)
}
func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
ctx, span := trace.StartSpan(ctx, "ChainHead")
defer span.End()
// TODO: cache and invalidate cache when timestamp is up (or have internal ChainNotify) // TODO: cache and invalidate cache when timestamp is up (or have internal ChainNotify)
return a.api.ChainHead(ctx) return a.api.ChainHead(ctx)
} }
func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
ctx, span := trace.StartSpan(ctx, "ChainGetTipSet")
defer span.End()
if err := a.checkTipset(ctx, tsk); err != nil {
return nil, fmt.Errorf("bad tipset: %w", err)
}
// TODO: since we're limiting lookbacks, should just cache this (could really even cache the json response bytes)
return a.api.ChainGetTipSet(ctx, tsk) return a.api.ChainGetTipSet(ctx, tsk)
} }
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
}
// Check if the tipset key refers to a tipset that's too far in the past
if err := a.checkTipset(ts); err != nil {
return nil, err
}
// Check if the height is too far in the past
if err := a.checkTipsetHeight(ts, h); err != nil {
return nil, err
}
return a.api.ChainGetTipSetByHeight(ctx, h, tsk)
}
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
}
return a.api.GasEstimateMessageGas(ctx, msg, spec, tsk)
}
func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) { func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) {
ctx, span := trace.StartSpan(ctx, "MpoolPush")
defer span.End()
// TODO: additional anti-spam checks // TODO: additional anti-spam checks
return a.api.MpoolPushUntrusted(ctx, sm) return a.api.MpoolPushUntrusted(ctx, sm)
} }
func (a *GatewayAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) {
if err := a.checkTipsetKey(ctx, tsk); err != nil {
return types.NewInt(0), err
}
return a.api.MsigGetAvailableBalance(ctx, addr, tsk)
}
func (a *GatewayAPI) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) {
if err := a.checkTipsetKey(ctx, start); err != nil {
return types.NewInt(0), err
}
if err := a.checkTipsetKey(ctx, end); err != nil {
return types.NewInt(0), err
}
return a.api.MsigGetVested(ctx, addr, start, end)
}
func (a *GatewayAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
if err := a.checkTipsetKey(ctx, tsk); err != nil {
return address.Undef, err
}
return a.api.StateAccountKey(ctx, addr, tsk)
}
func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) {
if err := a.checkTipsetKey(ctx, tsk); err != nil {
return nil, err
}
return a.api.StateGetActor(ctx, actor, tsk)
}
func (a *GatewayAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
if err := a.checkTipsetKey(ctx, tsk); err != nil {
return address.Undef, err
}
return a.api.StateLookupID(ctx, addr, tsk)
}
func (a *GatewayAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) {
return a.api.StateWaitMsgLimited(ctx, msg, confidence, stateWaitLookbackLimit)
}
var _ api.GatewayAPI = (*GatewayAPI)(nil)
var _ full.ChainModuleAPI = (*GatewayAPI)(nil)
var _ full.GasModuleAPI = (*GatewayAPI)(nil)
var _ full.MpoolModuleAPI = (*GatewayAPI)(nil)
var _ full.StateModuleAPI = (*GatewayAPI)(nil)

View File

@ -0,0 +1,191 @@
package main
import (
"context"
"sync"
"testing"
"time"
"github.com/filecoin-project/lotus/build"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
)
func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) {
ctx := context.Background()
lookbackTimestamp := uint64(time.Now().Unix()) - uint64(LookbackCap.Seconds())
type args struct {
h abi.ChainEpoch
tskh abi.ChainEpoch
genesisTS uint64
}
tests := []struct {
name string
args args
expErr bool
}{{
name: "basic",
args: args{
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
},
}, {
name: "genesis",
args: args{
h: abi.ChainEpoch(0),
tskh: abi.ChainEpoch(5),
},
}, {
name: "same epoch as tipset",
args: args{
h: abi.ChainEpoch(5),
tskh: abi.ChainEpoch(5),
},
}, {
name: "tipset too old",
args: args{
// Tipset height is 5, genesis is at LookbackCap - 10 epochs.
// So resulting tipset height will be 5 epochs earlier than LookbackCap.
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
genesisTS: lookbackTimestamp - build.BlockDelaySecs*10,
},
expErr: true,
}, {
name: "lookup height too old",
args: args{
// Tipset height is 5, lookup height is 1, genesis is at LookbackCap - 3 epochs.
// So
// - lookup height will be 2 epochs earlier than LookbackCap.
// - tipset height will be 2 epochs later than LookbackCap.
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
genesisTS: lookbackTimestamp - build.BlockDelaySecs*3,
},
expErr: true,
}, {
name: "tipset and lookup height within acceptable range",
args: args{
// Tipset height is 5, lookup height is 1, genesis is at LookbackCap.
// So
// - lookup height will be 1 epoch later than LookbackCap.
// - tipset height will be 5 epochs later than LookbackCap.
h: abi.ChainEpoch(1),
tskh: abi.ChainEpoch(5),
genesisTS: lookbackTimestamp,
},
}}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
mock := &mockGatewayDepsAPI{}
a := &GatewayAPI{api: mock}
// Create tipsets from genesis up to tskh and return the highest
ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS)
got, err := a.ChainGetTipSetByHeight(ctx, tt.args.h, ts.Key())
if tt.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.args.h, got.Height())
}
})
}
}
type mockGatewayDepsAPI struct {
lk sync.RWMutex
tipsets []*types.TipSet
}
func (m *mockGatewayDepsAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
m.lk.RLock()
defer m.lk.RUnlock()
return m.tipsets[len(m.tipsets)-1], nil
}
func (m *mockGatewayDepsAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
m.lk.RLock()
defer m.lk.RUnlock()
for _, ts := range m.tipsets {
if ts.Key() == tsk {
return ts, nil
}
}
return nil, nil
}
// createTipSets creates tipsets from genesis up to tskh and returns the highest
func (m *mockGatewayDepsAPI) createTipSets(h abi.ChainEpoch, genesisTimestamp uint64) *types.TipSet {
m.lk.Lock()
defer m.lk.Unlock()
targeth := h + 1 // add one for genesis block
if genesisTimestamp == 0 {
genesisTimestamp = uint64(time.Now().Unix()) - build.BlockDelaySecs*uint64(targeth)
}
var currts *types.TipSet
for currh := abi.ChainEpoch(0); currh < targeth; currh++ {
blks := mock.MkBlock(currts, 1, 1)
if currh == 0 {
blks.Timestamp = genesisTimestamp
}
currts = mock.TipSet(blks)
m.tipsets = append(m.tipsets, currts)
}
return m.tipsets[len(m.tipsets)-1]
}
func (m *mockGatewayDepsAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
m.lk.Lock()
defer m.lk.Unlock()
return m.tipsets[h], nil
}
func (m *mockGatewayDepsAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, h abi.ChainEpoch) (*api.MsgLookup, error) {
panic("implement me")
}

View File

@ -0,0 +1,210 @@
package main
import (
"bytes"
"context"
"fmt"
"os"
"testing"
"time"
init0 "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/builtin/multisig"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/client"
"github.com/filecoin-project/lotus/api/test"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node"
builder "github.com/filecoin-project/lotus/node/test"
)
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) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
blocktime := 5 * time.Millisecond
ctx := context.Background()
full, lite, closer := startNodes(ctx, t, blocktime)
defer closer()
// The full node starts with a wallet
fullWalletAddr, err := full.WalletDefaultAddress(ctx)
require.NoError(t, err)
// Check the full node's wallet balance from the lite node
balance, err := lite.WalletBalance(ctx, fullWalletAddr)
require.NoError(t, err)
fmt.Println(balance)
// 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, t, 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))
require.NoError(t, err)
// Sign some data with the lite node wallet address
data := []byte("hello")
sig, err := lite.WalletSign(ctx, liteWalletAddr, data)
require.NoError(t, err)
// Verify the signature
ok, err := lite.WalletVerify(ctx, liteWalletAddr, data, sig)
require.NoError(t, err)
require.True(t, ok)
// Create some wallets on the lite node to use for testing multisig
var walletAddrs []address.Address
for i := 0; i < 4; i++ {
addr, err := lite.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err)
walletAddrs = append(walletAddrs, addr)
err = sendFunds(ctx, t, lite, liteWalletAddr, addr, types.NewInt(1e15))
require.NoError(t, err)
}
// Create an msig with three of the addresses and threshold of two sigs
msigAddrs := walletAddrs[:3]
amt := types.NewInt(1000)
addProposal, err := lite.MsigCreate(ctx, 2, msigAddrs, abi.ChainEpoch(50), amt, liteWalletAddr, types.NewInt(0))
require.NoError(t, err)
res, err := lite.StateWaitMsg(ctx, addProposal, 1)
require.NoError(t, err)
require.EqualValues(t, 0, res.Receipt.ExitCode)
var execReturn init0.ExecReturn
err = execReturn.UnmarshalCBOR(bytes.NewReader(res.Receipt.Return))
require.NoError(t, err)
// Get available balance of msig: should be greater than zero and less
// than initial amount
msig := execReturn.IDAddress
msigBalance, err := lite.MsigGetAvailableBalance(ctx, msig, types.EmptyTSK)
require.NoError(t, err)
require.Greater(t, msigBalance.Int64(), int64(0))
require.Less(t, msigBalance.Int64(), amt.Int64())
// Propose to add a new address to the msig
addProposal, err = lite.MsigAddPropose(ctx, msig, walletAddrs[0], walletAddrs[3], false)
require.NoError(t, err)
res, err = lite.StateWaitMsg(ctx, addProposal, 1)
require.NoError(t, err)
require.EqualValues(t, 0, res.Receipt.ExitCode)
var proposeReturn multisig.ProposeReturn
err = proposeReturn.UnmarshalCBOR(bytes.NewReader(res.Receipt.Return))
require.NoError(t, err)
// Approve proposal (proposer is first (implicit) signer, approver is
// second signer
txnID := uint64(proposeReturn.TxnID)
approval1, err := lite.MsigAddApprove(ctx, msig, walletAddrs[1], txnID, walletAddrs[0], walletAddrs[3], false)
require.NoError(t, err)
res, err = lite.StateWaitMsg(ctx, approval1, 1)
require.NoError(t, err)
require.EqualValues(t, 0, res.Receipt.ExitCode)
var approveReturn multisig.ApproveReturn
err = approveReturn.UnmarshalCBOR(bytes.NewReader(res.Receipt.Return))
require.NoError(t, err)
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 {
msg := &types.Message{
From: fromAddr,
To: toAddr,
Value: amt,
}
sm, err := fromNode.MpoolPushMessage(ctx, msg, nil)
if err != nil {
return err
}
res, err := fromNode.StateWaitMsg(ctx, sm.Cid(), 1)
if err != nil {
return err
}
if res.Receipt.ExitCode != 0 {
return xerrors.Errorf("send funds failed with exit code %d", res.Receipt.ExitCode)
}
return nil
}
func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (test.TestNode, test.TestNode, jsonrpc.ClientCloser) {
var closer jsonrpc.ClientCloser
// Create one miner and two full nodes.
// - Put a gateway server in front of full node 1
// - Start full node 2 in lite mode
// - Connect lite node -> gateway server -> full node
opts := append(
// Full node
test.OneFull,
// Lite node
test.FullNodeOpts{
Lite: true,
Opts: func(nodes []test.TestNode) node.Option {
fullNode := nodes[0]
// Create a gateway server in front of the full node
_, addr, err := builder.CreateRPCServer(&GatewayAPI{api: fullNode})
require.NoError(t, err)
// Create a gateway client API that connects to the gateway server
var gapi api.GatewayAPI
gapi, closer, err = client.NewGatewayRPC(ctx, addr, nil)
require.NoError(t, err)
// Provide the gateway API to dependency injection
return node.Override(new(api.GatewayAPI), gapi)
},
},
)
n, sn := builder.RPCMockSbBuilder(t, opts, test.OneMiner)
full := n[0]
lite := n[1]
miner := sn[0]
// Get the listener address for the full node
fullAddr, err := full.NetAddrsListen(ctx)
require.NoError(t, err)
// Connect the miner and the full node
err = miner.NetConnect(ctx, fullAddr)
require.NoError(t, err)
// Start mining blocks
bm := test.NewBlockMiner(ctx, t, miner, blocktime)
bm.MineBlocks()
return full, lite, closer
}

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/chain/wallet"
_ "github.com/filecoin-project/lotus/lib/sigs/bls" _ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp" _ "github.com/filecoin-project/lotus/lib/sigs/secp"
@ -30,22 +30,22 @@ func main() {
return err return err
} }
var kt crypto.SigType var kt types.KeyType
switch cctx.String("type") { switch cctx.String("type") {
case "bls": case "bls":
kt = crypto.SigTypeBLS kt = types.KTBLS
case "secp256k1": case "secp256k1":
kt = crypto.SigTypeSecp256k1 kt = types.KTSecp256k1
default: default:
return fmt.Errorf("unrecognized key type: %q", cctx.String("type")) return fmt.Errorf("unrecognized key type: %q", cctx.String("type"))
} }
kaddr, err := w.GenerateKey(kt) kaddr, err := w.WalletNew(cctx.Context, kt)
if err != nil { if err != nil {
return err return err
} }
ki, err := w.Export(kaddr) ki, err := w.WalletExport(cctx.Context, kaddr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -396,13 +396,13 @@ var runCmd = &cli.Command{
Name: "pre-fee-cap-max", Name: "pre-fee-cap-max",
EnvVars: []string{"LOTUS_PCR_PRE_FEE_CAP_MAX"}, EnvVars: []string{"LOTUS_PCR_PRE_FEE_CAP_MAX"},
Usage: "messages with a fee cap larger than this will be skipped when processing pre commit messages", Usage: "messages with a fee cap larger than this will be skipped when processing pre commit messages",
Value: "0.0000000001", Value: "0.000000001",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "prove-fee-cap-max", Name: "prove-fee-cap-max",
EnvVars: []string{"LOTUS_PCR_PROVE_FEE_CAP_MAX"}, EnvVars: []string{"LOTUS_PCR_PROVE_FEE_CAP_MAX"},
Usage: "messages with a prove cap larger than this will be skipped when processing pre commit messages", Usage: "messages with a prove cap larger than this will be skipped when processing pre commit messages",
Value: "0.0000000001", Value: "0.000000001",
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
@ -884,6 +884,8 @@ func (r *refunder) processTipsetStorageMarketActor(ctx context.Context, tipset *
} }
refundValue = types.BigMul(types.NewInt(uint64(recp.GasUsed)), tipset.Blocks()[0].ParentBaseFee) refundValue = types.BigMul(types.NewInt(uint64(recp.GasUsed)), tipset.Blocks()[0].ParentBaseFee)
default:
return false, messageMethod, types.NewInt(0), nil
} }
return true, messageMethod, refundValue, nil return true, messageMethod, refundValue, nil

View File

@ -21,7 +21,6 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/extern/sector-storage/zerocomm" "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
"github.com/filecoin-project/specs-actors/actors/builtin/market" "github.com/filecoin-project/specs-actors/actors/builtin/market"
@ -94,7 +93,7 @@ func PreSeal(maddr address.Address, spt abi.RegisteredSealProof, offset abi.Sect
return nil, nil, err return nil, nil, err
} }
} else { } else {
minerAddr, err = wallet.GenerateKey(crypto.SigTypeBLS) minerAddr, err = wallet.GenerateKey(types.KTBLS)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
"github.com/filecoin-project/lotus/chain/actors/builtin/power" "github.com/filecoin-project/lotus/chain/actors/builtin/power"
"github.com/filecoin-project/lotus/chain/actors/builtin/reward" "github.com/filecoin-project/lotus/chain/actors/builtin/reward"
@ -43,6 +44,9 @@ type accountInfo struct {
PreCommits types.FIL PreCommits types.FIL
LockedFunds types.FIL LockedFunds types.FIL
Sectors uint64 Sectors uint64
VestingStart abi.ChainEpoch
VestingDuration abi.ChainEpoch
VestingAmount types.FIL
} }
var auditsCmd = &cli.Command{ var auditsCmd = &cli.Command{
@ -115,10 +119,8 @@ var chainBalanceCmd = &cli.Command{
infos = append(infos, ai) infos = append(infos, ai)
} }
fmt.Printf("Address,Balance,Type,Power,Worker,Owner\n") printAccountInfos(infos, false)
for _, acc := range infos {
fmt.Printf("%s,%s,%s,%s,%s,%s\n", acc.Address, acc.Balance, acc.Type, acc.Power, acc.Worker, acc.Owner)
}
return nil return nil
}, },
} }
@ -171,7 +173,7 @@ var chainBalanceStateCmd = &cli.Command{
bs := blockstore.NewBlockstore(ds) bs := blockstore.NewBlockstore(ds)
cs := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier)) cs := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil)
cst := cbor.NewCborStore(bs) cst := cbor.NewCborStore(bs)
store := adt.WrapStore(ctx, cst) store := adt.WrapStore(ctx, cst)
@ -196,6 +198,7 @@ var chainBalanceStateCmd = &cli.Command{
LockedFunds: types.FIL(big.NewInt(0)), LockedFunds: types.FIL(big.NewInt(0)),
InitialPledge: types.FIL(big.NewInt(0)), InitialPledge: types.FIL(big.NewInt(0)),
PreCommits: types.FIL(big.NewInt(0)), PreCommits: types.FIL(big.NewInt(0)),
VestingAmount: types.FIL(big.NewInt(0)),
} }
if minerInfo && builtin.IsStorageMinerActor(act.Code) { if minerInfo && builtin.IsStorageMinerActor(act.Code) {
@ -234,6 +237,32 @@ var chainBalanceStateCmd = &cli.Command{
ai.Worker = minfo.Worker ai.Worker = minfo.Worker
ai.Owner = minfo.Owner ai.Owner = minfo.Owner
} }
if builtin.IsMultisigActor(act.Code) {
mst, err := multisig.Load(store, act)
if err != nil {
return err
}
ai.VestingStart, err = mst.StartEpoch()
if err != nil {
return err
}
ib, err := mst.InitialBalance()
if err != nil {
return err
}
ai.VestingAmount = types.FIL(ib)
ai.VestingDuration, err = mst.UnlockDuration()
if err != nil {
return err
}
}
infos = append(infos, ai) infos = append(infos, ai)
return nil return nil
}) })
@ -241,20 +270,25 @@ var chainBalanceStateCmd = &cli.Command{
return xerrors.Errorf("failed to loop over actors: %w", err) return xerrors.Errorf("failed to loop over actors: %w", err)
} }
printAccountInfos(infos, minerInfo)
return nil
},
}
func printAccountInfos(infos []accountInfo, minerInfo bool) {
if minerInfo { if minerInfo {
fmt.Printf("Address,Balance,Type,Sectors,Worker,Owner,InitialPledge,Locked,PreCommits\n") fmt.Printf("Address,Balance,Type,Sectors,Worker,Owner,InitialPledge,Locked,PreCommits,VestingStart,VestingDuration,VestingAmount\n")
for _, acc := range infos { for _, acc := range infos {
fmt.Printf("%s,%s,%s,%d,%s,%s,%s,%s,%s\n", acc.Address, acc.Balance, acc.Type, acc.Sectors, acc.Worker, acc.Owner, acc.InitialPledge, acc.LockedFunds, acc.PreCommits) fmt.Printf("%s,%s,%s,%d,%s,%s,%s,%s,%s,%d,%d,%s\n", acc.Address, acc.Balance.Unitless(), acc.Type, acc.Sectors, acc.Worker, acc.Owner, acc.InitialPledge.Unitless(), acc.LockedFunds.Unitless(), acc.PreCommits.Unitless(), acc.VestingStart, acc.VestingDuration, acc.VestingAmount.Unitless())
} }
} else { } else {
fmt.Printf("Address,Balance,Type\n") fmt.Printf("Address,Balance,Type\n")
for _, acc := range infos { for _, acc := range infos {
fmt.Printf("%s,%s,%s\n", acc.Address, acc.Balance, acc.Type) fmt.Printf("%s,%s,%s\n", acc.Address, acc.Balance.Unitless(), acc.Type)
} }
} }
return nil
},
} }
var chainPledgeCmd = &cli.Command{ var chainPledgeCmd = &cli.Command{
@ -309,7 +343,7 @@ var chainPledgeCmd = &cli.Command{
bs := blockstore.NewBlockstore(ds) bs := blockstore.NewBlockstore(ds)
cs := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier)) cs := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil)
cst := cbor.NewCborStore(bs) cst := cbor.NewCborStore(bs)
store := adt.WrapStore(ctx, cst) store := adt.WrapStore(ctx, cst)
@ -338,7 +372,7 @@ var chainPledgeCmd = &cli.Command{
pledgeCollateral = c pledgeCollateral = c
} }
circ, err := sm.GetCirculatingSupplyDetailed(ctx, abi.ChainEpoch(epoch), state) circ, err := sm.GetVMCirculatingSupplyDetailed(ctx, abi.ChainEpoch(epoch), state)
if err != nil { if err != nil {
return err return err
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli" lcli "github.com/filecoin-project/lotus/cli"
cliutil "github.com/filecoin-project/lotus/cli/util"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
"github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multiaddr"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -111,7 +112,7 @@ var consensusCheckCmd = &cli.Command{
if err != nil { if err != nil {
return err return err
} }
ainfo := lcli.APIInfo{Addr: apima} ainfo := cliutil.APIInfo{Addr: apima.String()}
addr, err := ainfo.DialArgs() addr, err := ainfo.DialArgs()
if err != nil { if err != nil {
return err return err

View File

@ -83,7 +83,7 @@ var exportChainCmd = &cli.Command{
bs := blockstore.NewBlockstore(ds) bs := blockstore.NewBlockstore(ds)
cs := store.NewChainStore(bs, mds, nil) cs := store.NewChainStore(bs, mds, nil, nil)
if err := cs.Load(); err != nil { if err := cs.Load(); err != nil {
return err return err
} }

View File

@ -52,7 +52,7 @@ var genesisVerifyCmd = &cli.Command{
} }
bs := blockstore.NewBlockstore(datastore.NewMapDatastore()) bs := blockstore.NewBlockstore(datastore.NewMapDatastore())
cs := store.NewChainStore(bs, datastore.NewMapDatastore(), nil) cs := store.NewChainStore(bs, datastore.NewMapDatastore(), nil, nil)
cf := cctx.Args().Get(0) cf := cctx.Args().Get(0)
f, err := os.Open(cf) f, err := os.Open(cf)

View File

@ -32,10 +32,10 @@ import (
_ "github.com/filecoin-project/lotus/lib/sigs/secp" _ "github.com/filecoin-project/lotus/lib/sigs/secp"
) )
var validTypes = []string{wallet.KTBLS, wallet.KTSecp256k1, lp2p.KTLibp2pHost} var validTypes = []types.KeyType{types.KTBLS, types.KTSecp256k1, lp2p.KTLibp2pHost}
type keyInfoOutput struct { type keyInfoOutput struct {
Type string Type types.KeyType
Address string Address string
PublicKey string PublicKey string
} }
@ -86,7 +86,7 @@ var keyinfoVerifyCmd = &cli.Command{
return xerrors.Errorf("decoding key: '%s': %w", fileName, err) return xerrors.Errorf("decoding key: '%s': %w", fileName, err)
} }
if string(name) != keyInfo.Type { if types.KeyType(name) != keyInfo.Type {
return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type) return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type)
} }
case modules.KTJwtHmacSecret: case modules.KTJwtHmacSecret:
@ -98,14 +98,14 @@ var keyinfoVerifyCmd = &cli.Command{
if string(name) != modules.JWTSecretName { if string(name) != modules.JWTSecretName {
return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type) return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type)
} }
case wallet.KTSecp256k1, wallet.KTBLS: case types.KTSecp256k1, types.KTBLS:
keystore := wallet.NewMemKeyStore() keystore := wallet.NewMemKeyStore()
w, err := wallet.NewWallet(keystore) w, err := wallet.NewWallet(keystore)
if err != nil { if err != nil {
return err return err
} }
if _, err := w.Import(&keyInfo); err != nil { if _, err := w.WalletImport(cctx.Context, &keyInfo); err != nil {
return err return err
} }
@ -214,13 +214,13 @@ var keyinfoImportCmd = &cli.Command{
fmt.Printf("%s\n", peerid.String()) fmt.Printf("%s\n", peerid.String())
break break
case wallet.KTSecp256k1, wallet.KTBLS: case types.KTSecp256k1, types.KTBLS:
w, err := wallet.NewWallet(keystore) w, err := wallet.NewWallet(keystore)
if err != nil { if err != nil {
return err return err
} }
addr, err := w.Import(&keyInfo) addr, err := w.WalletImport(cctx.Context, &keyInfo)
if err != nil { if err != nil {
return err return err
} }
@ -317,7 +317,7 @@ var keyinfoInfoCmd = &cli.Command{
kio.PublicKey = base64.StdEncoding.EncodeToString(pkBytes) kio.PublicKey = base64.StdEncoding.EncodeToString(pkBytes)
break break
case wallet.KTSecp256k1, wallet.KTBLS: case types.KTSecp256k1, types.KTBLS:
kio.Type = keyInfo.Type kio.Type = keyInfo.Type
key, err := wallet.NewKey(keyInfo) key, err := wallet.NewKey(keyInfo)
@ -366,7 +366,7 @@ var keyinfoNewCmd = &cli.Command{
return fmt.Errorf("please specify a type to generate") return fmt.Errorf("please specify a type to generate")
} }
keyType := cctx.Args().First() keyType := types.KeyType(cctx.Args().First())
flagOutput := cctx.String("output") flagOutput := cctx.String("output")
if i := SliceIndex(len(validTypes), func(i int) bool { if i := SliceIndex(len(validTypes), func(i int) bool {
@ -404,8 +404,8 @@ var keyinfoNewCmd = &cli.Command{
keyInfo = ki keyInfo = ki
break break
case wallet.KTSecp256k1, wallet.KTBLS: case types.KTSecp256k1, types.KTBLS:
key, err := wallet.GenerateKey(wallet.ActSigType(keyType)) key, err := wallet.GenerateKey(keyType)
if err != nil { if err != nil {
return err return err
} }
@ -418,7 +418,7 @@ var keyinfoNewCmd = &cli.Command{
filename := flagOutput filename := flagOutput
filename = strings.ReplaceAll(filename, "<addr>", keyAddr) filename = strings.ReplaceAll(filename, "<addr>", keyAddr)
filename = strings.ReplaceAll(filename, "<type>", keyType) filename = strings.ReplaceAll(filename, "<type>", string(keyType))
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil { if err != nil {

248
cmd/lotus-shed/ledger.go Normal file
View File

@ -0,0 +1,248 @@
package main
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/filecoin-project/go-address"
"github.com/urfave/cli/v2"
ledgerfil "github.com/whyrusleeping/ledger-filecoin-go"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
ledgerwallet "github.com/filecoin-project/lotus/chain/wallet/ledger"
lcli "github.com/filecoin-project/lotus/cli"
)
var ledgerCmd = &cli.Command{
Name: "ledger",
Usage: "Ledger interactions",
Flags: []cli.Flag{},
Subcommands: []*cli.Command{
ledgerListAddressesCmd,
ledgerKeyInfoCmd,
ledgerSignTestCmd,
},
}
const hdHard = 0x80000000
var ledgerListAddressesCmd = &cli.Command{
Name: "list",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "print-balances",
Usage: "print balances",
Aliases: []string{"b"},
},
},
Action: func(cctx *cli.Context) error {
var api api.FullNode
if cctx.Bool("print-balances") {
a, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
api = a
defer closer()
}
ctx := lcli.ReqContext(cctx)
fl, err := ledgerfil.FindLedgerFilecoinApp()
if err != nil {
return err
}
end := 20
for i := 0; i < end; i++ {
if err := ctx.Err(); err != nil {
return err
}
p := []uint32{hdHard | 44, hdHard | 461, hdHard, 0, uint32(i)}
pubk, err := fl.GetPublicKeySECP256K1(p)
if err != nil {
return err
}
addr, err := address.NewSecp256k1Address(pubk)
if err != nil {
return err
}
if cctx.Bool("print-balances") && api != nil { // api check makes linter happier
b, err := api.WalletBalance(ctx, addr)
if err != nil {
return xerrors.Errorf("getting balance: %w", err)
}
if !b.IsZero() {
end = i + 21 // BIP32 spec, stop after 20 empty addresses
}
fmt.Printf("%s %s %s\n", addr, printHDPath(p), types.FIL(b))
} else {
fmt.Printf("%s %s\n", addr, printHDPath(p))
}
}
return nil
},
}
func parseHDPath(s string) ([]uint32, error) {
parts := strings.Split(s, "/")
if parts[0] != "m" {
return nil, fmt.Errorf("expected HD path to start with 'm'")
}
var out []uint32
for _, p := range parts[1:] {
var hard bool
if strings.HasSuffix(p, "'") {
p = p[:len(p)-1]
hard = true
}
v, err := strconv.ParseUint(p, 10, 32)
if err != nil {
return nil, err
}
if v >= hdHard {
return nil, fmt.Errorf("path element %s too large", p)
}
if hard {
v += hdHard
}
out = append(out, uint32(v))
}
return out, nil
}
func printHDPath(pth []uint32) string {
s := "m"
for _, p := range pth {
s += "/"
hard := p&hdHard != 0
p &^= hdHard // remove hdHard bit
s += fmt.Sprint(p)
if hard {
s += "'"
}
}
return s
}
var ledgerKeyInfoCmd = &cli.Command{
Name: "key-info",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
},
},
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return cli.ShowCommandHelp(cctx, cctx.Command.Name)
}
fl, err := ledgerfil.FindLedgerFilecoinApp()
if err != nil {
return err
}
p, err := parseHDPath(cctx.Args().First())
if err != nil {
return err
}
pubk, _, addr, err := fl.GetAddressPubKeySECP256K1(p)
if err != nil {
return err
}
if cctx.Bool("verbose") {
fmt.Println(addr)
fmt.Println(pubk)
}
a, err := address.NewFromString(addr)
if err != nil {
return err
}
var pd ledgerwallet.LedgerKeyInfo
pd.Address = a
pd.Path = p
b, err := json.Marshal(pd)
if err != nil {
return err
}
var ki types.KeyInfo
ki.Type = types.KTSecp256k1Ledger
ki.PrivateKey = b
out, err := json.Marshal(ki)
if err != nil {
return err
}
fmt.Println(string(out))
return nil
},
}
var ledgerSignTestCmd = &cli.Command{
Name: "sign",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return cli.ShowCommandHelp(cctx, cctx.Command.Name)
}
fl, err := ledgerfil.FindLedgerFilecoinApp()
if err != nil {
return err
}
p, err := parseHDPath(cctx.Args().First())
if err != nil {
return err
}
addr, err := address.NewFromString("f1xc3hws5n6y5m3m44gzb3gyjzhups6wzmhe663ji")
if err != nil {
return err
}
m := &types.Message{
To: addr,
From: addr,
}
b, err := m.ToStorageBlock()
if err != nil {
return err
}
sig, err := fl.SignSECP256K1(p, b.RawData())
if err != nil {
return err
}
fmt.Println(sig.SignatureBytes())
return nil
},
}

View File

@ -41,6 +41,7 @@ func main() {
syncCmd, syncCmd,
stateTreePruneCmd, stateTreePruneCmd,
datastoreCmd, datastoreCmd,
ledgerCmd,
} }
app := &cli.App{ app := &cli.App{

View File

@ -162,7 +162,7 @@ var stateTreePruneCmd = &cli.Command{
bs := blockstore.NewBlockstore(ds) bs := blockstore.NewBlockstore(ds)
cs := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier)) cs := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil)
if err := cs.Load(); err != nil { if err := cs.Load(); err != nil {
return fmt.Errorf("loading chainstore: %w", err) return fmt.Errorf("loading chainstore: %w", err)
} }

Some files were not shown because too many files have changed in this diff Show More