From 61db1e91d96f044b957907c99b6131c29ec8e59d Mon Sep 17 00:00:00 2001 From: simlecode <69969590+simlecode@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:13:04 +0800 Subject: [PATCH] feat: Use sophon gateway to sign f3 message --- app/node/builder.go | 2 +- app/node/node.go | 2 + app/submodule/f3/f3_submodule.go | 5 +- app/submodule/wallet/wallet_submodule.go | 44 ++++++-- cmd/daemon.go | 7 ++ cmd/main.go | 2 + pkg/config/config.go | 1 + pkg/vf3/ec.go | 12 +-- pkg/vf3/f3.go | 8 +- pkg/vf3/signer.go | 6 +- pkg/wallet/gateway/wallet_gateway.go | 117 ++++++++++++++++++++++ pkg/wallet/gateway/wallet_gateway_test.go | 36 +++++++ pkg/wallet/wallet.go | 2 + venus-shared/types/msg_meta.go | 2 + venus-shared/types/wallet/sign_types.go | 16 +++ 15 files changed, 236 insertions(+), 26 deletions(-) create mode 100644 pkg/wallet/gateway/wallet_gateway.go create mode 100644 pkg/wallet/gateway/wallet_gateway_test.go diff --git a/app/node/builder.go b/app/node/builder.go index 19874d5d5a..52545aa847 100644 --- a/app/node/builder.go +++ b/app/node/builder.go @@ -137,7 +137,7 @@ func (b *Builder) build(ctx context.Context) (*Node, error) { return nil, errors.Wrap(err, "failed to build node.wallet") } - nd.f3, err = f3.NewF3Submodule(ctx, nd.repo, nd.chain, nd.network, nd.wallet.API()) + nd.f3, err = f3.NewF3Submodule(ctx, nd.repo, nd.chain, nd.network, nd.wallet.GetWalletSign()) if err != nil { return nil, errors.Wrap(err, "failed to build node.f3") } diff --git a/app/node/node.go b/app/node/node.go index 12c4d66dfc..b4d7953688 100644 --- a/app/node/node.go +++ b/app/node/node.go @@ -249,6 +249,8 @@ func (node *Node) Stop(ctx context.Context) { log.Warnf("error shutdown jaeger-tracing: %w", err) } } + + node.Wallet().WalletGateway.Close() } // RunRPCAndWait start rpc server and listen to signal to exit diff --git a/app/submodule/f3/f3_submodule.go b/app/submodule/f3/f3_submodule.go index 722b0ea75c..0daf3f90e4 100644 --- a/app/submodule/f3/f3_submodule.go +++ b/app/submodule/f3/f3_submodule.go @@ -7,6 +7,7 @@ import ( "github.com/filecoin-project/venus/app/submodule/network" "github.com/filecoin-project/venus/pkg/repo" "github.com/filecoin-project/venus/pkg/vf3" + "github.com/filecoin-project/venus/pkg/wallet" v1api "github.com/filecoin-project/venus/venus-shared/api/chain/v1" logging "github.com/ipfs/go-log" ) @@ -21,7 +22,7 @@ func NewF3Submodule(ctx context.Context, repo repo.Repo, chain *chain.ChainSubmodule, network *network.NetworkSubmodule, - walletAPI v1api.IWallet, + walletSign wallet.WalletSignFunc, ) (*F3Submodule, error) { netConf := repo.Config().NetworkParams if !netConf.F3Enabled { @@ -36,7 +37,7 @@ func NewF3Submodule(ctx context.Context, ChainStore: chain.ChainReader, StateManager: chain.Stmgr, Datastore: repo.MetaDatastore(), - Wallet: walletAPI, + WalletSign: walletSign, ManifestProvider: vf3.NewManifestProvider(network.NetworkName, repo.MetaDatastore(), network.Pubsub, netConf), }) if err != nil { diff --git a/app/submodule/wallet/wallet_submodule.go b/app/submodule/wallet/wallet_submodule.go index 6109cd6e05..7b9026b8ff 100644 --- a/app/submodule/wallet/wallet_submodule.go +++ b/app/submodule/wallet/wallet_submodule.go @@ -2,6 +2,8 @@ package wallet import ( "context" + "fmt" + "strings" v0api "github.com/filecoin-project/venus/venus-shared/api/chain/v0" v1api "github.com/filecoin-project/venus/venus-shared/api/chain/v1" @@ -16,6 +18,7 @@ import ( "github.com/filecoin-project/venus/pkg/repo" "github.com/filecoin-project/venus/pkg/state" "github.com/filecoin-project/venus/pkg/wallet" + "github.com/filecoin-project/venus/pkg/wallet/gateway" "github.com/filecoin-project/venus/venus-shared/types" ) @@ -23,11 +26,12 @@ var log = logging.Logger("wallet") // WalletSubmodule enhances the `Node` with a "wallet" and FIL transfer capabilities. type WalletSubmodule struct { // nolint - Chain *chain.ChainSubmodule - Wallet *wallet.Wallet - adapter wallet.WalletIntersection - Signer types.Signer - Config *config.ConfigModule + Chain *chain.ChainSubmodule + Wallet *wallet.Wallet + adapter wallet.WalletIntersection + Signer types.Signer + Config *config.ConfigModule + WalletGateway *gateway.WalletGateway } type walletRepo interface { @@ -66,12 +70,25 @@ func NewWalletSubmodule(ctx context.Context, } else { adapter = fcWallet } + + var wg *gateway.WalletGateway + if len(repo.Config().Wallet.GatewayBacked) != 0 { + // GatewayBacked token:url + tokenURL := strings.SplitN(repo.Config().Wallet.GatewayBacked, ":", 2) + fmt.Println(tokenURL) + wg, err = gateway.NewWalletGateway(ctx, tokenURL[1], tokenURL[0]) + if err != nil { + return nil, err + } + log.Info("wallet gateway set up") + } return &WalletSubmodule{ - Config: cfgModule, - Chain: chain, - Wallet: fcWallet, - adapter: adapter, - Signer: state.NewSigner(headSigner, fcWallet), + Config: cfgModule, + Chain: chain, + Wallet: fcWallet, + adapter: adapter, + Signer: state.NewSigner(headSigner, fcWallet), + WalletGateway: wg, }, nil } @@ -94,6 +111,13 @@ func (wallet *WalletSubmodule) WalletIntersection() wallet.WalletIntersection { return wallet.adapter } +func (wallet *WalletSubmodule) GetWalletSign() wallet.WalletSignFunc { + if wallet.WalletGateway == nil { + return wallet.adapter.WalletSign + } + return wallet.WalletGateway.WalletSign +} + func getPassphraseConfig(cfg *pconfig.Config) (pconfig.PassphraseConfig, error) { return pconfig.PassphraseConfig{ ScryptN: cfg.Wallet.PassphraseConfig.ScryptN, diff --git a/cmd/daemon.go b/cmd/daemon.go index acffc586ce..d3ae638c09 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -58,6 +58,7 @@ var daemonCmd = &cmds.Command{ cmds.StringOption(Network, "when set, populates config with network specific parameters, eg. mainnet,2k,calibrationnet,interopnet,butterflynet").WithDefault("mainnet"), cmds.StringOption(Password, "set wallet password"), cmds.StringOption(Profile, "specify type of node, eg. bootstrapper"), + cmds.StringOption(WalletGateway, "set sophon gateway url and token, eg. token:url"), }, Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error { if limit, _ := req.Options[ULimit].(bool); limit { @@ -168,6 +169,9 @@ func initRun(req *cmds.Request, repoDir string) error { return fmt.Errorf("must also pass token with venus auth service to `--%s`", AuthServiceToken) } } + if walletGateway, ok := req.Options[WalletGateway].(string); ok && len(walletGateway) > 0 { + cfg.Wallet.GatewayBacked = walletGateway + } if err := rep.ReplaceConfig(cfg); err != nil { log.Errorf("Error replacing config %s", err) @@ -240,6 +244,9 @@ func daemonRun(req *cmds.Request, re cmds.ResponseEmitter) error { if len(config.API.VenusAuthURL)+len(config.API.VenusAuthToken) > 0 && len(config.API.VenusAuthToken)*len(config.API.VenusAuthURL) == 0 { return fmt.Errorf("must set both venus auth service url and token at the same time") } + if walletGateway, ok := req.Options[WalletGateway].(string); ok && len(walletGateway) > 0 { + config.Wallet.GatewayBacked = walletGateway + } if bootPeers, ok := req.Options[BootstrapPeers].([]string); ok && len(bootPeers) > 0 { config.Bootstrap.AddPeers(bootPeers...) diff --git a/cmd/main.go b/cmd/main.go index 999f4e4347..e7a2e45805 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -79,6 +79,8 @@ const ( AuthServiceURL = "auth-url" AuthServiceToken = "auth-token" + WalletGateway = "wallet-gateway" + BootstrapPeers = "bootstrap-peers" Profile = "profile" diff --git a/pkg/config/config.go b/pkg/config/config.go index 60a86d7aef..8d47b89678 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -166,6 +166,7 @@ type WalletConfig struct { PassphraseConfig PassphraseConfig `json:"passphraseConfig,omitempty"` RemoteEnable bool `json:"remoteEnable"` RemoteBackend string `json:"remoteBackend"` + GatewayBacked string `json:"gatewayBacked"` } type PassphraseConfig struct { diff --git a/pkg/vf3/ec.go b/pkg/vf3/ec.go index ee54afbaf1..3c3dff8037 100644 --- a/pkg/vf3/ec.go +++ b/pkg/vf3/ec.go @@ -77,12 +77,12 @@ func (ec *ecWrapper) GetTipsetByEpoch(ctx context.Context, epoch int64) (ec.TipS } func (ec *ecWrapper) GetTipset(ctx context.Context, tsk gpbft.TipSetKey) (ec.TipSet, error) { - tskLotus, err := types.TipSetKeyFromBytes(tsk) + key, err := types.TipSetKeyFromBytes(tsk) if err != nil { return nil, fmt.Errorf("decoding tsk: %w", err) } - ts, err := ec.ChainStore.GetTipSet(ctx, tskLotus) + ts, err := ec.ChainStore.GetTipSet(ctx, key) if err != nil { return nil, fmt.Errorf("getting tipset by key: %w", err) } @@ -104,11 +104,11 @@ func (ec *ecWrapper) GetParent(ctx context.Context, tsF3 ec.TipSet) (ec.TipSet, // // TODO: Revisit the type check here and remove as needed once testing // is over and fake EC backend goes away. - tskLotus, err := types.TipSetKeyFromBytes(tsF3.Key()) + tsk, err := types.TipSetKeyFromBytes(tsF3.Key()) if err != nil { return nil, fmt.Errorf("decoding tsk: %w", err) } - ts, err = ec.ChainStore.GetTipSet(ctx, tskLotus) + ts, err = ec.ChainStore.GetTipSet(ctx, tsk) if err != nil { return nil, fmt.Errorf("getting tipset by key for get parent: %w", err) } @@ -125,10 +125,10 @@ func (ec *ecWrapper) GetPowerTable(ctx context.Context, tskF3 gpbft.TipSetKey) ( if err != nil { return nil, fmt.Errorf("decoding tsk: %w", err) } - return ec.getPowerTableLotusTSK(ctx, tsk) + return ec.getPowerTableTSK(ctx, tsk) } -func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSetKey) (gpbft.PowerEntries, error) { +func (ec *ecWrapper) getPowerTableTSK(ctx context.Context, tsk types.TipSetKey) (gpbft.PowerEntries, error) { ts, err := ec.ChainStore.GetTipSet(ctx, tsk) if err != nil { return nil, fmt.Errorf("getting tipset by key for get parent: %w", err) diff --git a/pkg/vf3/f3.go b/pkg/vf3/f3.go index b5fcb82c6f..fad5d31d9f 100644 --- a/pkg/vf3/f3.go +++ b/pkg/vf3/f3.go @@ -21,7 +21,7 @@ import ( "github.com/filecoin-project/go-f3/manifest" "github.com/filecoin-project/venus/pkg/chain" "github.com/filecoin-project/venus/pkg/statemanger" - v1api "github.com/filecoin-project/venus/venus-shared/api/chain/v1" + "github.com/filecoin-project/venus/pkg/wallet" "github.com/filecoin-project/venus/venus-shared/types" logging "github.com/ipfs/go-log" ) @@ -56,7 +56,7 @@ type F3Params struct { ChainStore *chain.Store StateManager *statemanger.Stmgr Datastore datastore.Batching - Wallet v1api.IWallet + WalletSign wallet.WalletSignFunc } var log = logging.Logger("f3") @@ -79,7 +79,7 @@ func New(mctx context.Context, params F3Params) (*F3, error) { fff := &F3{ inner: module, ec: ec, - signer: &signer{params.Wallet}, + signer: &signer{sign: params.WalletSign}, newLeases: make(chan leaseRequest, 4), // some buffer to avoid } @@ -174,7 +174,7 @@ func (fff *F3) GetLatestCert(ctx context.Context) (*certs.FinalityCertificate, e } func (fff *F3) GetPowerTable(ctx context.Context, tsk types.TipSetKey) (gpbft.PowerEntries, error) { - return fff.ec.getPowerTableLotusTSK(ctx, tsk) + return fff.ec.getPowerTableTSK(ctx, tsk) } func (fff *F3) GetF3PowerTable(ctx context.Context, tsk types.TipSetKey) (gpbft.PowerEntries, error) { diff --git a/pkg/vf3/signer.go b/pkg/vf3/signer.go index 08ef83c4bf..2938d987b1 100644 --- a/pkg/vf3/signer.go +++ b/pkg/vf3/signer.go @@ -6,12 +6,12 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-f3/gpbft" - v1api "github.com/filecoin-project/venus/venus-shared/api/chain/v1" + "github.com/filecoin-project/venus/pkg/wallet" "github.com/filecoin-project/venus/venus-shared/types" ) type signer struct { - wallet v1api.IWallet + sign wallet.WalletSignFunc } // Sign signs a message with the private key corresponding to a public key. @@ -21,7 +21,7 @@ func (s *signer) Sign(ctx context.Context, sender gpbft.PubKey, msg []byte) ([]b if err != nil { return nil, fmt.Errorf("converting pubkey to address: %w", err) } - sig, err := s.wallet.WalletSign(ctx, addr, msg, types.MsgMeta{Type: types.MTUnknown}) + sig, err := s.sign(ctx, addr, msg, types.MsgMeta{Type: types.MTF3}) if err != nil { return nil, fmt.Errorf("error while signing: %w", err) } diff --git a/pkg/wallet/gateway/wallet_gateway.go b/pkg/wallet/gateway/wallet_gateway.go new file mode 100644 index 0000000000..1a28c9d679 --- /dev/null +++ b/pkg/wallet/gateway/wallet_gateway.go @@ -0,0 +1,117 @@ +package gateway + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/venus/pkg/crypto" + gatewayAPI "github.com/filecoin-project/venus/venus-shared/api/gateway/v2" + "github.com/filecoin-project/venus/venus-shared/types" + logging "github.com/ipfs/go-log/v2" +) + +var log = logging.Logger("wallet-gateway") + +type WalletGateway struct { + cli gatewayAPI.IWalletClient + closer jsonrpc.ClientCloser + + addressAccount map[address.Address][]string + + lk sync.RWMutex +} + +func NewWalletGateway(ctx context.Context, url, token string) (*WalletGateway, error) { + cli, close, err := gatewayAPI.DialIGatewayRPC(ctx, url, token, nil) + if err != nil { + return nil, err + } + + wg := &WalletGateway{cli: cli, closer: close, addressAccount: map[address.Address][]string{}} + err = wg.updateAddressAccount(ctx) + if err != nil { + return nil, err + } + go wg.loopUpdateAddressAccount(ctx) + + return wg, nil +} + +func (w *WalletGateway) updateAddressAccount(ctx context.Context) error { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + w.lk.Lock() + defer w.lk.Unlock() + + wds, err := w.cli.ListWalletInfo(ctx) + if err != nil { + return err + } + + for _, wd := range wds { + accountMap := map[string]struct{}{ + wd.Account: {}, + } + accounts := []string{wd.Account} + for _, account := range wd.SupportAccounts { + if _, ok := accountMap[account]; ok { + continue + } + accounts = append(accounts, account) + accountMap[account] = struct{}{} + } + + addrs := make(map[address.Address]struct{}, 0) + for _, cs := range wd.ConnectStates { + for _, addr := range cs.Addrs { + addrs[addr] = struct{}{} + } + } + + for addr := range addrs { + w.addressAccount[addr] = accounts + } + } + + return err +} + +func (w *WalletGateway) loopUpdateAddressAccount(ctx context.Context) { + ticker := time.NewTicker(10 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + err := w.updateAddressAccount(ctx) + if err != nil { + log.Errorf("update address account failed: %s", err) + } + case <-ctx.Done(): + return + } + } +} + +func (w *WalletGateway) Close() { + if w == nil { + return + } + w.closer() +} + +func (w *WalletGateway) WalletSign(ctx context.Context, k address.Address, msg []byte, meta types.MsgMeta) (*crypto.Signature, error) { + w.lk.RLock() + accounts, ok := w.addressAccount[k] + w.lk.RUnlock() + if !ok { + return nil, fmt.Errorf("address %s not found", k) + } + + return w.cli.WalletSign(ctx, k, accounts, msg, meta) +} diff --git a/pkg/wallet/gateway/wallet_gateway_test.go b/pkg/wallet/gateway/wallet_gateway_test.go new file mode 100644 index 0000000000..4d3b22b9f1 --- /dev/null +++ b/pkg/wallet/gateway/wallet_gateway_test.go @@ -0,0 +1,36 @@ +package gateway + +import ( + "context" + "fmt" + "testing" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/venus/pkg/crypto" + _ "github.com/filecoin-project/venus/pkg/crypto/bls" + _ "github.com/filecoin-project/venus/pkg/crypto/secp" + "github.com/filecoin-project/venus/venus-shared/types" + "github.com/stretchr/testify/assert" +) + +func TestUpdateAddress(t *testing.T) { + t.SkipNow() + url := "" + token := "" + + wg, err := NewWalletGateway(context.Background(), url, token) + assert.NoError(t, err) + var a address.Address + for addr, accounts := range wg.addressAccount { + fmt.Println(addr, accounts) + a = addr + } + data := []byte("data to be signed") + sig, err := wg.WalletSign(context.Background(), a, data, types.MsgMeta{ + Type: types.MTF3, + }) + assert.NoError(t, err) + fmt.Println(a, sig) + + assert.NoError(t, crypto.Verify(sig, a, data)) +} diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index 4148b243b5..3dd83e3eeb 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -25,6 +25,8 @@ var ( walletLog = logging.Logger("wallet") ) +type WalletSignFunc func(ctx context.Context, addr address.Address, msg []byte, meta types.MsgMeta) (*crypto.Signature, error) + // WalletIntersection // nolint type WalletIntersection interface { diff --git a/venus-shared/types/msg_meta.go b/venus-shared/types/msg_meta.go index 9f59b2f86e..a3c08d12d7 100644 --- a/venus-shared/types/msg_meta.go +++ b/venus-shared/types/msg_meta.go @@ -38,6 +38,8 @@ const ( MTProviderDealState = MsgType("providerdealstate") MTVerifyAddress = MsgType("verifyaddress") + + MTF3 = MsgType("f3") ) type MsgMeta struct { diff --git a/venus-shared/types/wallet/sign_types.go b/venus-shared/types/wallet/sign_types.go index adf236ce2a..e69b525ab7 100644 --- a/venus-shared/types/wallet/sign_types.go +++ b/venus-shared/types/wallet/sign_types.go @@ -176,6 +176,22 @@ var SupportedMsgTypes = map[types.MsgType]*Types{ return in, nil }, }, + types.MTF3: { + Type: reflect.TypeOf([]byte{}), + SignBytes: func(in interface{}) ([]byte, error) { + msg, isOk := in.([]byte) + if !isOk { + return nil, fmt.Errorf("MTF3 must be []byte") + } + return msg, nil + }, + ParseObj: func(in []byte, meta types.MsgMeta) (interface{}, error) { + if meta.Type == types.MTF3 { + return in, nil + } + return nil, fmt.Errorf("un-expected MsgType:%s", meta.Type) + }, + }, } // GetSignBytesAndObj Matches the type and returns the data that needs to be signed