Merge pull request #4421 from filecoin-project/fix/lite-msig-inspect

Ensure msig inspect cli works with lotus-lite
This commit is contained in:
Łukasz Magiera 2020-10-15 15:11:34 +02:00 committed by GitHub
commit c56ef260d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 396 additions and 32 deletions

View File

@ -10,9 +10,11 @@ import (
) )
type GatewayAPI interface { type GatewayAPI interface {
ChainHasObj(context.Context, cid.Cid) (bool, error)
ChainHead(ctx context.Context) (*types.TipSet, error) ChainHead(ctx context.Context) (*types.TipSet, error)
ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*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) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) 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) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error)
MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)

View File

@ -371,9 +371,11 @@ type WorkerStruct struct {
type GatewayStruct struct { type GatewayStruct struct {
Internal struct { Internal struct {
// TODO: does the gateway need perms? // TODO: does the gateway need perms?
ChainHasObj func(context.Context, cid.Cid) (bool, error)
ChainGetTipSet func(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) 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) ChainGetTipSetByHeight func(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
ChainHead func(ctx context.Context) (*types.TipSet, error) ChainHead func(ctx context.Context) (*types.TipSet, error)
ChainReadObj func(context.Context, cid.Cid) ([]byte, error)
GasEstimateMessageGas func(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) 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) 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) MsigGetAvailableBalance func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)
@ -1432,6 +1434,10 @@ func (w *WorkerStruct) Closing(ctx context.Context) (<-chan struct{}, error) {
return w.Internal.Closing(ctx) return w.Internal.Closing(ctx)
} }
func (g GatewayStruct) ChainHasObj(ctx context.Context, c cid.Cid) (bool, error) {
return g.Internal.ChainHasObj(ctx, c)
}
func (g GatewayStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { func (g GatewayStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {
return g.Internal.ChainHead(ctx) return g.Internal.ChainHead(ctx)
} }
@ -1444,6 +1450,10 @@ func (g GatewayStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEp
return g.Internal.ChainGetTipSetByHeight(ctx, h, tsk) return g.Internal.ChainGetTipSetByHeight(ctx, h, tsk)
} }
func (g GatewayStruct) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
return g.Internal.ChainReadObj(ctx, c)
}
func (g GatewayStruct) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { 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) return g.Internal.GasEstimateMessageGas(ctx, msg, spec, tsk)
} }

View File

@ -5,7 +5,6 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"reflect" "reflect"
"sort" "sort"
"strconv" "strconv"
@ -162,7 +161,7 @@ var msigCreateCmd = &cli.Command{
// check it executed successfully // check it executed successfully
if wait.Receipt.ExitCode != 0 { if wait.Receipt.ExitCode != 0 {
fmt.Println("actor creation failed!") fmt.Fprintln(cctx.App.Writer, "actor creation failed!")
return err return err
} }
@ -172,7 +171,7 @@ var msigCreateCmd = &cli.Command{
if err := execreturn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil { if err := execreturn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil {
return err return err
} }
fmt.Println("Created new multisig: ", execreturn.IDAddress, execreturn.RobustAddress) fmt.Fprintln(cctx.App.Writer, "Created new multisig: ", execreturn.IDAddress, execreturn.RobustAddress)
// TODO: maybe register this somewhere // TODO: maybe register this somewhere
return nil return nil
@ -236,25 +235,25 @@ var msigInspectCmd = &cli.Command{
return err return err
} }
fmt.Printf("Balance: %s\n", types.FIL(act.Balance)) fmt.Fprintf(cctx.App.Writer, "Balance: %s\n", types.FIL(act.Balance))
fmt.Printf("Spendable: %s\n", types.FIL(types.BigSub(act.Balance, locked))) fmt.Fprintf(cctx.App.Writer, "Spendable: %s\n", types.FIL(types.BigSub(act.Balance, locked)))
if cctx.Bool("vesting") { if cctx.Bool("vesting") {
ib, err := mstate.InitialBalance() ib, err := mstate.InitialBalance()
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("InitialBalance: %s\n", types.FIL(ib)) fmt.Fprintf(cctx.App.Writer, "InitialBalance: %s\n", types.FIL(ib))
se, err := mstate.StartEpoch() se, err := mstate.StartEpoch()
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("StartEpoch: %d\n", se) fmt.Fprintf(cctx.App.Writer, "StartEpoch: %d\n", se)
ud, err := mstate.UnlockDuration() ud, err := mstate.UnlockDuration()
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("UnlockDuration: %d\n", ud) fmt.Fprintf(cctx.App.Writer, "UnlockDuration: %d\n", ud)
} }
signers, err := mstate.Signers() signers, err := mstate.Signers()
@ -265,10 +264,10 @@ var msigInspectCmd = &cli.Command{
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("Threshold: %d / %d\n", threshold, len(signers)) fmt.Fprintf(cctx.App.Writer, "Threshold: %d / %d\n", threshold, len(signers))
fmt.Println("Signers:") fmt.Fprintln(cctx.App.Writer, "Signers:")
for _, s := range signers { for _, s := range signers {
fmt.Printf("\t%s\n", s) fmt.Fprintf(cctx.App.Writer, "\t%s\n", s)
} }
pending := make(map[int64]multisig.Transaction) pending := make(map[int64]multisig.Transaction)
@ -280,7 +279,7 @@ var msigInspectCmd = &cli.Command{
} }
decParams := cctx.Bool("decode-params") decParams := cctx.Bool("decode-params")
fmt.Println("Transactions: ", len(pending)) fmt.Fprintln(cctx.App.Writer, "Transactions: ", len(pending))
if len(pending) > 0 { if len(pending) > 0 {
var txids []int64 var txids []int64
for txid := range pending { for txid := range pending {
@ -290,7 +289,7 @@ var msigInspectCmd = &cli.Command{
return txids[i] < txids[j] return txids[i] < txids[j]
}) })
w := tabwriter.NewWriter(os.Stdout, 8, 4, 2, ' ', 0) w := tabwriter.NewWriter(cctx.App.Writer, 8, 4, 2, ' ', 0)
fmt.Fprintf(w, "ID\tState\tApprovals\tTo\tValue\tMethod\tParams\n") 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]
@ -699,7 +698,7 @@ var msigAddProposeCmd = &cli.Command{
return err return err
} }
fmt.Println("sent add proposal in message: ", msgCid) fmt.Fprintln(cctx.App.Writer, "sent add proposal in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")))
if err != nil { if err != nil {

55
cli/multisig_test.go Normal file
View File

@ -0,0 +1,55 @@
package cli
import (
"context"
"os"
"testing"
"time"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api/test"
clitest "github.com/filecoin-project/lotus/cli/test"
builder "github.com/filecoin-project/lotus/node/test"
)
// TestMultisig does a basic test to exercise the multisig CLI
// commands
func TestMultisig(t *testing.T) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
blocktime := 5 * time.Millisecond
ctx := context.Background()
nodes, _ := startNodes(ctx, t, blocktime)
clientNode := nodes[0]
clitest.RunMultisigTest(t, Commands, clientNode)
}
func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) {
n, sn := builder.RPCMockSbBuilder(t, test.OneFull, test.OneMiner)
full := n[0]
miner := sn[0]
// Get everyone connected
addrs, err := full.NetAddrsListen(ctx)
if err != nil {
t.Fatal(err)
}
if err := miner.NetConnect(ctx, addrs); err != nil {
t.Fatal(err)
}
// Start mining blocks
bm := test.NewBlockMiner(ctx, t, miner, blocktime)
bm.MineBlocks()
// Get the creator's address
creatorAddr, err := full.WalletDefaultAddress(ctx)
if err != nil {
t.Fatal(err)
}
// Create mock CLI
return n, []address.Address{creatorAddr}
}

View File

@ -437,6 +437,7 @@ type mockCLI struct {
out *bytes.Buffer out *bytes.Buffer
} }
// TODO: refactor to use the methods in cli/test/mockcli.go
func newMockCLI(t *testing.T) *mockCLI { func newMockCLI(t *testing.T) *mockCLI {
// Create a CLI App with an --api-url 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

124
cli/test/mockcli.go Normal file
View File

@ -0,0 +1,124 @@
package test
import (
"bytes"
"flag"
"strings"
"testing"
"github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/require"
lcli "github.com/urfave/cli/v2"
)
type mockCLI struct {
t *testing.T
cmds []*lcli.Command
cctx *lcli.Context
out *bytes.Buffer
}
func newMockCLI(t *testing.T, cmds []*lcli.Command) *mockCLI {
// Create a CLI App with an --api-url flag so that we can specify which node
// the command should be executed against
app := &lcli.App{
Flags: []lcli.Flag{
&lcli.StringFlag{
Name: "api-url",
Hidden: true,
},
},
Commands: cmds,
}
var out bytes.Buffer
app.Writer = &out
app.Setup()
cctx := lcli.NewContext(app, &flag.FlagSet{}, nil)
return &mockCLI{t: t, cmds: cmds, cctx: cctx, out: &out}
}
func (c *mockCLI) client(addr multiaddr.Multiaddr) *mockCLIClient {
return &mockCLIClient{t: c.t, cmds: c.cmds, addr: addr, cctx: c.cctx, out: c.out}
}
// mockCLIClient runs commands against a particular node
type mockCLIClient struct {
t *testing.T
cmds []*lcli.Command
addr multiaddr.Multiaddr
cctx *lcli.Context
out *bytes.Buffer
}
func (c *mockCLIClient) run(cmd []string, params []string, args []string) string {
// Add parameter --api-url=<node api listener address>
apiFlag := "--api-url=" + c.addr.String()
params = append([]string{apiFlag}, params...)
err := c.cctx.App.Run(append(append(cmd, params...), args...))
require.NoError(c.t, err)
// Get the output
str := strings.TrimSpace(c.out.String())
c.out.Reset()
return str
}
func (c *mockCLIClient) runCmd(input []string) string {
cmd := c.cmdByNameSub(input[0], input[1])
out, err := c.runCmdRaw(cmd, input[2:])
require.NoError(c.t, err)
return out
}
func (c *mockCLIClient) cmdByNameSub(name string, sub string) *lcli.Command {
for _, c := range c.cmds {
if c.Name == name {
for _, s := range c.Subcommands {
if s.Name == sub {
return s
}
}
}
}
return nil
}
func (c *mockCLIClient) runCmdRaw(cmd *lcli.Command, input []string) (string, error) {
// prepend --api-url=<node api listener address>
apiFlag := "--api-url=" + c.addr.String()
input = append([]string{apiFlag}, input...)
fs := c.flagSet(cmd)
err := fs.Parse(input)
require.NoError(c.t, err)
err = cmd.Action(lcli.NewContext(c.cctx.App, fs, c.cctx))
// Get the output
str := strings.TrimSpace(c.out.String())
c.out.Reset()
return str, err
}
func (c *mockCLIClient) flagSet(cmd *lcli.Command) *flag.FlagSet {
// Apply app level flags (so we can process --api-url flag)
fs := &flag.FlagSet{}
for _, f := range c.cctx.App.Flags {
err := f.Apply(fs)
if err != nil {
c.t.Fatal(err)
}
}
// Apply command level flags
for _, f := range cmd.Flags {
err := f.Apply(fs)
if err != nil {
c.t.Fatal(err)
}
}
return fs
}

110
cli/test/multisig.go Normal file
View File

@ -0,0 +1,110 @@
package test
import (
"context"
"fmt"
"regexp"
"strings"
"testing"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api/test"
"github.com/filecoin-project/lotus/chain/types"
logging "github.com/ipfs/go-log/v2"
"github.com/stretchr/testify/require"
lcli "github.com/urfave/cli/v2"
)
func QuietMiningLogs() {
logging.SetLogLevel("miner", "ERROR")
logging.SetLogLevel("chainstore", "ERROR")
logging.SetLogLevel("chain", "ERROR")
logging.SetLogLevel("sub", "ERROR")
logging.SetLogLevel("storageminer", "ERROR")
}
func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) {
ctx := context.Background()
// Create mock CLI
mockCLI := newMockCLI(t, cmds)
clientCLI := mockCLI.client(clientNode.ListenAddr)
// Create some wallets on the node to use for testing multisig
var walletAddrs []address.Address
for i := 0; i < 4; i++ {
addr, err := clientNode.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err)
walletAddrs = append(walletAddrs, addr)
test.SendFunds(ctx, t, clientNode, addr, types.NewInt(1e15))
}
// Create an msig with three of the addresses and threshold of two sigs
// msig create --required=2 --duration=50 --value=1000attofil <addr1> <addr2> <addr3>
amtAtto := types.NewInt(1000)
threshold := 2
paramDuration := "--duration=50"
paramRequired := fmt.Sprintf("--required=%d", threshold)
paramValue := fmt.Sprintf("--value=%dattofil", amtAtto)
cmd := []string{
"msig", "create",
paramRequired,
paramDuration,
paramValue,
walletAddrs[0].String(),
walletAddrs[1].String(),
walletAddrs[2].String(),
}
out := clientCLI.runCmd(cmd)
fmt.Println(out)
// Extract msig robust address from output
expCreateOutPrefix := "Created new multisig:"
require.Regexp(t, regexp.MustCompile(expCreateOutPrefix), out)
parts := strings.Split(strings.TrimSpace(strings.Replace(out, expCreateOutPrefix, "", -1)), " ")
require.Len(t, parts, 2)
msigRobustAddr := parts[1]
fmt.Println("msig robust address:", msigRobustAddr)
// Propose to add a new address to the msig
// msig add-propose --from=<addr> <msig> <addr>
paramFrom := fmt.Sprintf("--from=%s", walletAddrs[0])
cmd = []string{
"msig", "add-propose",
paramFrom,
msigRobustAddr,
walletAddrs[3].String(),
}
out = clientCLI.runCmd(cmd)
fmt.Println(out)
// msig inspect <msig>
cmd = []string{"msig", "inspect", "--vesting", "--decode-params", msigRobustAddr}
out = clientCLI.runCmd(cmd)
fmt.Println(out)
// Expect correct balance
require.Regexp(t, regexp.MustCompile("Balance: 0.000000000000001 FIL"), out)
// Expect 1 transaction
require.Regexp(t, regexp.MustCompile(`Transactions:\s*1`), out)
// Expect transaction to be "AddSigner"
require.Regexp(t, regexp.MustCompile(`AddSigner`), out)
// Approve adding the new address
// msig add-approve --from=<addr> <msig> <addr> 0 <addr> false
txnID := "0"
paramFrom = fmt.Sprintf("--from=%s", walletAddrs[1])
cmd = []string{
"msig", "add-approve",
paramFrom,
msigRobustAddr,
walletAddrs[0].String(),
txnID,
walletAddrs[3].String(),
"false",
}
out = clientCLI.runCmd(cmd)
fmt.Println(out)
}

View File

@ -26,9 +26,11 @@ var (
// gatewayDepsAPI defines the API methods that the GatewayAPI depends on // gatewayDepsAPI defines the API methods that the GatewayAPI depends on
// (to make it easy to mock for tests) // (to make it easy to mock for tests)
type gatewayDepsAPI interface { type gatewayDepsAPI interface {
ChainHasObj(context.Context, cid.Cid) (bool, error)
ChainHead(ctx context.Context) (*types.TipSet, error) ChainHead(ctx context.Context) (*types.TipSet, error)
ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*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) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, 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) MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error)
MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)
@ -40,7 +42,18 @@ type gatewayDepsAPI interface {
} }
type GatewayAPI struct { type GatewayAPI struct {
api gatewayDepsAPI api gatewayDepsAPI
lookbackCap time.Duration
}
// NewGatewayAPI creates a new GatewayAPI with the default lookback cap
func NewGatewayAPI(api gatewayDepsAPI) *GatewayAPI {
return newGatewayAPI(api, LookbackCap)
}
// used by the tests
func newGatewayAPI(api gatewayDepsAPI, lookbackCap time.Duration) *GatewayAPI {
return &GatewayAPI{api: api, lookbackCap: lookbackCap}
} }
func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) error { func (a *GatewayAPI) checkTipsetKey(ctx context.Context, tsk types.TipSetKey) error {
@ -76,13 +89,17 @@ func (a *GatewayAPI) checkTipsetHeight(ts *types.TipSet, h abi.ChainEpoch) error
} }
func (a *GatewayAPI) checkTimestamp(at time.Time) error { func (a *GatewayAPI) checkTimestamp(at time.Time) error {
if time.Since(at) > LookbackCap { if time.Since(at) > a.lookbackCap {
return ErrLookbackTooLong return ErrLookbackTooLong
} }
return nil return nil
} }
func (a *GatewayAPI) ChainHasObj(ctx context.Context, c cid.Cid) (bool, error) {
return a.api.ChainHasObj(ctx, c)
}
func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
// 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)
@ -112,6 +129,10 @@ func (a *GatewayAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoc
return a.api.ChainGetTipSetByHeight(ctx, h, tsk) return a.api.ChainGetTipSetByHeight(ctx, h, tsk)
} }
func (a *GatewayAPI) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
return a.api.ChainReadObj(ctx, c)
}
func (a *GatewayAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { 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 { if err := a.checkTipsetKey(ctx, tsk); err != nil {
return nil, err return nil, err

View File

@ -88,7 +88,7 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
mock := &mockGatewayDepsAPI{} mock := &mockGatewayDepsAPI{}
a := &GatewayAPI{api: mock} a := NewGatewayAPI(mock)
// Create tipsets from genesis up to tskh and return the highest // Create tipsets from genesis up to tskh and return the highest
ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS) ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS)
@ -109,6 +109,10 @@ type mockGatewayDepsAPI struct {
tipsets []*types.TipSet tipsets []*types.TipSet
} }
func (m *mockGatewayDepsAPI) ChainHasObj(context.Context, cid.Cid) (bool, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { func (m *mockGatewayDepsAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
m.lk.RLock() m.lk.RLock()
defer m.lk.RUnlock() defer m.lk.RUnlock()
@ -158,6 +162,10 @@ func (m *mockGatewayDepsAPI) ChainGetTipSetByHeight(ctx context.Context, h abi.C
return m.tipsets[h], nil return m.tipsets[h], nil
} }
func (m *mockGatewayDepsAPI) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
panic("implement me")
}
func (m *mockGatewayDepsAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { func (m *mockGatewayDepsAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
panic("implement me") panic("implement me")
} }

View File

@ -4,10 +4,14 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"math"
"os" "os"
"testing" "testing"
"time" "time"
"github.com/filecoin-project/lotus/cli"
clitest "github.com/filecoin-project/lotus/cli/test"
init0 "github.com/filecoin-project/specs-actors/actors/builtin/init" init0 "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/builtin/multisig" "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
@ -26,20 +30,22 @@ import (
builder "github.com/filecoin-project/lotus/node/test" builder "github.com/filecoin-project/lotus/node/test"
) )
const maxLookbackCap = time.Duration(math.MaxInt64)
func init() { func init() {
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1) policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048)) policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256)) policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
} }
// TestEndToEnd tests that API calls can be made on a lite node that is // TestEndToEndWalletMsig tests that wallet and msig API calls can be made
// connected through a gateway to a full API node // on a lite node that is connected through a gateway to a full API node
func TestEndToEnd(t *testing.T) { func TestEndToEndWalletMsig(t *testing.T) {
_ = os.Setenv("BELLMAN_NO_GPU", "1") _ = os.Setenv("BELLMAN_NO_GPU", "1")
blocktime := 5 * time.Millisecond blocktime := 5 * time.Millisecond
ctx := context.Background() ctx := context.Background()
full, lite, closer := startNodes(ctx, t, blocktime) full, lite, closer := startNodes(ctx, t, blocktime, maxLookbackCap)
defer closer() defer closer()
// The full node starts with a wallet // The full node starts with a wallet
@ -56,11 +62,11 @@ func TestEndToEnd(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Send some funds from the full node to the lite node // Send some funds from the full node to the lite node
err = sendFunds(ctx, t, full, fullWalletAddr, liteWalletAddr, types.NewInt(1e18)) err = sendFunds(ctx, full, fullWalletAddr, liteWalletAddr, types.NewInt(1e18))
require.NoError(t, err) require.NoError(t, err)
// Send some funds from the lite node back to the full node // Send some funds from the lite node back to the full node
err = sendFunds(ctx, t, lite, liteWalletAddr, fullWalletAddr, types.NewInt(100)) err = sendFunds(ctx, lite, liteWalletAddr, fullWalletAddr, types.NewInt(100))
require.NoError(t, err) require.NoError(t, err)
// Sign some data with the lite node wallet address // Sign some data with the lite node wallet address
@ -81,7 +87,7 @@ func TestEndToEnd(t *testing.T) {
walletAddrs = append(walletAddrs, addr) walletAddrs = append(walletAddrs, addr)
err = sendFunds(ctx, t, lite, liteWalletAddr, addr, types.NewInt(1e15)) err = sendFunds(ctx, lite, liteWalletAddr, addr, types.NewInt(1e15))
require.NoError(t, err) require.NoError(t, err)
} }
@ -135,7 +141,33 @@ func TestEndToEnd(t *testing.T) {
require.True(t, approveReturn.Applied) require.True(t, approveReturn.Applied)
} }
func sendFunds(ctx context.Context, t *testing.T, fromNode test.TestNode, fromAddr address.Address, toAddr address.Address, amt types.BigInt) error { // TestEndToEndMsigCLI tests that msig CLI calls can be made
// on a lite node that is connected through a gateway to a full API node
func TestEndToEndMsigCLI(t *testing.T) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
clitest.QuietMiningLogs()
blocktime := 5 * time.Millisecond
ctx := context.Background()
full, lite, closer := startNodes(ctx, t, blocktime, maxLookbackCap)
defer closer()
// The full node starts with a wallet
fullWalletAddr, err := full.WalletDefaultAddress(ctx)
require.NoError(t, err)
// Create a wallet on the lite node
liteWalletAddr, err := lite.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err)
// Send some funds from the full node to the lite node
err = sendFunds(ctx, full, fullWalletAddr, liteWalletAddr, types.NewInt(1e18))
require.NoError(t, err)
clitest.RunMultisigTest(t, cli.Commands, lite)
}
func sendFunds(ctx context.Context, fromNode test.TestNode, fromAddr address.Address, toAddr address.Address, amt types.BigInt) error {
msg := &types.Message{ msg := &types.Message{
From: fromAddr, From: fromAddr,
To: toAddr, To: toAddr,
@ -158,7 +190,7 @@ func sendFunds(ctx context.Context, t *testing.T, fromNode test.TestNode, fromAd
return nil return nil
} }
func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (test.TestNode, test.TestNode, jsonrpc.ClientCloser) { func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration, lookbackCap time.Duration) (test.TestNode, test.TestNode, jsonrpc.ClientCloser) {
var closer jsonrpc.ClientCloser var closer jsonrpc.ClientCloser
// Create one miner and two full nodes. // Create one miner and two full nodes.
@ -175,7 +207,7 @@ func startNodes(ctx context.Context, t *testing.T, blocktime time.Duration) (tes
fullNode := nodes[0] fullNode := nodes[0]
// Create a gateway server in front of the full node // Create a gateway server in front of the full node
_, addr, err := builder.CreateRPCServer(&GatewayAPI{api: fullNode}) _, addr, err := builder.CreateRPCServer(newGatewayAPI(fullNode, lookbackCap))
require.NoError(t, err) require.NoError(t, err)
// Create a gateway client API that connects to the gateway server // Create a gateway client API that connects to the gateway server

View File

@ -76,7 +76,7 @@ var runCmd = &cli.Command{
log.Info("Setting up API endpoint at " + address) log.Info("Setting up API endpoint at " + address)
rpcServer := jsonrpc.NewServer() rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", &GatewayAPI{api: api}) rpcServer.Register("Filecoin", NewGatewayAPI(api))
mux.Handle("/rpc/v0", rpcServer) mux.Handle("/rpc/v0", rpcServer)
mux.PathPrefix("/").Handler(http.DefaultServeMux) mux.PathPrefix("/").Handler(http.DefaultServeMux)

View File

@ -40,9 +40,11 @@ import (
var log = logging.Logger("fullnode") var log = logging.Logger("fullnode")
type ChainModuleAPI interface { type ChainModuleAPI interface {
ChainHasObj(context.Context, cid.Cid) (bool, error)
ChainHead(context.Context) (*types.TipSet, error) ChainHead(context.Context) (*types.TipSet, error)
ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*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) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
} }
// ChainModule provides a default implementation of ChainModuleAPI. // ChainModule provides a default implementation of ChainModuleAPI.
@ -206,8 +208,8 @@ func (m *ChainModule) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpo
return m.Chain.GetTipsetByHeight(ctx, h, ts, true) return m.Chain.GetTipsetByHeight(ctx, h, ts, true)
} }
func (a *ChainAPI) ChainReadObj(ctx context.Context, obj cid.Cid) ([]byte, error) { func (m *ChainModule) ChainReadObj(ctx context.Context, obj cid.Cid) ([]byte, error) {
blk, err := a.Chain.Blockstore().Get(obj) blk, err := m.Chain.Blockstore().Get(obj)
if err != nil { if err != nil {
return nil, xerrors.Errorf("blockstore get: %w", err) return nil, xerrors.Errorf("blockstore get: %w", err)
} }
@ -219,8 +221,8 @@ func (a *ChainAPI) ChainDeleteObj(ctx context.Context, obj cid.Cid) error {
return a.Chain.Blockstore().DeleteBlock(obj) return a.Chain.Blockstore().DeleteBlock(obj)
} }
func (a *ChainAPI) ChainHasObj(ctx context.Context, obj cid.Cid) (bool, error) { func (m *ChainModule) ChainHasObj(ctx context.Context, obj cid.Cid) (bool, error) {
return a.Chain.Blockstore().Has(obj) return m.Chain.Blockstore().Has(obj)
} }
func (a *ChainAPI) ChainStatObj(ctx context.Context, obj cid.Cid, base cid.Cid) (api.ObjStat, error) { func (a *ChainAPI) ChainStatObj(ctx context.Context, obj cid.Cid, base cid.Cid) (api.ObjStat, error) {