From 418f5d509ca9504ff94de28284a527623176a039 Mon Sep 17 00:00:00 2001 From: hunjixin <1084400399@qq.com> Date: Wed, 28 Dec 2022 14:22:51 +0800 Subject: [PATCH] support actor config type --- .gitignore | 3 +- api/messager_impl.go | 112 +++--- cli/actor_cfg.go | 451 +++++++++++++++++++++++++ cli/address.go | 28 +- cli/flag.go | 23 ++ cli/util.go | 15 +- docs/zh/design-specs.md | 16 + go.mod | 6 +- go.sum | 8 +- integration_test/actor_cfg_test.go | 127 +++++++ integration_test/shared_params_test.go | 17 +- main.go | 1 + models/mtypes/db_cid.go | 63 ++++ models/mysql/actor_cfg.go | 155 +++++++++ models/mysql/actor_cfg_test.go | 221 ++++++++++++ models/mysql/address.go | 79 ++--- models/mysql/address_test.go | 30 +- models/mysql/db.go | 31 +- models/mysql/shared_params.go | 43 ++- models/mysql/shared_params_test.go | 16 +- models/mysql/testing.go | 71 ++++ models/mysql/types.go | 12 + models/repo/actor_cfg_repo.go | 18 + models/repo/repo.go | 10 +- models/sqlite/actor_cfg.go | 155 +++++++++ models/sqlite/actor_cfg_test.go | 133 ++++++++ models/sqlite/address.go | 73 ++-- models/sqlite/address_test.go | 58 ++-- models/sqlite/db.go | 35 +- models/sqlite/init.go | 27 -- models/sqlite/message.go | 4 +- models/sqlite/node.go | 13 +- models/sqlite/node_test.go | 69 +++- models/sqlite/shared_params.go | 44 ++- models/sqlite/shared_params_test.go | 32 +- models/sqlite/types.go | 40 +++ service/message_selector.go | 112 ++++-- service/message_selector_test.go | 40 ++- service/message_service.go | 16 + service/shared_params_service.go | 16 +- testhelper/utils.go | 15 +- 41 files changed, 2049 insertions(+), 389 deletions(-) create mode 100644 cli/actor_cfg.go create mode 100644 integration_test/actor_cfg_test.go create mode 100644 models/mtypes/db_cid.go create mode 100644 models/mysql/actor_cfg.go create mode 100644 models/mysql/actor_cfg_test.go create mode 100644 models/mysql/types.go create mode 100644 models/repo/actor_cfg_repo.go create mode 100644 models/sqlite/actor_cfg.go create mode 100644 models/sqlite/actor_cfg_test.go delete mode 100644 models/sqlite/init.go create mode 100644 models/sqlite/types.go diff --git a/.gitignore b/.gitignore index a70c1750..5aee4de3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,8 @@ *.txt tipset.json test_sqlite_db -./cmds/cfg_example/cfg_example +cmds/cfg_example/cfg_example +cmds/client_example/client_example venus-messager *.log ./venus-messager-tools diff --git a/api/messager_impl.go b/api/messager_impl.go index 17bdd757..11a48752 100644 --- a/api/messager_impl.go +++ b/api/messager_impl.go @@ -46,190 +46,206 @@ type MessageImp struct { Net pubsub.INet } -func (m MessageImp) HasMessageByUid(ctx context.Context, id string) (bool, error) { +var _ messager.IMessager = (*MessageImp)(nil) + +func (m *MessageImp) HasMessageByUid(ctx context.Context, id string) (bool, error) { return m.MessageSrv.HasMessageByUid(ctx, id) } -func (m MessageImp) WaitMessage(ctx context.Context, id string, confidence uint64) (*types.Message, error) { +func (m *MessageImp) WaitMessage(ctx context.Context, id string, confidence uint64) (*types.Message, error) { return m.MessageSrv.WaitMessage(ctx, id, confidence) } -func (m MessageImp) PushMessage(ctx context.Context, msg *venusTypes.Message, meta *types.SendSpec) (string, error) { +func (m *MessageImp) PushMessage(ctx context.Context, msg *venusTypes.Message, meta *types.SendSpec) (string, error) { return m.MessageSrv.PushMessage(ctx, msg, meta) } -func (m MessageImp) PushMessageWithId(ctx context.Context, id string, msg *venusTypes.Message, meta *types.SendSpec) (string, error) { +func (m *MessageImp) PushMessageWithId(ctx context.Context, id string, msg *venusTypes.Message, meta *types.SendSpec) (string, error) { return m.MessageSrv.PushMessageWithId(ctx, id, msg, meta) } -func (m MessageImp) GetMessageByUid(ctx context.Context, id string) (*types.Message, error) { +func (m *MessageImp) GetMessageByUid(ctx context.Context, id string) (*types.Message, error) { return m.MessageSrv.GetMessageByUid(ctx, id) } -func (m MessageImp) GetMessageBySignedCid(ctx context.Context, cid cid.Cid) (*types.Message, error) { +func (m *MessageImp) GetMessageBySignedCid(ctx context.Context, cid cid.Cid) (*types.Message, error) { return m.MessageSrv.GetMessageBySignedCid(ctx, cid) } -func (m MessageImp) GetMessageByUnsignedCid(ctx context.Context, cid cid.Cid) (*types.Message, error) { +func (m *MessageImp) GetMessageByUnsignedCid(ctx context.Context, cid cid.Cid) (*types.Message, error) { return m.MessageSrv.GetMessageByUnsignedCid(ctx, cid) } -func (m MessageImp) GetMessageByFromAndNonce(ctx context.Context, from address.Address, nonce uint64) (*types.Message, error) { +func (m *MessageImp) GetMessageByFromAndNonce(ctx context.Context, from address.Address, nonce uint64) (*types.Message, error) { return m.MessageSrv.GetMessageByFromAndNonce(ctx, from, nonce) } -func (m MessageImp) ListMessage(ctx context.Context) ([]*types.Message, error) { +func (m *MessageImp) ListMessage(ctx context.Context) ([]*types.Message, error) { return m.MessageSrv.ListMessage(ctx) } -func (m MessageImp) ListMessageByFromState(ctx context.Context, from address.Address, state types.MessageState, isAsc bool, pageIndex, pageSize int) ([]*types.Message, error) { +func (m *MessageImp) ListMessageByFromState(ctx context.Context, from address.Address, state types.MessageState, isAsc bool, pageIndex, pageSize int) ([]*types.Message, error) { return m.MessageSrv.ListMessageByFromState(ctx, from, state, isAsc, pageIndex, pageSize) } -func (m MessageImp) ListMessageByAddress(ctx context.Context, addr address.Address) ([]*types.Message, error) { +func (m *MessageImp) ListMessageByAddress(ctx context.Context, addr address.Address) ([]*types.Message, error) { return m.MessageSrv.ListMessageByAddress(ctx, addr) } -func (m MessageImp) ListFailedMessage(ctx context.Context) ([]*types.Message, error) { +func (m *MessageImp) ListFailedMessage(ctx context.Context) ([]*types.Message, error) { return m.MessageSrv.ListFailedMessage(ctx) } -func (m MessageImp) ListBlockedMessage(ctx context.Context, addr address.Address, d time.Duration) ([]*types.Message, error) { +func (m *MessageImp) ListBlockedMessage(ctx context.Context, addr address.Address, d time.Duration) ([]*types.Message, error) { return m.MessageSrv.ListBlockedMessage(ctx, addr, d) } -func (m MessageImp) UpdateMessageStateByID(ctx context.Context, id string, state types.MessageState) error { +func (m *MessageImp) UpdateMessageStateByID(ctx context.Context, id string, state types.MessageState) error { return m.MessageSrv.UpdateMessageStateByID(ctx, id, state) } -func (m MessageImp) UpdateAllFilledMessage(ctx context.Context) (int, error) { +func (m *MessageImp) UpdateAllFilledMessage(ctx context.Context) (int, error) { return m.MessageSrv.UpdateAllFilledMessage(ctx) } -func (m MessageImp) UpdateFilledMessageByID(ctx context.Context, id string) (string, error) { +func (m *MessageImp) UpdateFilledMessageByID(ctx context.Context, id string) (string, error) { return m.MessageSrv.UpdateFilledMessageByID(ctx, id) } -func (m MessageImp) ReplaceMessage(ctx context.Context, params *types.ReplacMessageParams) (cid.Cid, error) { +func (m *MessageImp) ReplaceMessage(ctx context.Context, params *types.ReplacMessageParams) (cid.Cid, error) { return m.MessageSrv.ReplaceMessage(ctx, params) } -func (m MessageImp) RepublishMessage(ctx context.Context, id string) error { +func (m *MessageImp) RepublishMessage(ctx context.Context, id string) error { return m.MessageSrv.RepublishMessage(ctx, id) } -func (m MessageImp) MarkBadMessage(ctx context.Context, id string) error { +func (m *MessageImp) MarkBadMessage(ctx context.Context, id string) error { return m.MessageSrv.MarkBadMessage(ctx, id) } -func (m MessageImp) RecoverFailedMsg(ctx context.Context, addr address.Address) ([]string, error) { +func (m *MessageImp) RecoverFailedMsg(ctx context.Context, addr address.Address) ([]string, error) { return m.MessageSrv.RecoverFailedMsg(ctx, addr) } -func (m MessageImp) GetAddress(ctx context.Context, addr address.Address) (*types.Address, error) { +func (m *MessageImp) GetAddress(ctx context.Context, addr address.Address) (*types.Address, error) { return m.AddressSrv.GetAddress(ctx, addr) } -func (m MessageImp) HasAddress(ctx context.Context, addr address.Address) (bool, error) { +func (m *MessageImp) HasAddress(ctx context.Context, addr address.Address) (bool, error) { return m.AddressSrv.HasAddress(ctx, addr) } -func (m MessageImp) WalletHas(ctx context.Context, addr address.Address) (bool, error) { +func (m *MessageImp) WalletHas(ctx context.Context, addr address.Address) (bool, error) { return m.AddressSrv.WalletHas(ctx, addr) } -func (m MessageImp) ListAddress(ctx context.Context) ([]*types.Address, error) { +func (m *MessageImp) ListAddress(ctx context.Context) ([]*types.Address, error) { return m.AddressSrv.ListAddress(ctx) } -func (m MessageImp) UpdateNonce(ctx context.Context, addr address.Address, nonce uint64) error { +func (m *MessageImp) UpdateNonce(ctx context.Context, addr address.Address, nonce uint64) error { return m.AddressSrv.UpdateNonce(ctx, addr, nonce) } -func (m MessageImp) DeleteAddress(ctx context.Context, addr address.Address) error { +func (m *MessageImp) DeleteAddress(ctx context.Context, addr address.Address) error { return m.AddressSrv.DeleteAddress(ctx, addr) } -func (m MessageImp) ForbiddenAddress(ctx context.Context, addr address.Address) error { +func (m *MessageImp) ForbiddenAddress(ctx context.Context, addr address.Address) error { return m.AddressSrv.ForbiddenAddress(ctx, addr) } -func (m MessageImp) ActiveAddress(ctx context.Context, addr address.Address) error { +func (m *MessageImp) ActiveAddress(ctx context.Context, addr address.Address) error { return m.AddressSrv.ActiveAddress(ctx, addr) } -func (m MessageImp) SetSelectMsgNum(ctx context.Context, addr address.Address, num uint64) error { +func (m *MessageImp) SetSelectMsgNum(ctx context.Context, addr address.Address, num uint64) error { return m.AddressSrv.SetSelectMsgNum(ctx, addr, num) } -func (m MessageImp) SetFeeParams(ctx context.Context, params *types.AddressSpec) error { +func (m *MessageImp) SetFeeParams(ctx context.Context, params *types.AddressSpec) error { return m.AddressSrv.SetFeeParams(ctx, params) } -func (m MessageImp) ClearUnFillMessage(ctx context.Context, addr address.Address) (int, error) { +func (m *MessageImp) ClearUnFillMessage(ctx context.Context, addr address.Address) (int, error) { return m.MessageSrv.ClearUnFillMessage(ctx, addr) } -func (m MessageImp) GetSharedParams(ctx context.Context) (*types.SharedSpec, error) { +func (m *MessageImp) GetSharedParams(ctx context.Context) (*types.SharedSpec, error) { return m.ParamsSrv.GetSharedParams(ctx) } -func (m MessageImp) SetSharedParams(ctx context.Context, params *types.SharedSpec) error { +func (m *MessageImp) SetSharedParams(ctx context.Context, params *types.SharedSpec) error { return m.ParamsSrv.SetSharedParams(ctx, params) } -func (m MessageImp) SaveNode(ctx context.Context, node *types.Node) error { +func (m *MessageImp) SaveNode(ctx context.Context, node *types.Node) error { return m.NodeSrv.SaveNode(ctx, node) } -func (m MessageImp) GetNode(ctx context.Context, name string) (*types.Node, error) { +func (m *MessageImp) GetNode(ctx context.Context, name string) (*types.Node, error) { return m.NodeSrv.GetNode(ctx, name) } -func (m MessageImp) HasNode(ctx context.Context, name string) (bool, error) { +func (m *MessageImp) HasNode(ctx context.Context, name string) (bool, error) { return m.NodeSrv.HasNode(ctx, name) } -func (m MessageImp) ListNode(ctx context.Context) ([]*types.Node, error) { +func (m *MessageImp) ListNode(ctx context.Context) ([]*types.Node, error) { return m.NodeSrv.ListNode(ctx) } -func (m MessageImp) DeleteNode(ctx context.Context, name string) error { +func (m *MessageImp) DeleteNode(ctx context.Context, name string) error { return m.NodeSrv.DeleteNode(ctx, name) } -func (m MessageImp) Send(ctx context.Context, params types.QuickSendParams) (string, error) { +func (m *MessageImp) Send(ctx context.Context, params types.QuickSendParams) (string, error) { return m.MessageSrv.Send(ctx, params) } -func (m MessageImp) NetFindPeer(ctx context.Context, peerID peer.ID) (peer.AddrInfo, error) { +func (m *MessageImp) NetFindPeer(ctx context.Context, peerID peer.ID) (peer.AddrInfo, error) { return m.Net.FindPeer(ctx, peerID) } -func (m MessageImp) NetConnect(ctx context.Context, pi peer.AddrInfo) error { +func (m *MessageImp) NetConnect(ctx context.Context, pi peer.AddrInfo) error { return m.Net.Connect(ctx, pi) } -func (m MessageImp) NetPeers(ctx context.Context) ([]peer.AddrInfo, error) { +func (m *MessageImp) NetPeers(ctx context.Context) ([]peer.AddrInfo, error) { return m.Net.Peers(ctx) } -func (m MessageImp) NetAddrsListen(ctx context.Context) (peer.AddrInfo, error) { +func (m *MessageImp) NetAddrsListen(ctx context.Context) (peer.AddrInfo, error) { return m.Net.AddrListen(ctx) } -var _ messager.IMessager = (*MessageImp)(nil) - -func (m MessageImp) Version(_ context.Context) (venusTypes.Version, error) { +func (m *MessageImp) Version(_ context.Context) (venusTypes.Version, error) { return venusTypes.Version{ Version: version.Version, }, nil } -func (m MessageImp) SetLogLevel(ctx context.Context, subSystem, level string) error { +func (m *MessageImp) SetLogLevel(ctx context.Context, subSystem, level string) error { return logging.SetLogLevel(subSystem, level) } -func (m MessageImp) LogList(ctx context.Context) ([]string, error) { +func (m *MessageImp) LogList(ctx context.Context) ([]string, error) { return logging.GetSubsystems(), nil } + +func (m *MessageImp) SaveActorCfg(ctx context.Context, actorCfg *types.ActorCfg) error { + return m.MessageSrv.SaveActorCfg(ctx, actorCfg) +} + +func (m *MessageImp) UpdateActorCfg(ctx context.Context, id venusTypes.UUID, changeSpecParams *types.ChangeGasSpecParams) error { + return m.MessageSrv.UpdateActorCfg(ctx, id, changeSpecParams) +} + +func (m *MessageImp) ListActorCfg(ctx context.Context) ([]*types.ActorCfg, error) { + return m.MessageSrv.ListActorCfg(ctx) +} + +func (m *MessageImp) GetActorCfgByID(ctx context.Context, id venusTypes.UUID) (*types.ActorCfg, error) { + return m.MessageSrv.GetActorCfgByID(ctx, id) +} diff --git a/cli/actor_cfg.go b/cli/actor_cfg.go new file mode 100644 index 00000000..65873575 --- /dev/null +++ b/cli/actor_cfg.go @@ -0,0 +1,451 @@ +package cli + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os" + "sort" + "strconv" + + "github.com/filecoin-project/go-state-types/big" + + actors2 "github.com/filecoin-project/venus/venus-shared/actors" + + "github.com/filecoin-project/venus/venus-shared/utils" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/go-state-types/actors" + types2 "github.com/filecoin-project/venus/venus-shared/types" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/venus-messager/cli/tablewriter" + types "github.com/filecoin-project/venus/venus-shared/types/messager" + "github.com/urfave/cli/v2" +) + +var ActorCfgCmds = &cli.Command{ + Name: "actor", + Usage: "actor config", + Subcommands: []*cli.Command{ + listActorCfgCmd, + getActorCfgCmd, + updateActorCfgCmd, + addActorCfgCmd, + listBuiltinActorCmd, + }, +} + +var listActorCfgCmd = &cli.Command{ + Name: "list", + Usage: "list actor config", + Flags: []cli.Flag{ + outputTypeFlag, + }, + Action: func(ctx *cli.Context) error { + client, closer, err := getAPI(ctx) + if err != nil { + return err + } + defer closer() + + nodeAPI, nodeAPICloser, err := getNodeAPI(ctx) + if err != nil { + return err + } + defer nodeAPICloser() + + if err := LoadBuiltinActors(ctx.Context, nodeAPI); err != nil { + return err + } + + actorCfgs, err := client.ListActorCfg(ctx.Context) + if err != nil { + return err + } + + if ctx.String(outputTypeFlag.Name) == "table" { + return outputActorCfgWithTable(actorCfgs) + } + + bytes, err := json.MarshalIndent(actorCfgs, " ", "\t") + if err != nil { + return err + } + fmt.Println(string(bytes)) + return nil + }, +} + +var updateActorCfgCmd = &cli.Command{ + Name: "update-fee-params", + Usage: "update fee params of actor config", + ArgsUsage: "", + Flags: []cli.Flag{ + gasOverEstimationFlag, + gasFeeCapFlag, + maxFeeFlag, + basefeeFlag, + GasOverPremiumFlag, + }, + Action: func(ctx *cli.Context) error { + client, closer, err := getAPI(ctx) + if err != nil { + return err + } + defer closer() + + nodeAPI, nodeAPICloser, err := getNodeAPI(ctx) + if err != nil { + return err + } + defer nodeAPICloser() + + if err := LoadBuiltinActors(ctx.Context, nodeAPI); err != nil { + return err + } + + if ctx.NArg() != 1 { + return errors.New("must specify one uid argument") + } + uuidStr := ctx.Args().Get(0) + id, err := types2.ParseUUID(uuidStr) + if err != nil { + return err + } + + hasUpdate := false + changeGasSpecParams := &types.ChangeGasSpecParams{} + if ctx.IsSet(gasOverEstimationFlag.Name) { + hasUpdate = true + gasOverEstimation := ctx.Float64(gasOverEstimationFlag.Name) + changeGasSpecParams.GasOverEstimation = &gasOverEstimation + } + + if ctx.IsSet(GasOverPremiumFlag.Name) { + hasUpdate = true + gasOverPremium := ctx.Float64(GasOverPremiumFlag.Name) + changeGasSpecParams.GasOverPremium = &gasOverPremium + } + + if ctx.IsSet(gasFeeCapFlag.Name) { + hasUpdate = true + gasFeeCap, err := types2.BigFromString(ctx.String(gasFeeCapFlag.Name)) + if err != nil { + return fmt.Errorf("parsing feecap failed %v", err) + } + changeGasSpecParams.GasFeeCap = big.Int(gasFeeCap) + } + + if ctx.IsSet(maxFeeFlag.Name) { + hasUpdate = true + maxfee, err := types2.BigFromString(ctx.String(maxFeeFlag.Name)) + if err != nil { + return fmt.Errorf("parsing maxfee failed %v", err) + } + changeGasSpecParams.MaxFee = big.Int(maxfee) + } + + if ctx.IsSet(basefeeFlag.Name) { + hasUpdate = true + basefee, err := types2.BigFromString(ctx.String(basefeeFlag.Name)) + if err != nil { + return fmt.Errorf("parsing maxfee failed %v", err) + } + changeGasSpecParams.BaseFee = big.Int(basefee) + } + if !hasUpdate { + return errors.New("no new params to update") + } + return client.UpdateActorCfg(ctx.Context, id, changeGasSpecParams) + }, +} + +var addActorCfgCmd = &cli.Command{ + Name: "add", + Usage: "add new actor config", + ArgsUsage: " ", + Flags: []cli.Flag{ + gasOverEstimationFlag, + gasFeeCapFlag, + maxFeeFlag, + basefeeFlag, + GasOverPremiumFlag, + &cli.IntFlag{ + Name: "version", + }, + }, + Action: func(ctx *cli.Context) error { + client, closer, err := getAPI(ctx) + if err != nil { + return err + } + defer closer() + + nodeAPI, nodeAPICloser, err := getNodeAPI(ctx) + if err != nil { + return err + } + defer nodeAPICloser() + + if err := LoadBuiltinActors(ctx.Context, nodeAPI); err != nil { + return err + } + + if ctx.NArg() != 2 { + return errors.New("must specify code and method argument") + } + codeCid, err := cid.Decode(ctx.Args().Get(0)) + if err != nil { + return err + } + + method, err := strconv.ParseUint(ctx.Args().Get(1), 10, 64) + if err != nil { + return err + } + newActorCfg := &types.ActorCfg{ + ID: types2.NewUUID(), + MethodType: types.MethodType{ + Code: codeCid, + Method: abi.MethodNum(method), + }, + FeeSpec: types.FeeSpec{}, + } + + if ctx.IsSet("version") { + newActorCfg.ActorVersion = actors.Version(ctx.Int("version")) + } else { + //try resolve builtin + methodMeta, found := utils.MethodsMap[newActorCfg.Code][newActorCfg.Method] + if !found { + return fmt.Errorf("actor not found:%v method(%d) not exist", newActorCfg.Code, newActorCfg.Method) + } + newActorCfg.ActorVersion = methodMeta.Version + } + + hasArgument := false + if ctx.IsSet(gasOverEstimationFlag.Name) { + hasArgument = true + gasOverEstimation := ctx.Float64(gasOverEstimationFlag.Name) + newActorCfg.GasOverEstimation = gasOverEstimation + } + + if ctx.IsSet(GasOverPremiumFlag.Name) { + hasArgument = true + gasOverPremium := ctx.Float64(GasOverPremiumFlag.Name) + newActorCfg.GasOverPremium = gasOverPremium + } + + if ctx.IsSet(gasFeeCapFlag.Name) { + hasArgument = true + gasFeeCap, err := types2.BigFromString(ctx.String(gasFeeCapFlag.Name)) + if err != nil { + return fmt.Errorf("parsing feecap failed %v", err) + } + newActorCfg.GasFeeCap = big.Int(gasFeeCap) + } + + if ctx.IsSet(maxFeeFlag.Name) { + hasArgument = true + maxfee, err := types2.BigFromString(ctx.String(maxFeeFlag.Name)) + if err != nil { + return fmt.Errorf("parsing maxfee failed %v", err) + } + newActorCfg.MaxFee = big.Int(maxfee) + } + + if ctx.IsSet(basefeeFlag.Name) { + hasArgument = true + basefee, err := types2.BigFromString(ctx.String(basefeeFlag.Name)) + if err != nil { + return fmt.Errorf("parsing maxfee failed %v", err) + } + newActorCfg.BaseFee = big.Int(basefee) + } + if !hasArgument { + return errors.New("no argument to save") + } + return client.SaveActorCfg(ctx.Context, newActorCfg) + }, +} + +var getActorCfgCmd = &cli.Command{ + Name: "get", + Usage: "get actor config", + ArgsUsage: "", + Flags: []cli.Flag{ + outputTypeFlag, + }, + Action: func(ctx *cli.Context) error { + client, closer, err := getAPI(ctx) + if err != nil { + return err + } + defer closer() + + nodeAPI, nodeAPICloser, err := getNodeAPI(ctx) + if err != nil { + return err + } + defer nodeAPICloser() + + if err := LoadBuiltinActors(ctx.Context, nodeAPI); err != nil { + return err + } + + if ctx.NArg() != 1 { + return errors.New("must specific one uid argument") + } + uuidStr := ctx.Args().Get(0) + id, err := types2.ParseUUID(uuidStr) + if err != nil { + return err + } + + actorCfg, err := client.GetActorCfgByID(ctx.Context, id) + if err != nil { + return err + } + + if ctx.String(outputTypeFlag.Name) == "table" { + return outputActorCfgWithTable([]*types.ActorCfg{actorCfg}) + } + + bytes, err := json.MarshalIndent(actorCfg, " ", "\t") + if err != nil { + return err + } + fmt.Println(string(bytes)) + return nil + }, +} + +var listBuiltinActorCmd = &cli.Command{ + Name: "list-builtin-actors", + Usage: "list builtin actors", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "version", + }, + }, + Action: func(ctx *cli.Context) error { + type MethodMeta struct { + Name string + Code cid.Cid + Method abi.MethodNum + Version actors.Version + } + + nodeAPI, nodeAPICloser, err := getNodeAPI(ctx) + if err != nil { + return err + } + defer nodeAPICloser() + + if err := LoadBuiltinActors(ctx.Context, nodeAPI); err != nil { + return err + } + + methodsByActorsVersion := make(map[actors.Version]map[cid.Cid][]MethodMeta) + var maxVersion actors.Version + for codeCid, methodMap := range utils.MethodsMap { + for methodNumber, meta := range methodMap { + _, ok := methodsByActorsVersion[meta.Version] + if !ok { + methodsByActorsVersion[meta.Version] = make(map[cid.Cid][]MethodMeta) + } + methods := append(methodsByActorsVersion[meta.Version][codeCid], MethodMeta{ + Name: meta.Name, + Code: codeCid, + Method: methodNumber, + Version: meta.Version, + }) + sort.Slice(methods, func(i, j int) bool { + return methods[i].Method < methods[j].Method + }) + methodsByActorsVersion[meta.Version][codeCid] = methods + if meta.Version > maxVersion { + maxVersion = meta.Version + } + } + } + + var version actors.Version + if ctx.IsSet("version") { + version = actors.Version(ctx.Int("version")) + } else { + //get max version + version = maxVersion + } + + var actorCfgTw = tablewriter.New( + tablewriter.Col("Category"), + tablewriter.Col("Version"), + tablewriter.Col("Code"), + tablewriter.Col("Method"), + tablewriter.Col("MethodName"), + ) + printBuiltinTypes := methodsByActorsVersion[version] + for codeCid, methods := range printBuiltinTypes { + name, _, has := actors2.GetActorMetaByCode(codeCid) + if !has { + return fmt.Errorf("not found builtin actor %s", codeCid) + } + actorCfgTw.Write(map[string]interface{}{ + "Category": name, + }) + for _, method := range methods { + row := map[string]interface{}{ + "Version": method.Version, + "Code": codeCid, + "Method": method.Method, + "MethodName": method.Name, + } + actorCfgTw.Write(row) + } + } + return actorCfgTw.Flush(os.Stdout) + }, +} +var actorCfgTw = tablewriter.New( + tablewriter.Col("ID"), + tablewriter.Col("NVersion"), + tablewriter.Col("Code"), + tablewriter.Col("Method"), + tablewriter.Col("MethodName"), + tablewriter.Col("GasOverEstimation"), + tablewriter.Col("GasOverPremium"), + tablewriter.Col("MaxFee"), + tablewriter.Col("GasFeeCap"), + tablewriter.Col("BaseFee"), + tablewriter.Col("CreateAt"), +) + +func outputActorCfgWithTable(actorCfg []*types.ActorCfg) error { + for _, actorCfg := range actorCfg { + row := map[string]interface{}{ + "ID": actorCfg.ID, + "AVersion": actorCfg.ActorVersion, + "Code": actorCfg.Code, + "Method": actorCfg.Method, + "MethodName": resolveBuiltinMethodName(actorCfg.MethodType), + "GasOverEstimation": actorCfg.FeeSpec.GasOverEstimation, + "GasOverPremium": actorCfg.FeeSpec.GasOverPremium, + "MaxFee": actorCfg.FeeSpec.MaxFee, + "GasFeeCap": actorCfg.FeeSpec.GasFeeCap, + "BaseFee": actorCfg.FeeSpec.BaseFee, + "CreateAt": actorCfg.CreatedAt.Format("2006-01-02 15:04:05"), + } + actorCfgTw.Write(row) + } + + buf := new(bytes.Buffer) + if err := actorCfgTw.Flush(buf); err != nil { + return err + } + fmt.Println(buf) + return nil +} diff --git a/cli/address.go b/cli/address.go index 9059830b..0b19977d 100644 --- a/cli/address.go +++ b/cli/address.go @@ -221,22 +221,10 @@ var setFeeParamsCmd = &cli.Command{ Usage: "Address setting fee associated configuration", ArgsUsage: "
", Flags: []cli.Flag{ - &cli.Float64Flag{ - Name: "gas-overestimation", - Usage: "Estimate the coefficient of gas", - }, - &cli.StringFlag{ - Name: "gas-feecap", - Usage: "Gas feecap for a message (burn and pay to miner, attoFIL/GasUnit)", - }, - &cli.StringFlag{ - Name: "max-fee", - Usage: "Spend up to X attoFIL for message", - }, - &cli.StringFlag{ - Name: "base-fee", - Usage: "", - }, + gasOverEstimationFlag, + gasFeeCapFlag, + maxFeeFlag, + basefeeFlag, GasOverPremiumFlag, }, Action: func(ctx *cli.Context) error { @@ -251,11 +239,11 @@ var setFeeParamsCmd = &cli.Command{ } params := &messager.AddressSpec{ - GasOverEstimation: ctx.Float64("gas-overestimation"), + GasOverEstimation: ctx.Float64(gasOverEstimationFlag.Name), GasOverPremium: ctx.Float64(GasOverPremiumFlag.Name), - MaxFeeStr: ctx.String("max-fee"), - GasFeeCapStr: ctx.String("gas-feecap"), - BaseFeeStr: ctx.String("base-fee"), + MaxFeeStr: ctx.String(maxFeeFlag.Name), + GasFeeCapStr: ctx.String(gasFeeCapFlag.Name), + BaseFeeStr: ctx.String(basefeeFlag.Name), } params.Address, err = address.NewFromString(ctx.Args().First()) if err != nil { diff --git a/cli/flag.go b/cli/flag.go index 3cc4cbd4..c3ddd57e 100644 --- a/cli/flag.go +++ b/cli/flag.go @@ -14,6 +14,23 @@ var GasOverPremiumFlag = &cli.Float64Flag{ Usage: "", } +var gasOverEstimationFlag = &cli.Float64Flag{ + Name: "gas-overestimation", + Usage: "Estimate the coefficient of gas", +} +var gasFeeCapFlag = &cli.StringFlag{ + Name: "gas-feecap", + Usage: "Gas feecap for a message (burn and pay to miner, attoFIL/GasUnit)", +} +var maxFeeFlag = &cli.StringFlag{ + Name: "max-fee", + Usage: "Spend up to X attoFIL for message", +} +var basefeeFlag = &cli.StringFlag{ + Name: "base-fee", + Usage: "", +} + func ParseFlagToReplaceMessaeParams(cctx *cli.Context) (*messager.ReplacMessageParams, error) { params := messager.ReplacMessageParams{ Auto: cctx.Bool("auto"), @@ -57,6 +74,12 @@ var outputTypeFlag = &cli.StringFlag{ Value: "table", } +var uuidFlag = &cli.StringFlag{ + Name: "id", + Usage: "uuid type, ", + Required: true, +} + var FromFlag = &cli.StringFlag{ Name: "from", Usage: "address to send message", diff --git a/cli/util.go b/cli/util.go index 4808eb26..87937c44 100644 --- a/cli/util.go +++ b/cli/util.go @@ -18,7 +18,7 @@ import ( "github.com/ipfs/go-cid" ) -var tw = tablewriter.New( +var msgTw = tablewriter.New( tablewriter.Col("ID"), tablewriter.Col("To"), tablewriter.Col("From"), @@ -68,11 +68,11 @@ func outputWithTable(msgs []*types.Message, verbose bool, nodeAPI v1.FullNode) e if msg.Receipt != nil { row["ExitCode"] = msg.Receipt.ExitCode } - tw.Write(row) + msgTw.Write(row) } buf := new(bytes.Buffer) - if err := tw.Flush(buf); err != nil { + if err := msgTw.Flush(buf); err != nil { return err } fmt.Println(buf) @@ -186,3 +186,12 @@ func methodToStr(nodeAPI v1.FullNode, msg venusTypes.Message) string { return methodStr } + +func resolveBuiltinMethodName(methodType types.MethodType) string { + methodMeta, found := utils.MethodsMap[methodType.Code][methodType.Method] + if !found { + return "" + } + + return methodMeta.Name +} diff --git a/docs/zh/design-specs.md b/docs/zh/design-specs.md index 1e2219af..062bf6a3 100644 --- a/docs/zh/design-specs.md +++ b/docs/zh/design-specs.md @@ -189,3 +189,19 @@ node_type | int | 节点类型 is_deleted | int | 是否删除,-1:否,1:是 created_at | datetime | 创建时间 updated_at | datetime | 更新时间 + +5. actor费用配置表 + +name | type | desc +---|-----------------|---| +id | varchar(256) | primary key +actor_v | uint | actor版本 +code | varchar(256) | 代码 +method| unsigned bigint | 方法号 +gas_over_estimation | decimal(10,2) | gas limit的系数 +gas_over_estimation | decimal(10,2) | gas premium的系数 +max_fee | varchar(256) | 最大费用 +base_fee | varchar(256) | 基础费用阈值 +gas_fee_cap | varchar(256) | +created_at | datetime | 创建时间 +updated_at | datetime | 更新时间 \ No newline at end of file diff --git a/go.mod b/go.mod index c58152e4..85a54a32 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.1.5 github.com/filecoin-project/go-state-types v0.9.8 github.com/filecoin-project/specs-actors/v5 v5.0.6 - github.com/filecoin-project/venus v1.9.0 + github.com/filecoin-project/venus v1.9.0-rc1.0.20230105032048-c8d0f0e9a9b7 github.com/filecoin-project/venus-auth v1.9.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 @@ -103,7 +103,7 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru v0.5.4 github.com/hashicorp/hcl v1.0.0 // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/influxdata/influxdb-client-go/v2 v2.2.2 // indirect @@ -233,7 +233,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/hraban/lrucache v0.0.0-20201130153820-17052bf09781 // indirect github.com/ipfs/go-block-format v0.0.3 // indirect - github.com/libp2p/go-libp2p-core v0.20.0 // indirect + github.com/libp2p/go-libp2p-core v0.20.1 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index 65a13fd2..686012ce 100644 --- a/go.sum +++ b/go.sum @@ -418,8 +418,8 @@ github.com/filecoin-project/specs-storage v0.4.1/go.mod h1:Z2eK6uMwAOSLjek6+sy0j github.com/filecoin-project/storetheindex v0.3.5/go.mod h1:0r3d0kSpK63O6AvLr1CjAINLi+nWD49clzcnKV+GLpI= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= github.com/filecoin-project/venus v1.2.4/go.mod h1:hJULXHGAnWuq5S5KRtPkwbT8DqgM9II7NwyNU7t59D0= -github.com/filecoin-project/venus v1.9.0 h1:JdGPR7ank9XxlTmcaoXKKofqP+QnTmpiG5lEvJ5AixQ= -github.com/filecoin-project/venus v1.9.0/go.mod h1:3fBoP8nPxHYGerersp5m0J5hY0wsDGnWtolTEao/lwE= +github.com/filecoin-project/venus v1.9.0-rc1.0.20230105032048-c8d0f0e9a9b7 h1:PtTQo4scSMGo5kplvMZDVwRMpZ0V9IbJrgpn/IqVIp0= +github.com/filecoin-project/venus v1.9.0-rc1.0.20230105032048-c8d0f0e9a9b7/go.mod h1:BJn0TSnkDTX8R3duiCpgPKDYU5ybrPCgJlkxZi/wmTs= github.com/filecoin-project/venus-auth v1.3.2/go.mod h1:m5Jog2GYxztwP7w3m/iJdv/V1/bTcAVU9rm/CbhxRQU= github.com/filecoin-project/venus-auth v1.9.0 h1:GH0o/jPdF55/U/uLoMzrqR9+DOsMf5oWM/X4UPuyWPA= github.com/filecoin-project/venus-auth v1.9.0/go.mod h1:Ckj8F/iuSgXnCb9LvH0IiPR7swJZQAhabDOxVycLGWs= @@ -1175,8 +1175,8 @@ github.com/libp2p/go-libp2p-core v0.11.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQR github.com/libp2p/go-libp2p-core v0.12.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= github.com/libp2p/go-libp2p-core v0.13.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= github.com/libp2p/go-libp2p-core v0.14.0/go.mod h1:tLasfcVdTXnixsLB0QYaT1syJOhsbrhG7q6pGrHtBg8= -github.com/libp2p/go-libp2p-core v0.20.0 h1:PGKM74+T+O/FaZNARNW32i90RMBHCcgd/hkum2UQ5eY= -github.com/libp2p/go-libp2p-core v0.20.0/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY= +github.com/libp2p/go-libp2p-core v0.20.1 h1:fQz4BJyIFmSZAiTbKV8qoYhEH5Dtv/cVhZbG3Ib/+Cw= +github.com/libp2p/go-libp2p-core v0.20.1/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY= github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= diff --git a/integration_test/actor_cfg_test.go b/integration_test/actor_cfg_test.go new file mode 100644 index 00000000..966f2d03 --- /dev/null +++ b/integration_test/actor_cfg_test.go @@ -0,0 +1,127 @@ +package integration + +import ( + "context" + "testing" + "time" + + types2 "github.com/filecoin-project/venus/venus-shared/types" + + "github.com/filecoin-project/venus/venus-shared/testutil" + + "github.com/filecoin-project/venus/venus-shared/api/messager" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/venus-messager/config" + "github.com/filecoin-project/venus-messager/testhelper" + types "github.com/filecoin-project/venus/venus-shared/types/messager" + "github.com/stretchr/testify/assert" +) + +func TestActorCfgAPI(t *testing.T) { + ctx := context.Background() + cfg := config.DefaultConfig() + cfg.API.Address = "/ip4/0.0.0.0/tcp/0" + cfg.MessageService.SkipPushMessage = true + cfg.MessageService.WaitingChainHeadStableDuration = 2 * time.Second + authClient := testhelper.NewMockAuthClient() + ms, err := mockMessagerServer(ctx, t.TempDir(), cfg, authClient) + assert.NoError(t, err) + + go ms.start(ctx) + assert.NoError(t, <-ms.appStartErr) + + account := defaultLocalToken + addrCount := 10 + addrs := testhelper.RandAddresses(t, addrCount) + authClient.AddMockUserAndSigner(account, addrs) + assert.NoError(t, ms.walletCli.AddAddress(account, addrs)) + + api, closer, err := newMessagerClient(ctx, ms.port, ms.token) + assert.NoError(t, err) + defer closer() + + allAddrs := make([]address.Address, 0, len(addrs)) + for _, addr := range addrs { + allAddrs = append(allAddrs, testhelper.ResolveAddr(t, addr)) + } + + actorCfgs := make([]*types.ActorCfg, 5) + testutil.Provide(t, &actorCfgs) + + t.Run("test save actor config", func(t *testing.T) { + testCreateActorCfg(ctx, t, api, actorCfgs) + }) + + t.Run("test list actor config", func(t *testing.T) { + testListActorCfg(ctx, t, api, actorCfgs) + }) + + t.Run("test get actor config", func(t *testing.T) { + testGetActorCfg(ctx, t, api, actorCfgs) + }) + + t.Run("test update actor config", func(t *testing.T) { + testUpdateActorCfg(ctx, t, api, actorCfgs) + }) + + assert.NoError(t, ms.stop(ctx)) +} + +func testCreateActorCfg(ctx context.Context, t *testing.T, api messager.IMessager, actorCfgs []*types.ActorCfg) { + for _, actorCfg := range actorCfgs { + assert.NoError(t, api.SaveActorCfg(ctx, actorCfg)) + } +} + +func testListActorCfg(ctx context.Context, t *testing.T, api messager.IMessager, actorCfgs []*types.ActorCfg) { + listResult, err := api.ListActorCfg(ctx) + assert.NoError(t, err) + assert.Len(t, listResult, len(actorCfgs)) + + expect := map[types2.UUID]*types.ActorCfg{} + for _, actorCfg := range actorCfgs { + expect[actorCfg.ID] = actorCfg + } + + for _, r := range listResult { + testhelper.Equal(t, expect[r.ID], r) + } +} + +func testGetActorCfg(ctx context.Context, t *testing.T, api messager.IMessager, actorCfgs []*types.ActorCfg) { + expect := map[types2.UUID]*types.ActorCfg{} + for _, actorCfg := range actorCfgs { + expect[actorCfg.ID] = actorCfg + } + + for _, actorCfg := range actorCfgs { + getResult, err := api.GetActorCfgByID(ctx, actorCfg.ID) + assert.NoError(t, err) + testhelper.Equal(t, expect[getResult.ID], getResult) + } +} + +func testUpdateActorCfg(ctx context.Context, t *testing.T, api messager.IMessager, actorCfgs []*types.ActorCfg) { + expect := map[types2.UUID]*types.ActorCfg{} + for _, actorCfg := range actorCfgs { + expect[actorCfg.ID] = actorCfg + } + + for _, actorCfg := range actorCfgs { + changeParams := &types.ChangeGasSpecParams{} + testutil.Provide(t, changeParams) + err := api.UpdateActorCfg(ctx, actorCfg.ID, changeParams) + assert.NoError(t, err) + + updatedR, err := api.GetActorCfgByID(ctx, actorCfg.ID) + assert.NoError(t, err) + assert.Equal(t, updatedR.FeeSpec, types.FeeSpec{ + GasOverEstimation: *changeParams.GasOverEstimation, + MaxFee: changeParams.MaxFee, + GasFeeCap: changeParams.GasFeeCap, + GasOverPremium: *changeParams.GasOverPremium, + BaseFee: changeParams.BaseFee, + }) + } +} diff --git a/integration_test/shared_params_test.go b/integration_test/shared_params_test.go index 10b3e5ed..1d4ae6aa 100644 --- a/integration_test/shared_params_test.go +++ b/integration_test/shared_params_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/venus-auth/jwtclient" types "github.com/filecoin-project/venus/venus-shared/types/messager" @@ -37,13 +36,15 @@ func TestSharedParamsAPI(t *testing.T) { assert.Equal(t, service.DefSharedParams, res) params := &types.SharedSpec{ - ID: 1, - GasOverEstimation: 10, - MaxFee: big.NewInt(11111111), - GasFeeCap: big.NewInt(11111112), - BaseFee: big.NewInt(10000), - GasOverPremium: 10, - SelMsgNum: 100, + ID: 1, + SelMsgNum: 100, + FeeSpec: types.FeeSpec{ + GasOverEstimation: 10, + MaxFee: big.NewInt(11111111), + GasFeeCap: big.NewInt(11111112), + BaseFee: big.NewInt(10000), + GasOverPremium: 10, + }, } assert.NoError(t, api.SetSharedParams(ctx, params)) diff --git a/main.go b/main.go index 8918823c..3177ee1d 100644 --- a/main.go +++ b/main.go @@ -51,6 +51,7 @@ func main() { ccli.MsgCmds, ccli.AddrCmds, ccli.SharedParamsCmds, + ccli.ActorCfgCmds, ccli.NodeCmds, ccli.LogCmds, ccli.SendCmd, diff --git a/models/mtypes/db_cid.go b/models/mtypes/db_cid.go new file mode 100644 index 00000000..13256dd4 --- /dev/null +++ b/models/mtypes/db_cid.go @@ -0,0 +1,63 @@ +package mtypes + +import ( + "database/sql/driver" + "errors" + + "github.com/ipfs/go-cid" +) + +type DBCid cid.Cid + +var UndefDBCid = DBCid{} + +func NewDBCid(id cid.Cid) DBCid { + return DBCid(id) +} + +func (c *DBCid) Scan(value interface{}) error { + var val string + switch value := value.(type) { + case []byte: + val = string(value) + case string: + val = value + default: + return errors.New("address should be a `[]byte` or `string`") + } + + if len(val) == 0 { + *c = UndefDBCid + return nil + } + cid, err := cid.Decode(val) + if err != nil { + return err + } + *c = DBCid(cid) + + return nil +} + +func (c DBCid) Value() (driver.Value, error) { + return c.String(), nil +} + +func (c DBCid) String() string { + if c == UndefDBCid { + return "" + } + return cid.Cid(c).String() +} + +func (c DBCid) Cid() cid.Cid { + return cid.Cid(c) +} + +func (c DBCid) cidPtr() *cid.Cid { + if c == UndefDBCid { + return nil + } + cid := cid.Cid(c) + return &cid +} diff --git a/models/mysql/actor_cfg.go b/models/mysql/actor_cfg.go new file mode 100644 index 00000000..b3f9b895 --- /dev/null +++ b/models/mysql/actor_cfg.go @@ -0,0 +1,155 @@ +package mysql + +import ( + "context" + "time" + + "github.com/filecoin-project/go-state-types/actors" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/venus-messager/models/mtypes" + + shared "github.com/filecoin-project/venus/venus-shared/types" + "gorm.io/gorm" + + "github.com/filecoin-project/venus-messager/models/repo" + types "github.com/filecoin-project/venus/venus-shared/types/messager" +) + +type mysqlActorCfg struct { + ID shared.UUID `gorm:"column:id;type:varchar(256);primary_key;"` // 主键 + ActorVersion int `gorm:"column:actor_v;type:unsigned int;NOT NULL"` + Code mtypes.DBCid `gorm:"column:code;type:varchar(256);index:idx_code_method,unique;NOT NULL;"` + Method uint64 `gorm:"column:method;type:unsigned bigint;index:idx_code_method,unique;NOT NULL"` + + FeeSpec + + CreatedAt time.Time `gorm:"column:created_at;index;NOT NULL"` // 创建时间 + UpdatedAt time.Time `gorm:"column:updated_at;index;NOT NULL"` // 更新时间 +} + +func fromActorCfg(actorCfg *types.ActorCfg) *mysqlActorCfg { + return &mysqlActorCfg{ + ID: actorCfg.ID, + ActorVersion: int(actorCfg.ActorVersion), + Code: mtypes.NewDBCid(actorCfg.Code), + Method: uint64(actorCfg.Method), + FeeSpec: FeeSpec{ + GasOverEstimation: actorCfg.GasOverEstimation, + GasOverPremium: actorCfg.GasOverPremium, + MaxFee: mtypes.SafeFromGo(actorCfg.MaxFee.Int), + GasFeeCap: mtypes.SafeFromGo(actorCfg.GasFeeCap.Int), + BaseFee: mtypes.SafeFromGo(actorCfg.BaseFee.Int), + }, + CreatedAt: actorCfg.CreatedAt, + UpdatedAt: actorCfg.UpdatedAt, + } +} + +func (mysqlActorCfg mysqlActorCfg) ActorCfg() *types.ActorCfg { + return &types.ActorCfg{ + ID: mysqlActorCfg.ID, + ActorVersion: actors.Version(mysqlActorCfg.ActorVersion), + MethodType: types.MethodType{ + Code: mysqlActorCfg.Code.Cid(), + Method: abi.MethodNum(mysqlActorCfg.Method), + }, + FeeSpec: types.FeeSpec{ + GasOverEstimation: mysqlActorCfg.GasOverEstimation, + GasOverPremium: mysqlActorCfg.GasOverPremium, + MaxFee: big.Int(mtypes.SafeFromGo(mysqlActorCfg.MaxFee.Int)), + GasFeeCap: big.Int(mtypes.SafeFromGo(mysqlActorCfg.GasFeeCap.Int)), + BaseFee: big.Int(mtypes.SafeFromGo(mysqlActorCfg.BaseFee.Int)), + }, + CreatedAt: mysqlActorCfg.CreatedAt, + UpdatedAt: mysqlActorCfg.UpdatedAt, + } +} + +func (mysqlActorCfg mysqlActorCfg) TableName() string { + return "actor_cfg" +} + +var _ repo.ActorCfgRepo = (*mysqlActorCfgRepo)(nil) + +type mysqlActorCfgRepo struct { + *gorm.DB +} + +func newMysqlActorCfgRepo(db *gorm.DB) *mysqlActorCfgRepo { + return &mysqlActorCfgRepo{DB: db} +} + +func (s *mysqlActorCfgRepo) SaveActorCfg(ctx context.Context, actorCfg *types.ActorCfg) error { + return s.DB.Save(fromActorCfg(actorCfg)).Error +} + +func (s *mysqlActorCfgRepo) GetActorCfgByMethodType(ctx context.Context, methodType *types.MethodType) (*types.ActorCfg, error) { + var a mysqlActorCfg + if err := s.DB.Take(&a, "code = ? and method = ?", methodType.Code.String(), methodType.Method).Error; err != nil { + return nil, err + } + + return a.ActorCfg(), nil +} + +func (s *mysqlActorCfgRepo) GetActorCfgByID(ctx context.Context, id shared.UUID) (*types.ActorCfg, error) { + var a mysqlActorCfg + if err := s.DB.Take(&a, "id = ?", id).Error; err != nil { + return nil, err + } + + return a.ActorCfg(), nil +} + +func (s *mysqlActorCfgRepo) ListActorCfg(ctx context.Context) ([]*types.ActorCfg, error) { + var list []*mysqlActorCfg + if err := s.DB.Find(&list).Error; err != nil { + return nil, err + } + + result := make([]*types.ActorCfg, len(list)) + for index, r := range list { + result[index] = r.ActorCfg() + } + + return result, nil +} + +func (s *mysqlActorCfgRepo) DelActorCfgByMethodType(ctx context.Context, methodType *types.MethodType) error { + return s.DB.Delete(mysqlActorCfg{}, "code = ? and method = ?", methodType.Code.String(), methodType.Method).Error +} + +func (s *mysqlActorCfgRepo) DelActorCfgById(ctx context.Context, id shared.UUID) error { + return s.DB.Delete(mysqlActorCfg{}, "id = ?", id).Error +} + +func (s *mysqlActorCfgRepo) UpdateSelectSpecById(ctx context.Context, id shared.UUID, spec *types.ChangeGasSpecParams) error { + updateColumns := make(map[string]interface{}, 6) + if !spec.GasFeeCap.Nil() { + updateColumns["gas_fee_cap"] = spec.GasFeeCap.String() + } + if !spec.BaseFee.Nil() { + updateColumns["base_fee"] = spec.BaseFee.String() + } + if !spec.MaxFee.Nil() { + updateColumns["max_fee"] = spec.MaxFee.String() + } + + if spec.GasOverEstimation != nil { + updateColumns["gas_over_estimation"] = *spec.GasOverEstimation + } + if spec.GasOverPremium != nil { + updateColumns["gas_over_premium"] = *spec.GasOverPremium + } + + if len(updateColumns) == 0 { + return nil + } + + updateColumns["updated_at"] = time.Now() + + return s.DB.Model((*mysqlActorCfg)(nil)).Where("id = ?", id).UpdateColumns(updateColumns).Error +} diff --git a/models/mysql/actor_cfg_test.go b/models/mysql/actor_cfg_test.go new file mode 100644 index 00000000..765921c0 --- /dev/null +++ b/models/mysql/actor_cfg_test.go @@ -0,0 +1,221 @@ +package mysql + +import ( + "context" + "regexp" + "testing" + + "github.com/filecoin-project/venus-messager/models/mtypes" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/filecoin-project/venus-messager/models/repo" + "github.com/filecoin-project/venus/venus-shared/testutil" + types "github.com/filecoin-project/venus/venus-shared/types/messager" + "gorm.io/gorm" + + "github.com/stretchr/testify/assert" +) + +func Test_mysqlActorCfgRepo_SaveActorCfg(t *testing.T) { + r, mock, sqlDB := setup(t) + t.Run("mysql test save actor config", wrapper(testSaveActorCfg, r, mock)) + t.Run("mysql test get actor config by id", wrapper(testGetActorTypeById, r, mock)) + t.Run("mysql test get actor config by method type", wrapper(testGetActorTypeByMethodType, r, mock)) + t.Run("mysql test list actor config by id", wrapper(testListActorType, r, mock)) + t.Run("mysql test delete actor config by method types", wrapper(testDeleteActorCfgByMethodType, r, mock)) + t.Run("mysql test delete actor config by id", wrapper(testDeleteActorCfgById, r, mock)) + t.Run("mysql test update actor config", wrapper(testUpdateSelectSpec, r, mock)) + assert.NoError(t, closeDB(mock, sqlDB)) +} + +func testSaveActorCfg(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { + ctx := context.Background() + + var actorCfg types.ActorCfg + testutil.Provide(t, &actorCfg) + + mysqlActorCfg := fromActorCfg(&actorCfg) + updateSQL, updateArgs := genUpdateSQL(mysqlActorCfg, false) + updateArgs = append(updateArgs, mysqlActorCfg.ID) + + mock.ExpectBegin() + mock.ExpectExec(regexp.QuoteMeta(updateSQL)). + WithArgs(updateArgs...). + WillReturnResult(sqlmock.NewResult(0, 0)) + mock.ExpectCommit() + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `actor_cfg` WHERE `id` = ? ORDER BY `actor_cfg`.`id` LIMIT 1")). + WithArgs(mysqlActorCfg.ID). + WillReturnError(gorm.ErrRecordNotFound) + + insertSql, insertArgs := genInsertSQL(mysqlActorCfg) + mock.ExpectBegin() + mock.ExpectExec(regexp.QuoteMeta(insertSql)). + WithArgs(insertArgs...). + WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + + assert.Nil(t, r.ActorCfgRepo().SaveActorCfg(ctx, &actorCfg)) +} + +func testGetActorTypeById(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { + ctx := context.Background() + var actorCfg types.ActorCfg + testutil.Provide(t, &actorCfg) + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `actor_cfg` WHERE id = ? LIMIT 1")). + WithArgs(actorCfg.ID). + WillReturnRows(sqlmock.NewRows([]string{"id"})) + + _, err := r.ActorCfgRepo().GetActorCfgByID(ctx, actorCfg.ID) + assert.Equal(t, repo.ErrRecordNotFound, err) + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `actor_cfg` WHERE id = ? LIMIT 1")). + WithArgs(actorCfg.ID). + WillReturnRows(genSelectResult(fromActorCfg(&actorCfg))) + + actorCfgR, err := r.ActorCfgRepo().GetActorCfgByID(ctx, actorCfg.ID) + assert.NoError(t, err) + assert.Equal(t, actorCfg, *actorCfgR) +} + +func testGetActorTypeByMethodType(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { + ctx := context.Background() + var actorCfg types.ActorCfg + testutil.Provide(t, &actorCfg) + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `actor_cfg` WHERE code = ? and method = ? LIMIT 1")). + WithArgs(mtypes.NewDBCid(actorCfg.Code), actorCfg.Method). + WillReturnRows(sqlmock.NewRows([]string{"id"})) + + _, err := r.ActorCfgRepo().GetActorCfgByMethodType(ctx, &types.MethodType{ + Code: actorCfg.Code, + Method: actorCfg.Method, + }) + assert.Equal(t, repo.ErrRecordNotFound, err) + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `actor_cfg` WHERE code = ? and method = ? LIMIT 1")). + WithArgs(mtypes.NewDBCid(actorCfg.Code), actorCfg.Method). + WillReturnRows(genSelectResult(fromActorCfg(&actorCfg))) + + actorCfgR, err := r.ActorCfgRepo().GetActorCfgByMethodType(ctx, &types.MethodType{ + Code: actorCfg.Code, + Method: actorCfg.Method, + }) + assert.NoError(t, err) + assert.Equal(t, actorCfg, *actorCfgR) +} + +func testListActorType(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { + ctx := context.Background() + actorCfgs := make([]*types.ActorCfg, 10) + testutil.Provide(t, &actorCfgs) + + actorMysqlCfgs := make([]*mysqlActorCfg, len(actorCfgs)) + for index, actorCfg := range actorCfgs { + actorMysqlCfgs[index] = fromActorCfg(actorCfg) + } + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `actor_cfg`")). + WithArgs(). + WillReturnRows(genSelectResult(actorMysqlCfgs)) + + val, err := r.ActorCfgRepo().ListActorCfg(ctx) + assert.NoError(t, err) + assertActorCfgArrValue(t, actorCfgs, val) +} + +func assertActorCfgValue(t *testing.T, expectVal, actualVal *types.ActorCfg) { + assert.Equal(t, expectVal.ID, actualVal.ID) + assert.Equal(t, expectVal.ActorVersion, actualVal.ActorVersion) + assert.Equal(t, expectVal.MethodType, actualVal.MethodType) + assert.Equal(t, expectVal.FeeSpec, actualVal.FeeSpec) +} + +func assertActorCfgArrValue(t *testing.T, expectVal, actualVal []*types.ActorCfg) { + assert.Equal(t, len(expectVal), len(actualVal)) + + for index, val := range expectVal { + assertActorCfgValue(t, val, actualVal[index]) + } +} + +func testDeleteActorCfgByMethodType(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { + ctx := context.Background() + var actorCfg types.ActorCfg + testutil.Provide(t, &actorCfg) + + mock.ExpectBegin() + mock.ExpectExec(regexp.QuoteMeta("DELETE FROM `actor_cfg` WHERE code = ? and method = ?")). + WithArgs(mtypes.NewDBCid(actorCfg.Code), actorCfg.Method). + WillReturnResult(driverResult{0, 1}) + mock.ExpectCommit() + + err := r.ActorCfgRepo().DelActorCfgByMethodType(ctx, &types.MethodType{ + Code: actorCfg.Code, + Method: actorCfg.Method, + }) + assert.NoError(t, err) +} + +func testDeleteActorCfgById(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { + ctx := context.Background() + var actorCfg types.ActorCfg + testutil.Provide(t, &actorCfg) + + mock.ExpectBegin() + mock.ExpectExec(regexp.QuoteMeta("DELETE FROM `actor_cfg` WHERE id = ?")). + WithArgs(actorCfg.ID). + WillReturnResult(driverResult{0, 1}) + mock.ExpectCommit() + + err := r.ActorCfgRepo().DelActorCfgById(ctx, actorCfg.ID) + assert.NoError(t, err) +} + +func testUpdateSelectSpec(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { + ctx := context.Background() + var actorCfg types.ActorCfg + testutil.Provide(t, &actorCfg) + + mock.ExpectBegin() + mock.ExpectExec(regexp.QuoteMeta("UPDATE `actor_cfg` SET `base_fee`=?,`gas_fee_cap`=?,`gas_over_estimation`=?,`gas_over_premium`=?,`max_fee`=?,`updated_at`=? WHERE id = ?")). + WithArgs(actorCfg.BaseFee.String(), actorCfg.GasFeeCap.String(), actorCfg.GasOverEstimation, actorCfg.GasOverPremium, actorCfg.MaxFee.String(), anyTime{}, actorCfg.ID). + WillReturnResult(driverResult{0, 1}) + mock.ExpectCommit() + + err := r.ActorCfgRepo().UpdateSelectSpecById(ctx, actorCfg.ID, + &types.ChangeGasSpecParams{ + GasOverEstimation: &actorCfg.GasOverEstimation, + MaxFee: actorCfg.MaxFee, + GasFeeCap: actorCfg.GasFeeCap, + GasOverPremium: &actorCfg.GasOverPremium, + BaseFee: actorCfg.BaseFee, + }) + assert.NoError(t, err) + + //only update select num + mock.ExpectBegin() + mock.ExpectExec(regexp.QuoteMeta("UPDATE `actor_cfg` SET `gas_over_premium`=?,`updated_at`=? WHERE id = ?")). + WithArgs(actorCfg.GasOverPremium, anyTime{}, actorCfg.ID). + WillReturnResult(driverResult{0, 1}) + mock.ExpectCommit() + + err = r.ActorCfgRepo().UpdateSelectSpecById(ctx, actorCfg.ID, + &types.ChangeGasSpecParams{ + GasOverPremium: &actorCfg.GasOverPremium, + }) + assert.NoError(t, err) + + //only update max fee + mock.ExpectBegin() + mock.ExpectExec(regexp.QuoteMeta("UPDATE `actor_cfg` SET `max_fee`=?,`updated_at`=? WHERE id = ?")). + WithArgs(actorCfg.MaxFee.String(), anyTime{}, actorCfg.ID). + WillReturnResult(driverResult{0, 1}) + mock.ExpectCommit() + + err = r.ActorCfgRepo().UpdateSelectSpecById(ctx, actorCfg.ID, + &types.ChangeGasSpecParams{ + MaxFee: actorCfg.MaxFee, + }) + assert.NoError(t, err) +} diff --git a/models/mysql/address.go b/models/mysql/address.go index 186668bb..98cf1e25 100644 --- a/models/mysql/address.go +++ b/models/mysql/address.go @@ -15,17 +15,14 @@ import ( ) type mysqlAddress struct { - ID shared.UUID `gorm:"column:id;type:varchar(256);primary_key"` - Addr string `gorm:"column:addr;type:varchar(256);uniqueIndex;NOT NULL"` - Nonce uint64 `gorm:"column:nonce;type:bigint unsigned;index;NOT NULL"` - Weight int64 `gorm:"column:weight;type:bigint;index;NOT NULL"` - SelMsgNum uint64 `gorm:"column:sel_msg_num;type:bigint unsigned;NOT NULL"` - State types.AddressState `gorm:"column:state;type:int;index;default:1"` - GasOverEstimation float64 `gorm:"column:gas_over_estimation;type:decimal(10,2);NOT NULL"` - MaxFee mtypes.Int `gorm:"column:max_fee;type:varchar(256);default:0"` - GasFeeCap mtypes.Int `gorm:"column:gas_fee_cap;type:varchar(256);default:0"` - GasOverPremium float64 `gorm:"column:gas_over_premium;type:decimal(10,2);NOT NULL"` - BaseFee mtypes.Int `gorm:"column:base_fee;type:varchar(256);default:0"` + ID shared.UUID `gorm:"column:id;type:varchar(256);primary_key"` + Addr string `gorm:"column:addr;type:varchar(256);uniqueIndex;NOT NULL"` + Nonce uint64 `gorm:"column:nonce;type:bigint unsigned;index;NOT NULL"` + Weight int64 `gorm:"column:weight;type:bigint;index;NOT NULL"` + State types.AddressState `gorm:"column:state;type:int;index;default:1"` + SelMsgNum uint64 `gorm:"column:sel_msg_num;type:bigint unsigned;NOT NULL"` + + FeeSpec IsDeleted int `gorm:"column:is_deleted;index;default:-1;NOT NULL"` // 是否删除 1:是 -1:否 CreatedAt time.Time `gorm:"column:created_at;index;NOT NULL"` // 创建时间 @@ -38,20 +35,22 @@ func (s mysqlAddress) TableName() string { func fromAddress(addr *types.Address) *mysqlAddress { return &mysqlAddress{ - ID: addr.ID, - Addr: addr.Addr.String(), - Nonce: addr.Nonce, - Weight: addr.Weight, - SelMsgNum: addr.SelMsgNum, - State: addr.State, - GasOverEstimation: addr.GasOverEstimation, - GasOverPremium: addr.GasOverPremium, - MaxFee: mtypes.SafeFromGo(addr.MaxFee.Int), - GasFeeCap: mtypes.SafeFromGo(addr.GasFeeCap.Int), - BaseFee: mtypes.SafeFromGo(addr.BaseFee.Int), - IsDeleted: addr.IsDeleted, - CreatedAt: addr.CreatedAt, - UpdatedAt: addr.UpdatedAt, + ID: addr.ID, + Addr: addr.Addr.String(), + Nonce: addr.Nonce, + Weight: addr.Weight, + State: addr.State, + SelMsgNum: addr.SelMsgNum, + FeeSpec: FeeSpec{ + GasOverEstimation: addr.GasOverEstimation, + GasOverPremium: addr.GasOverPremium, + MaxFee: mtypes.SafeFromGo(addr.MaxFee.Int), + GasFeeCap: mtypes.SafeFromGo(addr.GasFeeCap.Int), + BaseFee: mtypes.SafeFromGo(addr.BaseFee.Int), + }, + IsDeleted: addr.IsDeleted, + CreatedAt: addr.CreatedAt, + UpdatedAt: addr.UpdatedAt, } } @@ -61,20 +60,22 @@ func (s mysqlAddress) Address() (*types.Address, error) { return nil, err } return &types.Address{ - ID: s.ID, - Addr: addr, - Nonce: s.Nonce, - Weight: s.Weight, - SelMsgNum: s.SelMsgNum, - State: s.State, - MaxFee: big.Int(mtypes.SafeFromGo(s.MaxFee.Int)), - GasFeeCap: big.Int(mtypes.SafeFromGo(s.GasFeeCap.Int)), - BaseFee: big.Int(mtypes.SafeFromGo(s.BaseFee.Int)), - GasOverPremium: s.GasOverPremium, - GasOverEstimation: s.GasOverEstimation, - IsDeleted: s.IsDeleted, - CreatedAt: s.CreatedAt, - UpdatedAt: s.UpdatedAt, + ID: s.ID, + Addr: addr, + Nonce: s.Nonce, + Weight: s.Weight, + State: s.State, + SelMsgNum: s.SelMsgNum, + FeeSpec: types.FeeSpec{ + GasOverEstimation: s.GasOverEstimation, + GasOverPremium: s.GasOverPremium, + MaxFee: big.Int(mtypes.SafeFromGo(s.MaxFee.Int)), + GasFeeCap: big.Int(mtypes.SafeFromGo(s.GasFeeCap.Int)), + BaseFee: big.Int(mtypes.SafeFromGo(s.BaseFee.Int)), + }, + IsDeleted: s.IsDeleted, + CreatedAt: s.CreatedAt, + UpdatedAt: s.UpdatedAt, }, nil } diff --git a/models/mysql/address_test.go b/models/mysql/address_test.go index 01e86ced..7fa4749e 100644 --- a/models/mysql/address_test.go +++ b/models/mysql/address_test.go @@ -285,21 +285,23 @@ func testUpdateFeeParams(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { func newAddressInfo(t *testing.T) (*types.Address, error) { randNum := rand.Int63n(1000) return &types.Address{ - ID: venustypes.NewUUID(), - Addr: testutil.AddressProvider()(t), - Nonce: uint64(randNum), - Weight: randNum, - SelMsgNum: uint64(randNum), + ID: venustypes.NewUUID(), + Addr: testutil.AddressProvider()(t), + Nonce: uint64(randNum), + Weight: randNum, // Any zero value like 0, '', false won’t be saved into the database for those fields defined default value, // you might want to use pointer type or Scanner/Valuer to avoid this. - State: types.AddressState(rand.Intn(4) + 1), - GasOverEstimation: float64(randNum), - GasOverPremium: float64(randNum), - MaxFee: big.NewInt(randNum), - GasFeeCap: big.NewInt(randNum), - BaseFee: big.NewInt(randNum), - IsDeleted: repo.NotDeleted, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), + State: types.AddressState(rand.Intn(4) + 1), + SelMsgNum: uint64(randNum), + FeeSpec: types.FeeSpec{ + GasOverEstimation: float64(randNum), + GasOverPremium: float64(randNum), + MaxFee: big.NewInt(randNum), + GasFeeCap: big.NewInt(randNum), + BaseFee: big.NewInt(randNum), + }, + IsDeleted: repo.NotDeleted, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), }, nil } diff --git a/models/mysql/db.go b/models/mysql/db.go index 196ac360..4dcd9476 100644 --- a/models/mysql/db.go +++ b/models/mysql/db.go @@ -15,6 +15,10 @@ type Repo struct { *gorm.DB } +func (d Repo) ActorCfgRepo() repo.ActorCfgRepo { + return newMysqlActorCfgRepo(d.DB) +} + func (d Repo) MessageRepo() repo.MessageRepo { return newMysqlMessageRepo(d.DB) } @@ -32,20 +36,7 @@ func (d Repo) NodeRepo() repo.NodeRepo { } func (d Repo) AutoMigrate() error { - err := d.GetDb().AutoMigrate(mysqlMessage{}) - if err != nil { - return err - } - - if err := d.GetDb().AutoMigrate(mysqlAddress{}); err != nil { - return err - } - - if err := d.GetDb().AutoMigrate(mysqlSharedParams{}); err != nil { - return err - } - - return d.GetDb().AutoMigrate(mysqlNode{}) + return d.GetDb().AutoMigrate(mysqlActorCfg{}, mysqlMessage{}, mysqlAddress{}, mysqlSharedParams{}, mysqlNode{}) } func (d Repo) GetDb() *gorm.DB { @@ -71,6 +62,18 @@ type TxMysqlRepo struct { *gorm.DB } +func (t *TxMysqlRepo) ActorCfgRepo() repo.ActorCfgRepo { + return newMysqlActorCfgRepo(t.DB) +} + +func (t *TxMysqlRepo) SharedParamsRepo() repo.SharedParamsRepo { + return newMysqlSharedParamsRepo(t.DB) +} + +func (t *TxMysqlRepo) NodeRepo() repo.NodeRepo { + return newMysqlNodeRepo(t.DB) +} + func (t *TxMysqlRepo) MessageRepo() repo.MessageRepo { return newMysqlMessageRepo(t.DB) } diff --git a/models/mysql/shared_params.go b/models/mysql/shared_params.go index 826b96d6..b0341f18 100644 --- a/models/mysql/shared_params.go +++ b/models/mysql/shared_params.go @@ -13,37 +13,36 @@ import ( ) type mysqlSharedParams struct { - ID uint `gorm:"primary_key;column:id;type:SMALLINT(2) unsigned AUTO_INCREMENT;NOT NULL"` - - GasOverEstimation float64 `gorm:"column:gas_over_estimation;type:DOUBLE;NOT NULL"` - MaxFee mtypes.Int `gorm:"column:max_fee;type:varchar(256);NOT NULL;default:0"` - GasFeeCap mtypes.Int `gorm:"column:gas_fee_cap;type:varchar(256);NOT NULL;default:0"` - GasOverPremium float64 `gorm:"column:gas_over_premium;type:DOUBLE;NOT NULL;default:0"` - SelMsgNum uint64 `gorm:"column:sel_msg_num;type:BIGINT(20) UNSIGNED;NOT NULL"` - BaseFee mtypes.Int `gorm:"column:base_fee;type:varchar(256);default:0"` + ID uint `gorm:"primary_key;column:id;type:SMALLINT(2) unsigned AUTO_INCREMENT;NOT NULL"` + SelMsgNum uint64 `gorm:"column:sel_msg_num;type:bigint unsigned;NOT NULL"` + FeeSpec } func fromSharedParams(sp types.SharedSpec) *mysqlSharedParams { return &mysqlSharedParams{ - ID: sp.ID, - GasOverEstimation: sp.GasOverEstimation, - MaxFee: mtypes.SafeFromGo(sp.MaxFee.Int), - GasFeeCap: mtypes.SafeFromGo(sp.GasFeeCap.Int), - BaseFee: mtypes.SafeFromGo(sp.BaseFee.Int), - GasOverPremium: sp.GasOverPremium, - SelMsgNum: sp.SelMsgNum, + ID: sp.ID, + SelMsgNum: sp.SelMsgNum, + FeeSpec: FeeSpec{ + BaseFee: mtypes.SafeFromGo(sp.BaseFee.Int), + GasOverEstimation: sp.GasOverEstimation, + MaxFee: mtypes.SafeFromGo(sp.MaxFee.Int), + GasFeeCap: mtypes.SafeFromGo(sp.GasFeeCap.Int), + GasOverPremium: sp.GasOverPremium, + }, } } func (ssp mysqlSharedParams) SharedParams() *types.SharedSpec { return &types.SharedSpec{ - ID: ssp.ID, - GasOverEstimation: ssp.GasOverEstimation, - MaxFee: big.Int(mtypes.SafeFromGo(ssp.MaxFee.Int)), - GasFeeCap: big.Int(mtypes.SafeFromGo(ssp.GasFeeCap.Int)), - BaseFee: big.Int(mtypes.SafeFromGo(ssp.BaseFee.Int)), - GasOverPremium: ssp.GasOverPremium, - SelMsgNum: ssp.SelMsgNum, + ID: ssp.ID, + SelMsgNum: ssp.SelMsgNum, + FeeSpec: types.FeeSpec{ + GasOverEstimation: ssp.GasOverEstimation, + MaxFee: big.Int(mtypes.SafeFromGo(ssp.MaxFee.Int)), + GasFeeCap: big.Int(mtypes.SafeFromGo(ssp.GasFeeCap.Int)), + BaseFee: big.Int(mtypes.SafeFromGo(ssp.BaseFee.Int)), + GasOverPremium: ssp.GasOverPremium, + }, } } diff --git a/models/mysql/shared_params_test.go b/models/mysql/shared_params_test.go index 0367d49d..7e340d39 100644 --- a/models/mysql/shared_params_test.go +++ b/models/mysql/shared_params_test.go @@ -40,13 +40,15 @@ func testGetSharedParams(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { func testSetSharedParams(t *testing.T, r repo.Repo, mock sqlmock.Sqlmock) { ctx := context.Background() params := &types.SharedSpec{ - ID: 1, - GasOverEstimation: 1.25, - MaxFee: big.NewInt(100), - GasFeeCap: big.NewInt(1000), - BaseFee: big.NewInt(1001), - GasOverPremium: 4.4, - SelMsgNum: 10, + ID: 1, + SelMsgNum: 10, + FeeSpec: types.FeeSpec{ + GasOverEstimation: 1.25, + MaxFee: big.NewInt(100), + GasFeeCap: big.NewInt(1000), + BaseFee: big.NewInt(1001), + GasOverPremium: 4.4, + }, } mysqlParams := fromSharedParams(*params) diff --git a/models/mysql/testing.go b/models/mysql/testing.go index acaa500c..c64e16ec 100644 --- a/models/mysql/testing.go +++ b/models/mysql/testing.go @@ -148,3 +148,74 @@ func genUpdateSQL(obj interface{}, skipZero bool, where ...string) (string, []dr } return buf.String(), updateArgs } + +func genSelectResult(obj interface{}, selColumns ...string) *sqlmock.Rows { + inCol := func(col string) bool { + if len(selColumns) == 0 { + return true + } + for _, selCol := range selColumns { + if selCol == col { + return true + } + } + return false + } + originT := reflect.TypeOf(obj) + originValue := reflect.ValueOf(obj) + targetT := originT + if originT.Kind() == reflect.Slice || originT.Kind() == reflect.Array { + targetT = originT.Elem() + if targetT.Kind() == reflect.Ptr { + targetT = targetT.Elem() + } + } + emptyVal := reflect.New(targetT).Interface() + fmt.Println(emptyVal) + objSchema, _ := schema.Parse(emptyVal, &sync.Map{}, db.NamingStrategy) + + var columns []string + for _, dbName := range objSchema.DBNames { + if !inCol(dbName) { + continue + } + columns = append(columns, dbName) + } + mockRows := sqlmock.NewRows(columns) + + getRow := func(val reflect.Value) []driver.Value { + var row []driver.Value + for _, colName := range columns { + field := objSchema.LookUpField(colName) + fieldV, _ := field.ValueOf(val) + row = append(row, fieldV) + } + return row + } + + if originT.Kind() == reflect.Slice || originT.Kind() == reflect.Array { + vLen := originValue.Len() + for i := 0; i < vLen; i++ { + oneRow := getRow(originValue.Index(i)) + mockRows.AddRow(oneRow...) + } + } else { + oneRow := getRow(originValue) + mockRows.AddRow(oneRow...) + } + return mockRows +} + +var _ driver.Result = (*driverResult)(nil) + +type driverResult struct { + lastInsertId, rowAffected int64 +} + +func (d driverResult) LastInsertId() (int64, error) { + return 0, nil +} + +func (d driverResult) RowsAffected() (int64, error) { + return 1, nil +} diff --git a/models/mysql/types.go b/models/mysql/types.go new file mode 100644 index 00000000..c26888b3 --- /dev/null +++ b/models/mysql/types.go @@ -0,0 +1,12 @@ +package mysql + +import "github.com/filecoin-project/venus-messager/models/mtypes" + +// FeeSpec just use in this package, do not use in others +type FeeSpec struct { + GasOverEstimation float64 `gorm:"column:gas_over_estimation;type:decimal(10,2);NOT NULL"` + MaxFee mtypes.Int `gorm:"column:max_fee;type:varchar(256);default:0"` + GasFeeCap mtypes.Int `gorm:"column:gas_fee_cap;type:varchar(256);default:0"` + GasOverPremium float64 `gorm:"column:gas_over_premium;type:decimal(10,2);NOT NULL"` + BaseFee mtypes.Int `gorm:"column:base_fee;type:varchar(256);default:0"` +} diff --git a/models/repo/actor_cfg_repo.go b/models/repo/actor_cfg_repo.go new file mode 100644 index 00000000..bf2fdf2c --- /dev/null +++ b/models/repo/actor_cfg_repo.go @@ -0,0 +1,18 @@ +package repo + +import ( + "context" + + shared "github.com/filecoin-project/venus/venus-shared/types" + types "github.com/filecoin-project/venus/venus-shared/types/messager" +) + +type ActorCfgRepo interface { + SaveActorCfg(ctx context.Context, address *types.ActorCfg) error + GetActorCfgByMethodType(ctx context.Context, methodType *types.MethodType) (*types.ActorCfg, error) + GetActorCfgByID(ctx context.Context, id shared.UUID) (*types.ActorCfg, error) + ListActorCfg(ctx context.Context) ([]*types.ActorCfg, error) + DelActorCfgByMethodType(ctx context.Context, addr *types.MethodType) error + DelActorCfgById(ctx context.Context, id shared.UUID) error + UpdateSelectSpecById(ctx context.Context, id shared.UUID, spec *types.ChangeGasSpecParams) error +} diff --git a/models/repo/repo.go b/models/repo/repo.go index 15d783c0..12f7293b 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -22,15 +22,15 @@ type Repo interface { DbClose() error AutoMigrate() error - MessageRepo() MessageRepo - AddressRepo() AddressRepo - SharedParamsRepo() SharedParamsRepo - NodeRepo() NodeRepo + TxRepo } type TxRepo interface { + ActorCfgRepo() ActorCfgRepo MessageRepo() MessageRepo AddressRepo() AddressRepo + SharedParamsRepo() SharedParamsRepo + NodeRepo() NodeRepo } type ISqlField interface { @@ -84,3 +84,5 @@ func FromMsgReceipt(receipt *types.MessageReceipt) *SqlMsgReceipt { s.ExitCode = receipt.ExitCode return &s } + +var ErrRecordNotFound = gorm.ErrRecordNotFound diff --git a/models/sqlite/actor_cfg.go b/models/sqlite/actor_cfg.go new file mode 100644 index 00000000..7f105b81 --- /dev/null +++ b/models/sqlite/actor_cfg.go @@ -0,0 +1,155 @@ +package sqlite + +import ( + "context" + "time" + + "github.com/filecoin-project/go-state-types/actors" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/venus-messager/models/mtypes" + + shared "github.com/filecoin-project/venus/venus-shared/types" + "gorm.io/gorm" + + "github.com/filecoin-project/venus-messager/models/repo" + types "github.com/filecoin-project/venus/venus-shared/types/messager" +) + +type sqliteActorCfg struct { + ID shared.UUID `gorm:"column:id;type:varchar(256);primary_key;"` // 主键 + ActorVersion int `gorm:"column:actor_v;type:INTEGER;NOT NULL"` + Code mtypes.DBCid `gorm:"column:code;type:varchar(256);index:idx_code_method,unique;NOT NULL"` + Method sqliteUint64 `gorm:"column:method;type:INTEGER;index:idx_code_method,unique;NOT NULL"` + + FeeSpec + + CreatedAt time.Time `gorm:"column:created_at;index;NOT NULL"` // 创建时间 + UpdatedAt time.Time `gorm:"column:updated_at;index;NOT NULL"` // 更新时间 +} + +func fromActorCfg(actorCfg *types.ActorCfg) *sqliteActorCfg { + return &sqliteActorCfg{ + ID: actorCfg.ID, + ActorVersion: int(actorCfg.ActorVersion), + Code: mtypes.NewDBCid(actorCfg.Code), + Method: sqliteUint64(actorCfg.Method), + FeeSpec: FeeSpec{ + GasOverEstimation: actorCfg.GasOverEstimation, + GasOverPremium: actorCfg.GasOverPremium, + MaxFee: mtypes.SafeFromGo(actorCfg.MaxFee.Int), + GasFeeCap: mtypes.SafeFromGo(actorCfg.GasFeeCap.Int), + BaseFee: mtypes.SafeFromGo(actorCfg.BaseFee.Int), + }, + CreatedAt: actorCfg.CreatedAt, + UpdatedAt: actorCfg.UpdatedAt, + } +} + +func (sqliteActorCfg sqliteActorCfg) ActorCfg() *types.ActorCfg { + return &types.ActorCfg{ + ID: sqliteActorCfg.ID, + ActorVersion: actors.Version(sqliteActorCfg.ActorVersion), + MethodType: types.MethodType{ + Code: sqliteActorCfg.Code.Cid(), + Method: abi.MethodNum(sqliteActorCfg.Method), + }, + FeeSpec: types.FeeSpec{ + GasOverEstimation: sqliteActorCfg.GasOverEstimation, + GasOverPremium: sqliteActorCfg.GasOverPremium, + MaxFee: big.Int(mtypes.SafeFromGo(sqliteActorCfg.MaxFee.Int)), + GasFeeCap: big.Int(mtypes.SafeFromGo(sqliteActorCfg.GasFeeCap.Int)), + BaseFee: big.Int(mtypes.SafeFromGo(sqliteActorCfg.BaseFee.Int)), + }, + CreatedAt: sqliteActorCfg.CreatedAt, + UpdatedAt: sqliteActorCfg.UpdatedAt, + } +} + +func (sqliteActorCfg sqliteActorCfg) TableName() string { + return "actor_cfg" +} + +var _ repo.ActorCfgRepo = (*sqliteActorCfgRepo)(nil) + +type sqliteActorCfgRepo struct { + *gorm.DB +} + +func newSqliteActorCfgRepo(db *gorm.DB) *sqliteActorCfgRepo { + return &sqliteActorCfgRepo{DB: db} +} + +func (s *sqliteActorCfgRepo) SaveActorCfg(ctx context.Context, actorCfg *types.ActorCfg) error { + return s.DB.Save(fromActorCfg(actorCfg)).Error +} + +func (s *sqliteActorCfgRepo) GetActorCfgByMethodType(ctx context.Context, methodType *types.MethodType) (*types.ActorCfg, error) { + var a sqliteActorCfg + if err := s.DB.Take(&a, "code = ? and method = ?", methodType.Code.String(), sqliteUint64(methodType.Method)).Error; err != nil { + return nil, err + } + + return a.ActorCfg(), nil +} + +func (s *sqliteActorCfgRepo) GetActorCfgByID(ctx context.Context, id shared.UUID) (*types.ActorCfg, error) { + var a sqliteActorCfg + if err := s.DB.Take(&a, "id = ?", id).Error; err != nil { + return nil, err + } + + return a.ActorCfg(), nil +} + +func (s *sqliteActorCfgRepo) ListActorCfg(ctx context.Context) ([]*types.ActorCfg, error) { + var list []*sqliteActorCfg + if err := s.DB.Find(&list).Error; err != nil { + return nil, err + } + + result := make([]*types.ActorCfg, len(list)) + for index, r := range list { + result[index] = r.ActorCfg() + } + + return result, nil +} + +func (s *sqliteActorCfgRepo) DelActorCfgByMethodType(ctx context.Context, methodType *types.MethodType) error { + return s.DB.Delete(sqliteActorCfg{}, "code = ? and method = ?", methodType.Code.String(), sqliteUint64(methodType.Method)).Error +} + +func (s *sqliteActorCfgRepo) DelActorCfgById(ctx context.Context, id shared.UUID) error { + return s.DB.Delete(sqliteActorCfg{}, "id = ?", id).Error +} + +func (s *sqliteActorCfgRepo) UpdateSelectSpecById(ctx context.Context, id shared.UUID, spec *types.ChangeGasSpecParams) error { + updateColumns := make(map[string]interface{}, 6) + if !spec.GasFeeCap.Nil() { + updateColumns["gas_fee_cap"] = spec.GasFeeCap.String() + } + if !spec.BaseFee.Nil() { + updateColumns["base_fee"] = spec.BaseFee.String() + } + if !spec.MaxFee.Nil() { + updateColumns["max_fee"] = spec.MaxFee.String() + } + + if spec.GasOverEstimation != nil { + updateColumns["gas_over_estimation"] = *spec.GasOverEstimation + } + if spec.GasOverPremium != nil { + updateColumns["gas_over_premium"] = *spec.GasOverPremium + } + + if len(updateColumns) == 0 { + return nil + } + + updateColumns["updated_at"] = time.Now() + + return s.DB.Model((*sqliteActorCfg)(nil)).Where("id = ?", id).UpdateColumns(updateColumns).Error +} diff --git a/models/sqlite/actor_cfg_test.go b/models/sqlite/actor_cfg_test.go new file mode 100644 index 00000000..f5fec9ae --- /dev/null +++ b/models/sqlite/actor_cfg_test.go @@ -0,0 +1,133 @@ +package sqlite + +import ( + "context" + "testing" + + shared "github.com/filecoin-project/venus/venus-shared/types" + + "github.com/filecoin-project/venus/venus-shared/testutil" + + types "github.com/filecoin-project/venus/venus-shared/types/messager" + "github.com/stretchr/testify/assert" +) + +func Test_fromActorCfg(t *testing.T) { + ctx := context.Background() + actorCfgRepo := setupRepo(t).ActorCfgRepo() + + var expectActorCfgs []*types.ActorCfg + for i := 0; i < 10; i++ { + var actorCfg types.ActorCfg + testutil.Provide(t, &actorCfg) + assert.NoError(t, actorCfgRepo.SaveActorCfg(ctx, &actorCfg)) + expectActorCfgs = append(expectActorCfgs, &actorCfg) + + actorCfgCp := actorCfg + actorCfgCp.ID = shared.NewUUID() + err := actorCfgRepo.SaveActorCfg(ctx, &actorCfgCp) + assert.EqualError(t, err, "UNIQUE constraint failed: actor_cfg.code, actor_cfg.method") + + actorCfg2, err := actorCfgRepo.GetActorCfgByID(ctx, actorCfg.ID) + assert.NoError(t, err) + assertActorCfgValue(t, &actorCfg, actorCfg2) + } + + //ListActorCfg + actorsList, err := actorCfgRepo.ListActorCfg(ctx) + assert.NoError(t, err) + assertActorCfgArrValue(t, expectActorCfgs, actorsList) + + //GetActorCfgByMethodType + for _, actorCfg := range expectActorCfgs { + actorActorCfg, err := actorCfgRepo.GetActorCfgByMethodType(ctx, &types.MethodType{ + Code: actorCfg.Code, + Method: actorCfg.Method, + }) + assert.NoError(t, err) + assertActorCfgValue(t, actorCfg, actorActorCfg) + } + + //UpdateSelectSpec + for _, actorCfg := range expectActorCfgs { + updateAsset := func(cfg func() (*types.ActorCfg, *types.ChangeGasSpecParams)) { + expectActorCfg, changeParams := cfg() + err := actorCfgRepo.UpdateSelectSpecById(ctx, actorCfg.ID, changeParams) + assert.NoError(t, err) + + actorCfg2, err := actorCfgRepo.GetActorCfgByID(ctx, actorCfg.ID) + assert.NoError(t, err) + + assertActorCfgValue(t, expectActorCfg, actorCfg2) + } + var selectSpec types.FeeSpec + testutil.Provide(t, &selectSpec) + updateAsset(func() (*types.ActorCfg, *types.ChangeGasSpecParams) { + actorCfg.GasOverEstimation = selectSpec.GasOverEstimation + actorCfgCp := *actorCfg + return &actorCfgCp, &types.ChangeGasSpecParams{ + GasOverEstimation: &selectSpec.GasOverEstimation, + } + }) + + updateAsset(func() (*types.ActorCfg, *types.ChangeGasSpecParams) { + actorCfg.GasOverPremium = selectSpec.GasOverPremium + actorCfgCp := *actorCfg + return &actorCfgCp, &types.ChangeGasSpecParams{ + GasOverPremium: &selectSpec.GasOverPremium, + } + }) + + updateAsset(func() (*types.ActorCfg, *types.ChangeGasSpecParams) { + actorCfg.MaxFee = selectSpec.MaxFee + actorCfgCp := *actorCfg + return &actorCfgCp, &types.ChangeGasSpecParams{ + MaxFee: selectSpec.MaxFee, + } + }) + + updateAsset(func() (*types.ActorCfg, *types.ChangeGasSpecParams) { + actorCfg.GasFeeCap = selectSpec.GasFeeCap + actorCfgCp := *actorCfg + return &actorCfgCp, &types.ChangeGasSpecParams{ + GasFeeCap: selectSpec.GasFeeCap, + } + }) + updateAsset(func() (*types.ActorCfg, *types.ChangeGasSpecParams) { + actorCfg.BaseFee = selectSpec.BaseFee + actorCfgCp := *actorCfg + return &actorCfgCp, &types.ChangeGasSpecParams{ + BaseFee: selectSpec.BaseFee, + } + }) + } + + //Delete + + for _, actorCfg := range expectActorCfgs[:5] { + assert.NoError(t, actorCfgRepo.DelActorCfgById(ctx, actorCfg.ID)) + } + + for _, actorCfg := range expectActorCfgs[5:] { + assert.NoError(t, actorCfgRepo.DelActorCfgByMethodType(ctx, &types.MethodType{actorCfg.Code, actorCfg.Method})) + } + + actorsR, err := actorCfgRepo.ListActorCfg(ctx) + assert.NoError(t, err) + assert.Equal(t, 0, len(actorsR)) +} + +func assertActorCfgValue(t *testing.T, expectVal, actualVal *types.ActorCfg) { + assert.Equal(t, expectVal.ID, actualVal.ID) + assert.Equal(t, expectVal.ActorVersion, actualVal.ActorVersion) + assert.Equal(t, expectVal.MethodType, actualVal.MethodType) + assert.Equal(t, expectVal.FeeSpec, actualVal.FeeSpec) +} + +func assertActorCfgArrValue(t *testing.T, expectVal, actualVal []*types.ActorCfg) { + assert.Equal(t, len(expectVal), len(actualVal)) + + for index, val := range expectVal { + assertActorCfgValue(t, val, actualVal[index]) + } +} diff --git a/models/sqlite/address.go b/models/sqlite/address.go index b05b6532..710456f4 100644 --- a/models/sqlite/address.go +++ b/models/sqlite/address.go @@ -19,19 +19,10 @@ type sqliteAddress struct { Addr string `gorm:"column:addr;type:varchar(256);uniqueIndex;NOT NULL"` Nonce uint64 `gorm:"column:nonce;type:unsigned bigint;index;NOT NULL"` Weight int64 `gorm:"column:weight;type:bigint;index;NOT NULL"` - SelMsgNum uint64 `gorm:"column:sel_msg_num;type:unsigned bigint;NOT NULL"` State types.AddressState `gorm:"column:state;type:int;index;default:1"` + SelMsgNum uint64 `gorm:"column:sel_msg_num;type:unsigned bigint;NOT NULL"` - // todo set GasOverEstimation not null after https://github.com/go-gorm/sqlite/issues/121 - // GasOverEstimation float64 `gorm:"column:gas_over_estimation;type:decimal(10,2);NOT NULL"` - GasOverEstimation float64 `gorm:"column:gas_over_estimation;type:decimal(10,2)"` - MaxFee mtypes.Int `gorm:"column:max_fee;type:varchar(256);default:0"` - GasFeeCap mtypes.Int `gorm:"column:gas_fee_cap;type:varchar(256);default:0"` - - // todo set GasOverEstimation not null after https://github.com/go-gorm/sqlite/issues/121 - // GasOverPremium float64 `gorm:"column:gas_over_premium;type:decimal(10,2);NOT NULL"` - GasOverPremium float64 `gorm:"column:gas_over_premium;type:decimal(10,2)"` - BaseFee mtypes.Int `gorm:"column:base_fee;type:varchar(256);default:0"` + FeeSpec IsDeleted int `gorm:"column:is_deleted;index;default:-1;NOT NULL"` // 是否删除 1:是 -1:否 CreatedAt time.Time `gorm:"column:created_at;index;NOT NULL"` // 创建时间 @@ -44,20 +35,22 @@ func (s sqliteAddress) TableName() string { func fromAddress(addr *types.Address) *sqliteAddress { return &sqliteAddress{ - ID: addr.ID, - Addr: addr.Addr.String(), - Nonce: addr.Nonce, - Weight: addr.Weight, - SelMsgNum: addr.SelMsgNum, - State: addr.State, - GasOverEstimation: addr.GasOverEstimation, - GasOverPremium: addr.GasOverPremium, - MaxFee: mtypes.SafeFromGo(addr.MaxFee.Int), - GasFeeCap: mtypes.SafeFromGo(addr.GasFeeCap.Int), - BaseFee: mtypes.SafeFromGo(addr.BaseFee.Int), - IsDeleted: addr.IsDeleted, - CreatedAt: addr.CreatedAt, - UpdatedAt: addr.UpdatedAt, + ID: addr.ID, + Addr: addr.Addr.String(), + Nonce: addr.Nonce, + Weight: addr.Weight, + State: addr.State, + SelMsgNum: addr.SelMsgNum, + FeeSpec: FeeSpec{ + GasOverEstimation: addr.GasOverEstimation, + GasOverPremium: addr.GasOverPremium, + MaxFee: mtypes.SafeFromGo(addr.MaxFee.Int), + GasFeeCap: mtypes.SafeFromGo(addr.GasFeeCap.Int), + BaseFee: mtypes.SafeFromGo(addr.BaseFee.Int), + }, + IsDeleted: addr.IsDeleted, + CreatedAt: addr.CreatedAt, + UpdatedAt: addr.UpdatedAt, } } @@ -68,20 +61,22 @@ func (s sqliteAddress) Address() (*types.Address, error) { } return &types.Address{ - ID: s.ID, - Addr: addr, - Nonce: s.Nonce, - Weight: s.Weight, - SelMsgNum: s.SelMsgNum, - State: s.State, - GasOverEstimation: s.GasOverEstimation, - GasOverPremium: s.GasOverPremium, - MaxFee: big.Int(mtypes.SafeFromGo(s.MaxFee.Int)), - GasFeeCap: big.Int(mtypes.SafeFromGo(s.GasFeeCap.Int)), - BaseFee: big.Int(mtypes.SafeFromGo(s.BaseFee.Int)), - IsDeleted: s.IsDeleted, - CreatedAt: s.CreatedAt, - UpdatedAt: s.UpdatedAt, + ID: s.ID, + Addr: addr, + Nonce: s.Nonce, + Weight: s.Weight, + State: s.State, + SelMsgNum: s.SelMsgNum, + FeeSpec: types.FeeSpec{ + GasOverEstimation: s.GasOverEstimation, + GasOverPremium: s.GasOverPremium, + MaxFee: big.Int(mtypes.SafeFromGo(s.MaxFee.Int)), + GasFeeCap: big.Int(mtypes.SafeFromGo(s.GasFeeCap.Int)), + BaseFee: big.Int(mtypes.SafeFromGo(s.BaseFee.Int)), + }, + IsDeleted: s.IsDeleted, + CreatedAt: s.CreatedAt, + UpdatedAt: s.UpdatedAt, }, nil } diff --git a/models/sqlite/address_test.go b/models/sqlite/address_test.go index 7f4ec38c..68e37fad 100644 --- a/models/sqlite/address_test.go +++ b/models/sqlite/address_test.go @@ -29,35 +29,40 @@ func TestAddress(t *testing.T) { assert.NoError(t, err) addrInfo := &types.Address{ - ID: venustypes.NewUUID(), - Addr: addr, - Nonce: 3, - Weight: 100, - SelMsgNum: 1, - State: types.AddressStateAlive, - GasOverEstimation: 1.25, - GasOverPremium: 1.6, - MaxFee: big.NewInt(10), - GasFeeCap: big.NewInt(1), - BaseFee: big.NewInt(10001), - IsDeleted: -1, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), + ID: venustypes.NewUUID(), + Addr: addr, + Nonce: 3, + Weight: 100, + State: types.AddressStateAlive, + SelMsgNum: 1, + FeeSpec: types.FeeSpec{ + + GasOverEstimation: 1.25, + GasOverPremium: 1.6, + MaxFee: big.NewInt(10), + GasFeeCap: big.NewInt(1), + BaseFee: big.NewInt(10001), + }, + IsDeleted: -1, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), } addrInfo2 := &types.Address{ - ID: venustypes.NewUUID(), - Addr: addr2, - SelMsgNum: 10, - State: types.AddressStateAlive, - GasOverPremium: 3.0, - MaxFee: big.NewInt(110), - GasFeeCap: big.NewInt(11), - BaseFee: big.NewInt(10000), - Nonce: 2, - IsDeleted: -1, - CreatedAt: time.Time{}, - UpdatedAt: time.Time{}, + ID: venustypes.NewUUID(), + Addr: addr2, + State: types.AddressStateAlive, + SelMsgNum: 10, + FeeSpec: types.FeeSpec{ + GasOverPremium: 3.0, + MaxFee: big.NewInt(110), + GasFeeCap: big.NewInt(11), + BaseFee: big.NewInt(10000), + }, + Nonce: 2, + IsDeleted: -1, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, } addrInfo3 := &types.Address{ @@ -66,6 +71,7 @@ func TestAddress(t *testing.T) { Nonce: 3, Weight: 1000, SelMsgNum: 10, + FeeSpec: types.FeeSpec{}, State: types.AddressStateAlive, IsDeleted: -1, CreatedAt: time.Now(), diff --git a/models/sqlite/db.go b/models/sqlite/db.go index a0979517..056029c4 100644 --- a/models/sqlite/db.go +++ b/models/sqlite/db.go @@ -14,6 +14,10 @@ type SqlLiteRepo struct { *gorm.DB } +func (d SqlLiteRepo) ActorCfgRepo() repo.ActorCfgRepo { + return newSqliteActorCfgRepo(d.DB) +} + func (d SqlLiteRepo) MessageRepo() repo.MessageRepo { return newSqliteMessageRepo(d.DB) } @@ -31,20 +35,7 @@ func (d SqlLiteRepo) NodeRepo() repo.NodeRepo { } func (d SqlLiteRepo) AutoMigrate() error { - err := d.GetDb().AutoMigrate(sqliteMessage{}) - if err != nil { - return err - } - - if err := d.GetDb().AutoMigrate(sqliteAddress{}); err != nil { - return err - } - - if err := d.GetDb().AutoMigrate(sqliteSharedParams{}); err != nil { - return err - } - - return d.GetDb().AutoMigrate(sqliteNode{}) + return d.GetDb().AutoMigrate(sqliteMessage{}, sqliteActorCfg{}, sqliteAddress{}, sqliteSharedParams{}, sqliteNode{}) } func (d SqlLiteRepo) GetDb() *gorm.DB { @@ -64,6 +55,18 @@ type TxSqlliteRepo struct { *gorm.DB } +func (t *TxSqlliteRepo) ActorCfgRepo() repo.ActorCfgRepo { + return newSqliteActorCfgRepo(t.DB) +} + +func (t *TxSqlliteRepo) SharedParamsRepo() repo.SharedParamsRepo { + return newSqliteSharedParamsRepo(t.DB) +} + +func (t *TxSqlliteRepo) NodeRepo() repo.NodeRepo { + return newSqliteNodeRepo(t.DB) +} + func (t *TxSqlliteRepo) MessageRepo() repo.MessageRepo { return newSqliteMessageRepo(t.DB) } @@ -88,9 +91,7 @@ func OpenSqlite(fsRepo filestore.FSRepo) (repo.Repo, error) { } db.Set("gorm:table_options", "CHARSET=utf8mb4") - if fsRepo.Config().DB.Sqlite.Debug { - db = db.Debug() - } + db = db.Debug() sqlDB, err := db.DB() if err != nil { diff --git a/models/sqlite/init.go b/models/sqlite/init.go deleted file mode 100644 index 469c196a..00000000 --- a/models/sqlite/init.go +++ /dev/null @@ -1,27 +0,0 @@ -package sqlite - -import ( - "reflect" - - types "github.com/filecoin-project/venus/venus-shared/types/messager" -) - -var ( - TMessage = reflect.TypeOf(&types.Message{}) - TSqliteMessage = reflect.TypeOf(&sqliteMessage{}) -) - -var ( - TAddress = reflect.TypeOf(&types.Address{}) - TSqliteAddress = reflect.TypeOf(&sqliteAddress{}) -) - -var ( - TSqliteSharedParams = reflect.TypeOf(&sqliteSharedParams{}) - TSharedParams = reflect.TypeOf(&types.SharedSpec{}) -) - -var ( - TNode = reflect.TypeOf(&types.Node{}) - TSqliteNode = reflect.TypeOf(&sqliteNode{}) -) diff --git a/models/sqlite/message.go b/models/sqlite/message.go index 72aaacda..1bc0d49a 100644 --- a/models/sqlite/message.go +++ b/models/sqlite/message.go @@ -33,7 +33,7 @@ type sqliteMessage struct { GasFeeCap mtypes.Int `gorm:"column:gas_fee_cap;type:varchar(256);default:0"` GasPremium mtypes.Int `gorm:"column:gas_premium;type:varchar(256);default:0"` - Method int `gorm:"column:method;type:int;NOT NULL"` + Method sqliteUint64 `gorm:"column:method;type:int;NOT NULL"` Params []byte `gorm:"column:params;type:blob;"` @@ -113,7 +113,7 @@ func fromMessage(srcMsg *types.Message) *sqliteMessage { GasLimit: srcMsg.GasLimit, GasFeeCap: mtypes.SafeFromGo(srcMsg.GasFeeCap.Int), GasPremium: mtypes.SafeFromGo(srcMsg.GasPremium.Int), - Method: int(srcMsg.Method), + Method: sqliteUint64(srcMsg.Method), Params: srcMsg.Params, Signature: (*repo.SqlSignature)(srcMsg.Signature), Height: srcMsg.Height, diff --git a/models/sqlite/node.go b/models/sqlite/node.go index 370f8439..71cb9c77 100644 --- a/models/sqlite/node.go +++ b/models/sqlite/node.go @@ -33,11 +33,21 @@ func fromNode(node *types.Node) *sqliteNode { Token: node.Token, Type: node.Type, IsDeleted: repo.NotDeleted, + CreatedAt: node.CreatedAt, + UpdatedAt: node.UpdatedAt, } } func (sqliteNode sqliteNode) Node() *types.Node { - return automapper.MustMapper(&sqliteNode, TNode).(*types.Node) + return &types.Node{ + ID: sqliteNode.ID, + Name: sqliteNode.Name, + URL: sqliteNode.URL, + Token: sqliteNode.Token, + Type: sqliteNode.Type, + CreatedAt: sqliteNode.CreatedAt, + UpdatedAt: sqliteNode.UpdatedAt, + } } func (sqliteNode sqliteNode) TableName() string { @@ -56,6 +66,7 @@ func newSqliteNodeRepo(db *gorm.DB) sqliteNodeRepo { func (s sqliteNodeRepo) CreateNode(node *types.Node) error { sNode := fromNode(node) + sNode.CreatedAt = time.Now() return s.DB.Create(sNode).Error } diff --git a/models/sqlite/node_test.go b/models/sqlite/node_test.go index 34884d82..9eff895e 100644 --- a/models/sqlite/node_test.go +++ b/models/sqlite/node_test.go @@ -4,6 +4,8 @@ import ( "math/rand" "testing" + "github.com/filecoin-project/venus-messager/testhelper" + "gorm.io/gorm" venustypes "github.com/filecoin-project/venus/venus-shared/types" @@ -22,29 +24,46 @@ func randNode() *types.Node { } func TestNode(t *testing.T) { - nodeRepo := setupRepo(t).NodeRepo() - - node := randNode() - node2 := randNode() - node3 := randNode() - randName := venustypes.NewUUID().String() - t.Run("create node", func(t *testing.T) { + nodeRepo := setupRepo(t).NodeRepo() + node := randNode() + node2 := randNode() + node3 := randNode() + assert.NoError(t, nodeRepo.CreateNode(node)) assert.NoError(t, nodeRepo.CreateNode(node2)) assert.NoError(t, nodeRepo.CreateNode(node3)) }) t.Run("get node", func(t *testing.T) { + nodeRepo := setupRepo(t).NodeRepo() + node := randNode() + node2 := randNode() + node3 := randNode() + randName := venustypes.NewUUID().String() + + assert.NoError(t, nodeRepo.CreateNode(node)) + assert.NoError(t, nodeRepo.CreateNode(node2)) + assert.NoError(t, nodeRepo.CreateNode(node3)) + res, err := nodeRepo.GetNode(node2.Name) assert.NoError(t, err) - assert.Equal(t, node2, res) + testhelper.Equal(t, node2, res) _, err = nodeRepo.GetNode(randName) assert.Equal(t, gorm.ErrRecordNotFound, err) }) t.Run("save node", func(t *testing.T) { + nodeRepo := setupRepo(t).NodeRepo() + node := randNode() + node2 := randNode() + node3 := randNode() + + assert.NoError(t, nodeRepo.CreateNode(node)) + assert.NoError(t, nodeRepo.CreateNode(node2)) + assert.NoError(t, nodeRepo.CreateNode(node3)) + node.URL = "url" node.Token = "token" node.Type = types.FullNode @@ -52,17 +71,37 @@ func TestNode(t *testing.T) { assert.NoError(t, nodeRepo.SaveNode(node)) res, err := nodeRepo.GetNode(node.Name) assert.NoError(t, err) - assert.Equal(t, node, res) + testhelper.Equal(t, node, res) }) t.Run("list node", func(t *testing.T) { + nodeRepo := setupRepo(t).NodeRepo() + node := randNode() + node2 := randNode() + node3 := randNode() + + assert.NoError(t, nodeRepo.CreateNode(node)) + assert.NoError(t, nodeRepo.CreateNode(node2)) + assert.NoError(t, nodeRepo.CreateNode(node3)) + list, err := nodeRepo.ListNode() assert.NoError(t, err) assert.Equal(t, len(list), 3) - assert.Equal(t, []*types.Node{node, node2, node3}, list) + + testhelper.Equal(t, []*types.Node{node, node2, node3}, list) }) t.Run("has node", func(t *testing.T) { + nodeRepo := setupRepo(t).NodeRepo() + node := randNode() + node2 := randNode() + node3 := randNode() + randName := venustypes.NewUUID().String() + + assert.NoError(t, nodeRepo.CreateNode(node)) + assert.NoError(t, nodeRepo.CreateNode(node2)) + assert.NoError(t, nodeRepo.CreateNode(node3)) + has, err := nodeRepo.HasNode(node.Name) assert.NoError(t, err) assert.True(t, has) @@ -73,6 +112,16 @@ func TestNode(t *testing.T) { }) t.Run("delete node", func(t *testing.T) { + nodeRepo := setupRepo(t).NodeRepo() + node := randNode() + node2 := randNode() + node3 := randNode() + randName := venustypes.NewUUID().String() + + assert.NoError(t, nodeRepo.CreateNode(node)) + assert.NoError(t, nodeRepo.CreateNode(node2)) + assert.NoError(t, nodeRepo.CreateNode(node3)) + err := nodeRepo.DelNode(node.Name) assert.NoError(t, err) diff --git a/models/sqlite/shared_params.go b/models/sqlite/shared_params.go index 620356ea..3cc9c345 100644 --- a/models/sqlite/shared_params.go +++ b/models/sqlite/shared_params.go @@ -13,38 +13,36 @@ import ( ) type sqliteSharedParams struct { - ID uint `gorm:"primary_key;column:id;type:INT unsigned AUTO_INCREMENT;NOT NULL" json:"id"` - - GasOverEstimation float64 `gorm:"column:gas_over_estimation;type:REAL;NOT NULL"` - MaxFee mtypes.Int `gorm:"column:max_fee;type:varchar(256);NOT NULL;default:0"` - GasFeeCap mtypes.Int `gorm:"column:gas_fee_cap;type:varchar(256);NOT NULL;default:0"` - GasOverPremium float64 `gorm:"column:gas_over_premium;type:REAL;NOT NULL;default:0"` - BaseFee mtypes.Int `gorm:"column:base_fee;type:varchar(256);default:0"` - - SelMsgNum uint64 `gorm:"column:sel_msg_num;type:UNSIGNED BIG INT;NOT NULL"` + ID uint `gorm:"primary_key;column:id;type:INT unsigned AUTO_INCREMENT;NOT NULL" json:"id"` + SelMsgNum uint64 `gorm:"column:sel_msg_num;type:unsigned bigint;NOT NULL"` + FeeSpec } func fromSharedParams(sp types.SharedSpec) *sqliteSharedParams { return &sqliteSharedParams{ - ID: sp.ID, - GasOverEstimation: sp.GasOverEstimation, - MaxFee: mtypes.SafeFromGo(sp.MaxFee.Int), - GasFeeCap: mtypes.SafeFromGo(sp.GasFeeCap.Int), - BaseFee: mtypes.SafeFromGo(sp.BaseFee.Int), - GasOverPremium: sp.GasOverPremium, - SelMsgNum: sp.SelMsgNum, + ID: sp.ID, + SelMsgNum: sp.SelMsgNum, + FeeSpec: FeeSpec{ + BaseFee: mtypes.SafeFromGo(sp.BaseFee.Int), + GasOverEstimation: sp.GasOverEstimation, + MaxFee: mtypes.SafeFromGo(sp.MaxFee.Int), + GasFeeCap: mtypes.SafeFromGo(sp.GasFeeCap.Int), + GasOverPremium: sp.GasOverPremium, + }, } } func (ssp sqliteSharedParams) SharedParams() *types.SharedSpec { return &types.SharedSpec{ - ID: ssp.ID, - GasOverEstimation: ssp.GasOverEstimation, - MaxFee: big.Int(mtypes.SafeFromGo(ssp.MaxFee.Int)), - GasFeeCap: big.Int(mtypes.SafeFromGo(ssp.GasFeeCap.Int)), - BaseFee: big.Int(mtypes.SafeFromGo(ssp.BaseFee.Int)), - GasOverPremium: ssp.GasOverPremium, - SelMsgNum: ssp.SelMsgNum, + ID: ssp.ID, + SelMsgNum: ssp.SelMsgNum, + FeeSpec: types.FeeSpec{ + GasOverEstimation: ssp.GasOverEstimation, + MaxFee: big.Int(mtypes.SafeFromGo(ssp.MaxFee.Int)), + GasFeeCap: big.Int(mtypes.SafeFromGo(ssp.GasFeeCap.Int)), + BaseFee: big.Int(mtypes.SafeFromGo(ssp.BaseFee.Int)), + GasOverPremium: ssp.GasOverPremium, + }, } } diff --git a/models/sqlite/shared_params_test.go b/models/sqlite/shared_params_test.go index af72122d..5ff1ff6e 100644 --- a/models/sqlite/shared_params_test.go +++ b/models/sqlite/shared_params_test.go @@ -15,13 +15,15 @@ func TestSharedParams(t *testing.T) { ctx := context.Background() params := &messager.SharedSpec{ - ID: 1, - GasOverEstimation: 1.5, - MaxFee: big.NewInt(10), - GasFeeCap: big.NewInt(100), - BaseFee: big.NewInt(1000), - GasOverPremium: 1.6, - SelMsgNum: 30, + ID: 1, + SelMsgNum: 30, + FeeSpec: messager.FeeSpec{ + GasOverEstimation: 1.5, + MaxFee: big.NewInt(10), + GasFeeCap: big.NewInt(100), + BaseFee: big.NewInt(1000), + GasOverPremium: 1.6, + }, } _, err := r.SetSharedParams(ctx, params) assert.Nil(t, err) @@ -32,13 +34,15 @@ func TestSharedParams(t *testing.T) { // update params but ID not 1 params2 := &messager.SharedSpec{ - ID: 3, - GasOverEstimation: 3.5, - MaxFee: big.NewInt(310), - GasFeeCap: big.NewInt(3100), - BaseFee: big.NewInt(0), - GasOverPremium: 3.6, - SelMsgNum: 10, + ID: 3, + SelMsgNum: 10, + FeeSpec: messager.FeeSpec{ + GasOverEstimation: 3.5, + MaxFee: big.NewInt(310), + GasFeeCap: big.NewInt(3100), + BaseFee: big.NewInt(0), + GasOverPremium: 3.6, + }, } _, err = r.SetSharedParams(ctx, params2) assert.Nil(t, err) diff --git a/models/sqlite/types.go b/models/sqlite/types.go new file mode 100644 index 00000000..a05bec31 --- /dev/null +++ b/models/sqlite/types.go @@ -0,0 +1,40 @@ +package sqlite + +import ( + "database/sql/driver" + "errors" + + "github.com/filecoin-project/venus-messager/models/mtypes" +) + +type FeeSpec struct { + BaseFee mtypes.Int `gorm:"column:base_fee;type:varchar(256);default:0"` //not include in message + + GasOverEstimation float64 `gorm:"column:gas_over_estimation;type:REAL;NOT NULL;default:0"` + MaxFee mtypes.Int `gorm:"column:max_fee;type:varchar(256);default:0"` + GasFeeCap mtypes.Int `gorm:"column:gas_fee_cap;type:varchar(256);default:0"` + GasOverPremium float64 `gorm:"column:gas_over_premium;type:REAL;NOT NULL;default:0"` +} + +type sqliteUint64 uint64 + +func newSqliteUint64(val uint64) sqliteUint64 { + return sqliteUint64(val) +} + +func (c *sqliteUint64) Scan(value interface{}) error { + switch value := value.(type) { + case int64: + *c = sqliteUint64(value) + case int: + *c = sqliteUint64(value) + default: + return errors.New("address should be a `[]byte` or `string`") + } + + return nil +} + +func (c sqliteUint64) Value() (driver.Value, error) { + return int64(c), nil +} diff --git a/service/message_selector.go b/service/message_selector.go index 1e5fb373..f959e80f 100644 --- a/service/message_selector.go +++ b/service/message_selector.go @@ -4,8 +4,15 @@ import ( "context" "errors" "fmt" + "strconv" "time" + "github.com/filecoin-project/go-state-types/network" + + lru "github.com/hashicorp/golang-lru" + + "gorm.io/gorm" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" @@ -113,7 +120,6 @@ func (msgSelectMgr *MsgSelectMgr) SelectMessage(ctx context.Context, ts *venusTy func (msgSelectMgr *MsgSelectMgr) getNonceInTipset(ctx context.Context, ts *venusTypes.TipSet) (*utils.NonceMap, error) { applied := utils.NewNonceMap() - // todo change with venus/lotus message for tipset selectMsg := func(m *venusTypes.Message) error { // The first match for a sender is guaranteed to have correct nonce -- the block isn't valid otherwise if _, ok := applied.Get(m.From); !ok { @@ -234,6 +240,8 @@ type work struct { start time.Time controlChan chan struct{} + + actorCache *lru.ARCCache } func newWork(ctx context.Context, @@ -246,6 +254,7 @@ func newWork(ctx context.Context, msgReceiver publisher.MessageReceiver, ) *work { ctx, cancel := context.WithCancel(ctx) + cache, _ := lru.NewARC(100) return &work{ ctx: ctx, cancel: cancel, @@ -257,6 +266,7 @@ func newWork(ctx context.Context, walletClient: walletClient, msgReceiver: msgReceiver, controlChan: make(chan struct{}, 1), + actorCache: cache, } } @@ -485,11 +495,18 @@ func (w *work) estimateMessage(ctx context.Context, addrInfo *types.Address, ) ([]*venusTypes.EstimateResult, []*types.Message, error) { candidateMessages := make([]*types.Message, 0, len(msgs)) - estimateMesssages := make([]*venusTypes.EstimateMessage, 0, len(msgs)) + estimateMessages := make([]*venusTypes.EstimateMessage, 0, len(msgs)) + nv, err := w.fullNode.StateNetworkVersion(ctx, venusTypes.EmptyTSK) + if err != nil { + return nil, nil, err + } for _, msg := range msgs { - // global msg meta - newMsgMeta := mergeMsgSpec(sharedParams, msg.Meta, addrInfo, msg) + actorCfg, err := w.getActorCfg(ctx, msg, nv) + if err != nil { + return nil, nil, err + } + newMsgMeta := mergeMsgSpec(sharedParams, msg.Meta, addrInfo, actorCfg, msg) if msg.GasFeeCap.NilOrZero() && !newMsgMeta.GasFeeCap.NilOrZero() { msg.GasFeeCap = newMsgMeta.GasFeeCap @@ -502,7 +519,7 @@ func (w *work) estimateMessage(ctx context.Context, } candidateMessages = append(candidateMessages, msg) - estimateMesssages = append(estimateMesssages, &venusTypes.EstimateMessage{ + estimateMessages = append(estimateMessages, &venusTypes.EstimateMessage{ Msg: &msg.Message, Spec: &venusTypes.MessageSendSpec{ MaxFee: newMsgMeta.MaxFee, @@ -519,7 +536,7 @@ func (w *work) estimateMessage(ctx context.Context, estimateMsgCtx, estimateMsgCancel := context.WithTimeout(ctx, w.cfg.EstimateMessageTimeout) defer estimateMsgCancel() - estimateResult, err := w.fullNode.GasBatchEstimateMessageGas(estimateMsgCtx, estimateMesssages, addrInfo.Nonce, ts.Key()) + estimateResult, err := w.fullNode.GasBatchEstimateMessageGas(estimateMsgCtx, estimateMessages, addrInfo.Nonce, ts.Key()) return estimateResult, candidateMessages, err } @@ -572,6 +589,35 @@ func (w *work) saveSelectedMessages(ctx context.Context, selectResult *MsgSelect return err } +func (w *work) getActorCfg(ctx context.Context, msg *types.Message, nv network.Version) (*types.ActorCfg, error) { + key := msg.To.String() + "-" + strconv.Itoa(int(nv)) + var actor *venusTypes.Actor + actorI, has := w.actorCache.Get(key) + if has { + actor = actorI.(*venusTypes.Actor) + } else { + var err error + actor, err = w.fullNode.StateGetActor(ctx, msg.To, venusTypes.EmptyTSK) + if err != nil { + return nil, err + } + w.actorCache.Add(key, actor) + } + + fmt.Println(actor.Code) + actorCfg, err := w.repo.ActorCfgRepo().GetActorCfgByMethodType(ctx, &types.MethodType{ + Code: actor.Code, + Method: msg.Method, + }) + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return nil, err + } + return actorCfg, nil +} + func (w *work) finish() { <-w.controlChan } @@ -605,46 +651,58 @@ type GasSpec struct { BaseFee big.Int } -func mergeMsgSpec(globalSpec *types.SharedSpec, sendSpec *types.SendSpec, addrInfo *types.Address, msg *types.Message) *GasSpec { +func mergeMsgSpec(globalSpec *types.SharedSpec, sendSpec *types.SendSpec, addrInfo *types.Address, actorCfg *types.ActorCfg, msg *types.Message) *GasSpec { newMsgMeta := &GasSpec{ GasOverEstimation: sendSpec.GasOverEstimation, GasOverPremium: sendSpec.GasOverPremium, MaxFee: sendSpec.MaxFee, } - if sendSpec.GasOverEstimation == 0 { - if addrInfo.GasOverEstimation != 0 { - newMsgMeta.GasOverEstimation = addrInfo.GasOverEstimation - } else if globalSpec != nil { - newMsgMeta.GasOverEstimation = globalSpec.GasOverEstimation - } + //msg > addr > actor > global + if sendSpec.GasOverEstimation != 0 { + newMsgMeta.GasOverEstimation = sendSpec.GasOverEstimation + } else if addrInfo.GasOverEstimation != 0 { + newMsgMeta.GasOverEstimation = addrInfo.GasOverEstimation + } else if actorCfg != nil && actorCfg.GasOverEstimation != 0 { + newMsgMeta.GasOverEstimation = actorCfg.GasOverEstimation + } else if globalSpec != nil { + newMsgMeta.GasOverEstimation = globalSpec.GasOverEstimation } - if sendSpec.MaxFee.NilOrZero() { - if !addrInfo.MaxFee.NilOrZero() { - newMsgMeta.MaxFee = addrInfo.MaxFee - } else if globalSpec != nil { - newMsgMeta.MaxFee = globalSpec.MaxFee - } + + if !sendSpec.MaxFee.NilOrZero() { + newMsgMeta.MaxFee = sendSpec.MaxFee + } else if !addrInfo.MaxFee.NilOrZero() { + newMsgMeta.MaxFee = addrInfo.MaxFee + } else if actorCfg != nil && !actorCfg.MaxFee.NilOrZero() { + newMsgMeta.MaxFee = actorCfg.MaxFee + } else if globalSpec != nil { + newMsgMeta.MaxFee = globalSpec.MaxFee + } + + if sendSpec.GasOverPremium != 0 { + newMsgMeta.GasOverPremium = sendSpec.GasOverPremium + } else if addrInfo.GasOverPremium != 0 { + newMsgMeta.GasOverPremium = addrInfo.GasOverPremium + } else if actorCfg != nil && actorCfg.GasOverPremium != 0 { + newMsgMeta.GasOverPremium = actorCfg.GasOverPremium + } else if globalSpec.GasOverPremium != 0 { + newMsgMeta.GasOverPremium = globalSpec.GasOverPremium } if msg.GasFeeCap.NilOrZero() { if !addrInfo.GasFeeCap.NilOrZero() { newMsgMeta.GasFeeCap = addrInfo.GasFeeCap + } else if actorCfg != nil && !actorCfg.GasFeeCap.NilOrZero() { + newMsgMeta.GasFeeCap = actorCfg.GasFeeCap } else if globalSpec != nil { newMsgMeta.GasFeeCap = globalSpec.GasFeeCap } } - if sendSpec.GasOverPremium == 0 { - if addrInfo.GasOverPremium != 0 { - newMsgMeta.GasOverPremium = addrInfo.GasOverPremium - } else if globalSpec.GasOverPremium != 0 { - newMsgMeta.GasOverPremium = globalSpec.GasOverPremium - } - } - if !addrInfo.BaseFee.NilOrZero() { newMsgMeta.BaseFee = addrInfo.BaseFee + } else if actorCfg != nil && !actorCfg.BaseFee.NilOrZero() { + newMsgMeta.BaseFee = actorCfg.BaseFee } else if globalSpec != nil { newMsgMeta.BaseFee = globalSpec.BaseFee } diff --git a/service/message_selector_test.go b/service/message_selector_test.go index aa897f11..18c5e591 100644 --- a/service/message_selector_test.go +++ b/service/message_selector_test.go @@ -44,11 +44,23 @@ func TestMergeMsgSpec(t *testing.T) { emptySendSpec := &types.SendSpec{} addrInfo := &types.Address{ - GasOverEstimation: 1.5, - MaxFee: big.NewInt(50000), - GasFeeCap: big.NewInt(50000), - GasOverPremium: 5.0, - BaseFee: big.NewInt(50001), + FeeSpec: types.FeeSpec{ + GasOverEstimation: 1.5, + MaxFee: big.NewInt(50000), + GasFeeCap: big.NewInt(50000), + GasOverPremium: 5.0, + BaseFee: big.NewInt(50001), + }, + } + + actorCfg := &types.ActorCfg{ + FeeSpec: types.FeeSpec{ + GasOverEstimation: 2.0, + MaxFee: big.NewInt(60000), + GasFeeCap: big.NewInt(60000), + GasOverPremium: 6.0, + BaseFee: big.NewInt(60001), + }, } emptyAddrInfo := &types.Address{} @@ -60,6 +72,7 @@ func TestMergeMsgSpec(t *testing.T) { globalSpec *types.SharedSpec sendSpec *types.SendSpec addrInfo *types.Address + actorCfg *types.ActorCfg msg *types.Message expect *GasSpec @@ -75,6 +88,7 @@ func TestMergeMsgSpec(t *testing.T) { defSharedParams, sendSpec, addrInfo, + nil, msg, &GasSpec{GasOverEstimation: sendSpec.GasOverEstimation, MaxFee: sendSpec.MaxFee, GasOverPremium: sendSpec.GasOverPremium, GasFeeCap: addrInfo.GasFeeCap, BaseFee: addrInfo.BaseFee}, }, @@ -82,6 +96,7 @@ func TestMergeMsgSpec(t *testing.T) { defSharedParams, emptySendSpec, addrInfo, + nil, msg, &GasSpec{GasOverEstimation: addrInfo.GasOverEstimation, MaxFee: addrInfo.MaxFee, GasOverPremium: addrInfo.GasOverPremium, GasFeeCap: addrInfo.GasFeeCap, BaseFee: addrInfo.BaseFee}, }, @@ -89,6 +104,7 @@ func TestMergeMsgSpec(t *testing.T) { defSharedParams, emptySendSpec, emptyAddrInfo, + nil, msg, &GasSpec{GasOverEstimation: defSharedParams.GasOverEstimation, MaxFee: defSharedParams.MaxFee, GasOverPremium: defSharedParams.GasOverPremium, GasFeeCap: defSharedParams.GasFeeCap, BaseFee: defSharedParams.BaseFee}, }, @@ -96,6 +112,7 @@ func TestMergeMsgSpec(t *testing.T) { defSharedParams, emptySendSpec, addrInfo, + nil, msg2, &GasSpec{GasOverEstimation: addrInfo.GasOverEstimation, MaxFee: addrInfo.MaxFee, GasOverPremium: addrInfo.GasOverPremium, BaseFee: addrInfo.BaseFee}, }, @@ -103,13 +120,22 @@ func TestMergeMsgSpec(t *testing.T) { defSharedParams, emptySendSpec, emptyAddrInfo, + nil, msg2, &GasSpec{GasOverEstimation: defSharedParams.GasOverEstimation, MaxFee: defSharedParams.MaxFee, GasOverPremium: defSharedParams.GasOverPremium, BaseFee: defSharedParams.BaseFee}, }, + { + defSharedParams, + emptySendSpec, + emptyAddrInfo, + actorCfg, + msg2, + &GasSpec{GasOverEstimation: actorCfg.GasOverEstimation, MaxFee: actorCfg.MaxFee, GasOverPremium: actorCfg.GasOverPremium, BaseFee: actorCfg.BaseFee}, + }, } for _, test := range tests { - gasSpec := mergeMsgSpec(test.globalSpec, test.sendSpec, test.addrInfo, test.msg) + gasSpec := mergeMsgSpec(test.globalSpec, test.sendSpec, test.addrInfo, test.actorCfg, test.msg) assert.Equal(t, test.expect, gasSpec) } } @@ -672,7 +698,7 @@ func checkGasFee(t *testing.T, srcMsgs, currMsgs *types.Message, sharedParams *t if srcMsgs.Meta != nil { meta = srcMsgs.Meta } - gasSpec := mergeMsgSpec(sharedParams, meta, addrInfo, srcMsgs) + gasSpec := mergeMsgSpec(sharedParams, meta, addrInfo, nil, srcMsgs) gasLimit := testhelper.DefGasUsed gasPremium := testhelper.DefGasPremium if gasSpec.GasOverEstimation != 0 { diff --git a/service/message_service.go b/service/message_service.go index b9b0b0bc..cd2cd567 100644 --- a/service/message_service.go +++ b/service/message_service.go @@ -891,6 +891,22 @@ func (ms *MessageService) ClearUnFillMessage(ctx context.Context, addr address.A } } +func (ms *MessageService) SaveActorCfg(ctx context.Context, actorCfg *types.ActorCfg) error { + return ms.repo.ActorCfgRepo().SaveActorCfg(ctx, actorCfg) +} + +func (ms *MessageService) UpdateActorCfg(ctx context.Context, id venusTypes.UUID, changeSpecParams *types.ChangeGasSpecParams) error { + return ms.repo.ActorCfgRepo().UpdateSelectSpecById(ctx, id, changeSpecParams) +} + +func (ms *MessageService) ListActorCfg(ctx context.Context) ([]*types.ActorCfg, error) { + return ms.repo.ActorCfgRepo().ListActorCfg(ctx) +} + +func (ms *MessageService) GetActorCfgByID(ctx context.Context, id venusTypes.UUID) (*types.ActorCfg, error) { + return ms.repo.ActorCfgRepo().GetActorCfgByID(ctx, id) +} + func (ms *MessageService) recordMetricsProc(ctx context.Context) { tm := time.NewTicker(time.Second * 60) defer tm.Stop() diff --git a/service/shared_params_service.go b/service/shared_params_service.go index f48563ce..e5ceb98d 100644 --- a/service/shared_params_service.go +++ b/service/shared_params_service.go @@ -15,13 +15,15 @@ import ( var DefaultMaxFee = venusTypes.MustParseFIL("0.07") var DefSharedParams = &types.SharedSpec{ - ID: 1, - GasOverEstimation: 1.25, - MaxFee: big.Int{Int: DefaultMaxFee.Int}, - GasFeeCap: big.NewInt(0), - GasOverPremium: 0, - SelMsgNum: 20, - BaseFee: big.NewInt(0), + ID: 1, + SelMsgNum: 20, + FeeSpec: types.FeeSpec{ + BaseFee: big.NewInt(0), + GasOverEstimation: 1.25, + MaxFee: big.Int{Int: DefaultMaxFee.Int}, + GasFeeCap: big.NewInt(0), + GasOverPremium: 0, + }, } type SharedParamsService struct { diff --git a/testhelper/utils.go b/testhelper/utils.go index 454db6c1..8100dfab 100644 --- a/testhelper/utils.go +++ b/testhelper/utils.go @@ -84,6 +84,16 @@ func Equal(t *testing.T, expect, actual interface{}) { expectRT := reflect.TypeOf(expect) actualRV := reflect.ValueOf(actual) assert.Equal(t, expectRV.Kind(), actualRV.Kind()) + + if expectRV.Kind() == reflect.Array || expectRT.Kind() == reflect.Slice { + assert.Equal(t, expectRV.Len(), actualRV.Len()) + mLen := expectRV.Len() + for i := 0; i < mLen; i++ { + Equal(t, expectRV.Index(i).Interface(), actualRV.Index(i).Interface()) + } + return + } + if expectRV.Kind() == reflect.Ptr { expectRV = expectRV.Elem() expectRT = expectRT.Elem() @@ -91,6 +101,7 @@ func Equal(t *testing.T, expect, actual interface{}) { if actualRV.Kind() == reflect.Ptr { actualRV = actualRV.Elem() } + assert.Equal(t, expectRV.NumField(), actualRV.NumField()) for i := 0; i < expectRV.NumField(); i++ { expectVal, ok := expectRV.Field(i).Interface().(time.Time) @@ -102,9 +113,9 @@ func Equal(t *testing.T, expect, actual interface{}) { actualVal, ok := actualRV.Field(i).Interface().(time.Time) assert.True(t, ok) if expectVal.IsZero() { - assert.True(t, actualVal.After(expectVal)) + assert.True(t, actualVal == expectVal || actualVal.After(expectVal), expectRT.Field(i).Name) } else if expectRT.Field(i).Name == "UpdatedAt" { - assert.True(t, actualVal.After(expectVal)) + assert.True(t, actualVal == expectVal || actualVal.After(expectVal), expectRT.Field(i).Name) } else { assert.Equal(t, expectVal.Format(timeFormat), actualVal.Format(timeFormat)) }