代码库地址: 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 钱包服务端代码相关推荐

  1. java cxf服务端代码_【JAVA】 cxf 生成 webservice 服务端代码

    CXF Apache CXF = Celtix + XFire.CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持,并且提供了多种 Binding . ...

  2. swagger 返回json字符串_[Swagger] Swagger Codegen 高效开发客户端对接服务端代码

    [Swagger] Swagger Codegen 高效开发客户端对接服务端代码 @TOC 手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链接和更多资源,可以关注其他博 ...

  3. 服务器项目混淆,压缩和混淆node.js服务端代码

    压缩和混淆node.js服务端代码 在前端我们有webpack,gulp等构建工具提供了从项目结构搭建到部署打包,基本所有工作流程所需要的都被覆盖到了. 在后台node.js写的服务端却是透明,很多时 ...

  4. swagger生成对应的客户端、服务端代码

    根据yaml文件生成对应的客户端.服务端代码 前言 ​ 对于早期的webservice接口,我们可以根据wsdl文件生成对应的客户端和服务端代码.那么同样的针对于Restful风格的接口,也有同样的根 ...

  5. 压缩和混淆node.js服务端代码

    压缩和混淆node.js服务端代码 在前端我们有webpack,gulp等构建工具提供了从项目结构搭建到部署打包,基本所有工作流程所需要的都被覆盖到了. 在后台node.js写的服务端却是透明,很多时 ...

  6. 打怪升级之UDP数据接收实验的服务端代码

    C++语言下的windows网络编程(SOCKET) 本文的主要目的是以C++语言在windows下实现UDP网络服务端的代码(VS2022). 所需的头文件调用 #include<iostre ...

  7. 仿京东开放平台框架,开发自己的开放平台(包含需求,服务端代码,SDK代码)...

    目录 1开放平台需求 1.1调用参数 1.2签名算法 2服务端代码,Java举例 2.1接口入口代码 2.2业务逻辑层 2.3基础工具类 3.SDK代码,Java举例 4.集成SDK,代码举例 现在开 ...

  8. 仿京东开放平台框架,开发自己的开放平台(包含需求,服务端代码,SDK代码)

    目录 1开放平台需求 1.1调用参数 1.2签名算法 2服务端代码,Java举例 2.1接口入口代码 2.2业务逻辑层 2.3基础工具类 3.SDK代码,Java举例 4.集成SDK,代码举例 现在开 ...

  9. git钩子放服务器_使用 git post-receive 钩子部署服务端代码

    在 git 中提交服务器源码的时候,如果能够直接更新到测试服务器,并且重启服务使其生效,会节省懒惰的程序员们大量的时间. git 的 Server-side hook (服务端钩子/挂钩)可以用来做件 ...

最新文章

  1. 华为云服务器默认密码怎么修改,云服务器怎么修改登录密码
  2. RDKit | 基于RDKit的单分子多构象生成
  3. retrofit2使用详解_秒懂Retrofit2之Converter
  4. 微信小程序教程02:App(Object)和Page(Object) 构造器介绍
  5. java大文件解析_java大文件(百M以上)的上传下载实例解析
  6. Qt文档阅读笔记-ToolBar QML Type
  7. 从新手到高手 c++全方位学习_股票新手怎样快速入门?关于散户学习炒股的几点建议...
  8. 为什么沃伦·巴菲特有那么多的时间去阅读和思考
  9. html5回到顶部代码,JS返回顶部实例代码
  10. vim 保存出错 E45: readonly option is set (add ! to override)
  11. Ubuntu设置Adsl上网
  12. c语言程序细菌分组实验报告,案例:细菌实验分组 - nimozp的个人空间 - OSCHINA - 中文开源技术交流社区...
  13. android studio中的apk位置
  14. 声学模型训练----Acoustic Modeling
  15. leaflet实现风场图
  16. python curl 获取返回值_python-将curl查询转换为请求
  17. 自定义绘制treeview,重绘treeview
  18. Linux 用户和用户组详解
  19. PC端使用百度地图查询经纬度
  20. 超级厉害的几个网站,一般人我都不告诉

热门文章

  1. FreeRTOS的信号量
  2. 空间转换,动画的说明与使用》
  3. alertmanager监控 Prometheus 告警,alertmanage配置邮件告警
  4. 创建一个简单的守护进程
  5. sqoop conditions原理
  6. 常用的一维时间序列滤波方法
  7. PADS 转allegro 17.2
  8. 盘点几本软件测试入门必读的经典书籍,让你少走一半弯路
  9. 官方认定:程序员属于农民工群体
  10. 关于fontawesome-webfont93e3.ttf加载不到的解决办法