代码结构设计得最好的多链支持的 HD 钱包服务端代码
代码库地址: https://github.com/SavourDao/savour-hd
Savour HD 是 Savour 项目的钱包的 HD. 后端服务,使用 golang 编写,提供 grpc 接口给上层服务访问
这是一个支持多链的 HD 钱包的服务端代码,包含对接个链的 wallet 模块,rpc f服务端,代码设计是清晰,规整;今天我把他推荐给大家使用。
1. 项目运行
1.1. 安装依赖
go mod tidy
1.2. 构建程序
go build 或者 go install savour-hd
1.3. 启动程序
./savour-hd -c ./config.yml
. 1.4. 启动 RPC 接口测试界面
grpcui -plaintext 127.0.0.1:8089
2. 项目简介
上图是该项目的一个大概的设计图,整个项目包含了 RPC 服务,钱包 factory 和与各个底层链的对接。
func main() {var f = flag.String("c", "config.yml", "config path")flag.Parse()conf, err := config.New(*f)if err != nil {panic(err)}dispatcher, err := walletdispatcher.New(conf)if err != nil {log.Error("Setup dispatcher failed", "err", err)panic(err)}grpcServer := grpc.NewServer(grpc.UnaryInterceptor(dispatcher.Interceptor))defer grpcServer.GracefulStop()wallet2.RegisterWalletServiceServer(grpcServer, dispatcher)listen, err := net.Listen("tcp", ":"+conf.Server.Port)if err != nil {log.Error("net listen failed", "err", err)panic(err)}reflection.Register(grpcServer)log.Info("savour dao start success", "port", conf.Server.Port)if err := grpcServer.Serve(listen); err != nil {log.Error("grpc server serve failed", "err", err)panic(err)}
}
package walletdispatcherimport ("context""github.com/SavourDao/savour-hd/rpc/common""github.com/SavourDao/savour-hd/wallet""github.com/SavourDao/savour-hd/wallet/solana""runtime/debug""strings""github.com/SavourDao/savour-hd/config"wallet2 "github.com/SavourDao/savour-hd/rpc/wallet""github.com/SavourDao/savour-hd/wallet/bitcoin""github.com/SavourDao/savour-hd/wallet/ethereum""github.com/ethereum/go-ethereum/log""google.golang.org/grpc""google.golang.org/grpc/codes""google.golang.org/grpc/status"
)type CommonRequest interface {GetChain() string
}type CommonReply = wallet2.SupportCoinsResponsetype ChainType = stringtype WalletDispatcher struct {registry map[ChainType]wallet.WalletAdaptor
}func (d *WalletDispatcher) mustEmbedUnimplementedWalletServiceServer() {//TODO implement mepanic("implement me")
}func New(conf *config.Config) (*WalletDispatcher, error) {dispatcher := WalletDispatcher{registry: make(map[ChainType]wallet.WalletAdaptor),}walletAdaptorFactoryMap := map[string]func(conf *config.Config) (wallet.WalletAdaptor, error){bitcoin.ChainName: bitcoin.NewChainAdaptor,ethereum.ChainName: ethereum.NewChainAdaptor,solana.ChainName: solana.NewChainAdaptor,}supportedChains := []string{bitcoin.ChainName, ethereum.ChainName, solana.ChainName}for _, c := range conf.Chains {if factory, ok := walletAdaptorFactoryMap[c]; ok {adaptor, err := factory(conf)if err != nil {log.Crit("failed to setup chain", "chain", c, "error", err)}dispatcher.registry[c] = adaptor} else {log.Error("unsupported chain", "chain", c, "supportedChains", supportedChains)}}return &dispatcher, nil
}func NewLocal(network config.NetWorkType) *WalletDispatcher {dispatcher := WalletDispatcher{registry: make(map[ChainType]wallet.WalletAdaptor),}walletAdaptorFactoryMap := map[string]func(network config.NetWorkType) wallet.WalletAdaptor{bitcoin.ChainName: bitcoin.NewLocalChainAdaptor,ethereum.ChainName: ethereum.NewLocalWalletAdaptor,}supportedChains := []string{bitcoin.ChainName, ethereum.ChainName}for _, c := range supportedChains {if factory, ok := walletAdaptorFactoryMap[c]; ok {dispatcher.registry[c] = factory(network)}}return &dispatcher
}func (d *WalletDispatcher) Interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {defer func() {if e := recover(); e != nil {log.Error("panic error", "msg", e)log.Debug(string(debug.Stack()))err = status.Errorf(codes.Internal, "Panic err: %v", e)}}()pos := strings.LastIndex(info.FullMethod, "/")method := info.FullMethod[pos+1:]chain := req.(CommonRequest).GetChain()log.Info(method, "chain", chain, "req", req)resp, err = handler(ctx, req)log.Debug("Finish handling", "resp", resp, "err", err)return
}func (d *WalletDispatcher) preHandler(req interface{}) (resp *CommonReply) {chain := req.(CommonRequest).GetChain()if _, ok := d.registry[chain]; !ok {return &CommonReply{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,Support: false,}}return nil
}func (d *WalletDispatcher) GetSupportCoins(ctx context.Context, request *wallet2.SupportCoinsRequest) (*wallet2.SupportCoinsResponse, error) {//TODO implement mepanic("implement me")
}func (d *WalletDispatcher) GetNonce(ctx context.Context, request *wallet2.NonceRequest) (*wallet2.NonceResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.NonceResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,}, nil}return d.registry[request.Chain].GetNonce(request)
}func (d *WalletDispatcher) GetGasPrice(ctx context.Context, request *wallet2.GasPriceRequest) (*wallet2.GasPriceResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.GasPriceResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,Gas: "",}, nil}return d.registry[request.Chain].GetGasPrice(request)
}func (d *WalletDispatcher) SendTx(ctx context.Context, request *wallet2.SendTxRequest) (*wallet2.SendTxResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.SendTxResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,TxHash: "",}, nil}return d.registry[request.Chain].SendTx(request)
}func (d *WalletDispatcher) GetBalance(ctx context.Context, request *wallet2.BalanceRequest) (*wallet2.BalanceResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.BalanceResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,Balance: "",}, nil}return d.registry[request.Chain].GetBalance(request)
}func (d *WalletDispatcher) GetTxByAddress(ctx context.Context, request *wallet2.TxAddressRequest) (*wallet2.TxAddressResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.TxAddressResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,Tx: nil,}, nil}return d.registry[request.Chain].GetTxByAddress(request)
}func (d *WalletDispatcher) GetTxByHash(ctx context.Context, request *wallet2.TxHashRequest) (*wallet2.TxHashResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.TxHashResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,Tx: nil,}, nil}return d.registry[request.Chain].GetTxByHash(request)
}func (d *WalletDispatcher) GetAccount(ctx context.Context, request *wallet2.AccountRequest) (*wallet2.AccountResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.AccountResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,AccountNumber: "",Sequence: "",}, nil}return d.registry[request.Chain].GetAccount(request)
}func (d *WalletDispatcher) GetUtxo(ctx context.Context, request *wallet2.UtxoRequest) (*wallet2.UtxoResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.UtxoResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,}, nil}return d.registry[request.Chain].GetUtxo(request)
}func (d *WalletDispatcher) GetMinRent(ctx context.Context, request *wallet2.MinRentRequest) (*wallet2.MinRentResponse, error) {resp := d.preHandler(request)if resp != nil {return &wallet2.MinRentResponse{Code: common.ReturnCode_ERROR,Msg: config.UnsupportedOperation,Value: "",}, nil}return d.registry[request.Chain].GetMinRent(request)
}
具体的详情大家可以看项目源码。
代码结构设计得最好的多链支持的 HD 钱包服务端代码相关推荐
- java cxf服务端代码_【JAVA】 cxf 生成 webservice 服务端代码
CXF Apache CXF = Celtix + XFire.CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持,并且提供了多种 Binding . ...
- swagger 返回json字符串_[Swagger] Swagger Codegen 高效开发客户端对接服务端代码
[Swagger] Swagger Codegen 高效开发客户端对接服务端代码 @TOC 手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链接和更多资源,可以关注其他博 ...
- 服务器项目混淆,压缩和混淆node.js服务端代码
压缩和混淆node.js服务端代码 在前端我们有webpack,gulp等构建工具提供了从项目结构搭建到部署打包,基本所有工作流程所需要的都被覆盖到了. 在后台node.js写的服务端却是透明,很多时 ...
- swagger生成对应的客户端、服务端代码
根据yaml文件生成对应的客户端.服务端代码 前言 对于早期的webservice接口,我们可以根据wsdl文件生成对应的客户端和服务端代码.那么同样的针对于Restful风格的接口,也有同样的根 ...
- 压缩和混淆node.js服务端代码
压缩和混淆node.js服务端代码 在前端我们有webpack,gulp等构建工具提供了从项目结构搭建到部署打包,基本所有工作流程所需要的都被覆盖到了. 在后台node.js写的服务端却是透明,很多时 ...
- 打怪升级之UDP数据接收实验的服务端代码
C++语言下的windows网络编程(SOCKET) 本文的主要目的是以C++语言在windows下实现UDP网络服务端的代码(VS2022). 所需的头文件调用 #include<iostre ...
- 仿京东开放平台框架,开发自己的开放平台(包含需求,服务端代码,SDK代码)...
目录 1开放平台需求 1.1调用参数 1.2签名算法 2服务端代码,Java举例 2.1接口入口代码 2.2业务逻辑层 2.3基础工具类 3.SDK代码,Java举例 4.集成SDK,代码举例 现在开 ...
- 仿京东开放平台框架,开发自己的开放平台(包含需求,服务端代码,SDK代码)
目录 1开放平台需求 1.1调用参数 1.2签名算法 2服务端代码,Java举例 2.1接口入口代码 2.2业务逻辑层 2.3基础工具类 3.SDK代码,Java举例 4.集成SDK,代码举例 现在开 ...
- git钩子放服务器_使用 git post-receive 钩子部署服务端代码
在 git 中提交服务器源码的时候,如果能够直接更新到测试服务器,并且重启服务使其生效,会节省懒惰的程序员们大量的时间. git 的 Server-side hook (服务端钩子/挂钩)可以用来做件 ...
最新文章
- 华为云服务器默认密码怎么修改,云服务器怎么修改登录密码
- RDKit | 基于RDKit的单分子多构象生成
- retrofit2使用详解_秒懂Retrofit2之Converter
- 微信小程序教程02:App(Object)和Page(Object) 构造器介绍
- java大文件解析_java大文件(百M以上)的上传下载实例解析
- Qt文档阅读笔记-ToolBar QML Type
- 从新手到高手 c++全方位学习_股票新手怎样快速入门?关于散户学习炒股的几点建议...
- 为什么沃伦·巴菲特有那么多的时间去阅读和思考
- html5回到顶部代码,JS返回顶部实例代码
- vim 保存出错 E45: readonly option is set (add ! to override)
- Ubuntu设置Adsl上网
- c语言程序细菌分组实验报告,案例:细菌实验分组 - nimozp的个人空间 - OSCHINA - 中文开源技术交流社区...
- android studio中的apk位置
- 声学模型训练----Acoustic Modeling
- leaflet实现风场图
- python curl 获取返回值_python-将curl查询转换为请求
- 自定义绘制treeview,重绘treeview
- Linux 用户和用户组详解
- PC端使用百度地图查询经纬度
- 超级厉害的几个网站,一般人我都不告诉