WIP: Nitro integration #76

Draft
roysc wants to merge 3 commits from roysc/nitro-integration into main
179 changed files with 15634 additions and 3059 deletions

View File

@ -1,4 +1,4 @@
FROM golang:1.21-bullseye AS builder
FROM golang:1.23.6-bookworm AS builder
# Set working directory for the build
WORKDIR /go/src/git.vdb.to/cerc-io/laconicd
@ -14,21 +14,24 @@ COPY . .
RUN make build
# Final image
FROM ubuntu:22.04
# DEV golang image
FROM golang:1.23.6-bookworm AS base
# Install ca-certificates, jq, curl, bash, and other necessary packages
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
jq curl netcat bash bc \
jq curl netcat-openbsd bash bc yq \
&& rm -rf /var/lib/apt/lists/*
# Copy over binary from the builder
COPY --from=builder /go/src/git.vdb.to/cerc-io/laconicd/build/laconicd /usr/bin/laconicd
# FROM base
# Copy over init script from builder
COPY --from=builder /go/src/git.vdb.to/cerc-io/laconicd/scripts/init.sh scripts/init.sh
# # Copy over binary from the builder
# COPY --from=builder /go/src/git.vdb.to/cerc-io/laconicd/build/laconicd /usr/bin/laconicd
WORKDIR /
# # Copy over init script from builder
# COPY --from=builder /go/src/git.vdb.to/cerc-io/laconicd/scripts/init.sh scripts/init.sh
# WORKDIR /
# Run laconicd by default
CMD ["laconicd"]
ENTRYPOINT ["laconicd"]

View File

@ -12,7 +12,7 @@ ifeq (,$(VERSION))
VERSION := $(shell git describe --exact-match 2>/dev/null)
# if VERSION is empty, then populate it with branch's name and raw commit hash
ifeq (,$(VERSION))
VERSION := $(BRANCH)-$(COMMIT)
VERSION := $(BRANCH)-$(COMMIT)
endif
endif
@ -31,7 +31,7 @@ BUILDDIR ?= $(CURDIR)/build
###########
go.sum: go.mod
echo "Ensure dependencies have not been modified ..." >&2
@echo "Ensure dependencies have not been modified ..." >&2
go mod verify
go mod tidy
@ -42,7 +42,7 @@ build-linux:
GOOS=linux GOARCH=amd64 LEDGER_ENABLED=false $(MAKE) build
$(BUILD_TARGETS): go.sum $(BUILDDIR)/
@echo "--> installing laconicd"
@echo "--> $@ing laconicd"
go $@ $(BUILD_FLAGS) $(BUILD_ARGS) ./cmd/laconicd
$(BUILDDIR)/:
@ -56,22 +56,23 @@ all: install
### Protobuf ###
##################
protoVer=0.14.0
protoVer=0.17.1
protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer)
protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName)
protoVolumes=-v $(shell readlink -f ..):/mod
protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoVolumes) $(protoImageName)
proto-all: proto-format proto-lint proto-gen
proto-gen:
@echo "Generating protobuf files..."
@$(protoImage) sh ./scripts/protocgen.sh
@$(protoRun) sh ./scripts/protocgen.sh
@go mod tidy
proto-format:
@$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \;
@$(protoRun) find ./ -name "*.proto" -exec clang-format -i {} \;
proto-lint:
@$(protoImage) buf lint proto/ --error-format=json
@$(protoRun) buf lint proto/ --error-format=json
.PHONY: proto-all proto-gen proto-format proto-lint
@ -106,3 +107,7 @@ test-e2e:
test-unit:
go test ./utils/... ./cmd/... -mod=readonly -test.v
test-system: build
test -d ./tests/system/binaries || ln -sf $(BUILDDIR) ./tests/system/binaries
go test ./tests/system -v

View File

@ -37,8 +37,8 @@ Run tests:
# integration tests
make test-integration
# e2e tests
make test-e2e
# system tests
make test-system
```
## Community

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/auction/v1/query.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Query_Params_FullMethodName = "/cerc.auction.v1.Query/Params"
@ -32,6 +32,8 @@ const (
// QueryClient is the client API for Query service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Query defines the gRPC querier interface for the auction module
type QueryClient interface {
// Params queries auction module params
Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
@ -60,8 +62,9 @@ func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
}
func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryParamsResponse)
err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -69,8 +72,9 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts .
}
func (c *queryClient) Auctions(ctx context.Context, in *QueryAuctionsRequest, opts ...grpc.CallOption) (*QueryAuctionsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryAuctionsResponse)
err := c.cc.Invoke(ctx, Query_Auctions_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Auctions_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -78,8 +82,9 @@ func (c *queryClient) Auctions(ctx context.Context, in *QueryAuctionsRequest, op
}
func (c *queryClient) GetAuction(ctx context.Context, in *QueryGetAuctionRequest, opts ...grpc.CallOption) (*QueryGetAuctionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetAuctionResponse)
err := c.cc.Invoke(ctx, Query_GetAuction_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetAuction_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -87,8 +92,9 @@ func (c *queryClient) GetAuction(ctx context.Context, in *QueryGetAuctionRequest
}
func (c *queryClient) GetBid(ctx context.Context, in *QueryGetBidRequest, opts ...grpc.CallOption) (*QueryGetBidResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetBidResponse)
err := c.cc.Invoke(ctx, Query_GetBid_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetBid_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -96,8 +102,9 @@ func (c *queryClient) GetBid(ctx context.Context, in *QueryGetBidRequest, opts .
}
func (c *queryClient) GetBids(ctx context.Context, in *QueryGetBidsRequest, opts ...grpc.CallOption) (*QueryGetBidsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetBidsResponse)
err := c.cc.Invoke(ctx, Query_GetBids_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetBids_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -105,8 +112,9 @@ func (c *queryClient) GetBids(ctx context.Context, in *QueryGetBidsRequest, opts
}
func (c *queryClient) AuctionsByBidder(ctx context.Context, in *QueryAuctionsByBidderRequest, opts ...grpc.CallOption) (*QueryAuctionsByBidderResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryAuctionsByBidderResponse)
err := c.cc.Invoke(ctx, Query_AuctionsByBidder_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_AuctionsByBidder_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -114,8 +122,9 @@ func (c *queryClient) AuctionsByBidder(ctx context.Context, in *QueryAuctionsByB
}
func (c *queryClient) AuctionsByOwner(ctx context.Context, in *QueryAuctionsByOwnerRequest, opts ...grpc.CallOption) (*QueryAuctionsByOwnerResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryAuctionsByOwnerResponse)
err := c.cc.Invoke(ctx, Query_AuctionsByOwner_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_AuctionsByOwner_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -123,8 +132,9 @@ func (c *queryClient) AuctionsByOwner(ctx context.Context, in *QueryAuctionsByOw
}
func (c *queryClient) GetAuctionModuleBalance(ctx context.Context, in *QueryGetAuctionModuleBalanceRequest, opts ...grpc.CallOption) (*QueryGetAuctionModuleBalanceResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetAuctionModuleBalanceResponse)
err := c.cc.Invoke(ctx, Query_GetAuctionModuleBalance_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetAuctionModuleBalance_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -133,7 +143,9 @@ func (c *queryClient) GetAuctionModuleBalance(ctx context.Context, in *QueryGetA
// QueryServer is the server API for Query service.
// All implementations must embed UnimplementedQueryServer
// for forward compatibility
// for forward compatibility.
//
// Query defines the gRPC querier interface for the auction module
type QueryServer interface {
// Params queries auction module params
Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
@ -154,9 +166,12 @@ type QueryServer interface {
mustEmbedUnimplementedQueryServer()
}
// UnimplementedQueryServer must be embedded to have forward compatible implementations.
type UnimplementedQueryServer struct {
}
// UnimplementedQueryServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedQueryServer struct{}
func (UnimplementedQueryServer) Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
@ -183,6 +198,7 @@ func (UnimplementedQueryServer) GetAuctionModuleBalance(context.Context, *QueryG
return nil, status.Errorf(codes.Unimplemented, "method GetAuctionModuleBalance not implemented")
}
func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}
func (UnimplementedQueryServer) testEmbeddedByValue() {}
// UnsafeQueryServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to QueryServer will
@ -192,6 +208,13 @@ type UnsafeQueryServer interface {
}
func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) {
// If the following call pancis, it indicates UnimplementedQueryServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Query_ServiceDesc, srv)
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/auction/v1/tx.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Msg_CreateAuction_FullMethodName = "/cerc.auction.v1.Msg/CreateAuction"
@ -29,6 +29,8 @@ const (
// MsgClient is the client API for Msg service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Tx defines the gRPC tx interface
type MsgClient interface {
// CreateAuction is the command for creating an auction
CreateAuction(ctx context.Context, in *MsgCreateAuction, opts ...grpc.CallOption) (*MsgCreateAuctionResponse, error)
@ -52,8 +54,9 @@ func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
}
func (c *msgClient) CreateAuction(ctx context.Context, in *MsgCreateAuction, opts ...grpc.CallOption) (*MsgCreateAuctionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgCreateAuctionResponse)
err := c.cc.Invoke(ctx, Msg_CreateAuction_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_CreateAuction_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -61,8 +64,9 @@ func (c *msgClient) CreateAuction(ctx context.Context, in *MsgCreateAuction, opt
}
func (c *msgClient) CommitBid(ctx context.Context, in *MsgCommitBid, opts ...grpc.CallOption) (*MsgCommitBidResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgCommitBidResponse)
err := c.cc.Invoke(ctx, Msg_CommitBid_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_CommitBid_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -70,8 +74,9 @@ func (c *msgClient) CommitBid(ctx context.Context, in *MsgCommitBid, opts ...grp
}
func (c *msgClient) RevealBid(ctx context.Context, in *MsgRevealBid, opts ...grpc.CallOption) (*MsgRevealBidResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgRevealBidResponse)
err := c.cc.Invoke(ctx, Msg_RevealBid_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_RevealBid_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -79,8 +84,9 @@ func (c *msgClient) RevealBid(ctx context.Context, in *MsgRevealBid, opts ...grp
}
func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgUpdateParamsResponse)
err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -88,8 +94,9 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts
}
func (c *msgClient) ReleaseFunds(ctx context.Context, in *MsgReleaseFunds, opts ...grpc.CallOption) (*MsgReleaseFundsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgReleaseFundsResponse)
err := c.cc.Invoke(ctx, Msg_ReleaseFunds_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_ReleaseFunds_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -98,7 +105,9 @@ func (c *msgClient) ReleaseFunds(ctx context.Context, in *MsgReleaseFunds, opts
// MsgServer is the server API for Msg service.
// All implementations must embed UnimplementedMsgServer
// for forward compatibility
// for forward compatibility.
//
// Tx defines the gRPC tx interface
type MsgServer interface {
// CreateAuction is the command for creating an auction
CreateAuction(context.Context, *MsgCreateAuction) (*MsgCreateAuctionResponse, error)
@ -114,9 +123,12 @@ type MsgServer interface {
mustEmbedUnimplementedMsgServer()
}
// UnimplementedMsgServer must be embedded to have forward compatible implementations.
type UnimplementedMsgServer struct {
}
// UnimplementedMsgServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMsgServer struct{}
func (UnimplementedMsgServer) CreateAuction(context.Context, *MsgCreateAuction) (*MsgCreateAuctionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAuction not implemented")
@ -134,6 +146,7 @@ func (UnimplementedMsgServer) ReleaseFunds(context.Context, *MsgReleaseFunds) (*
return nil, status.Errorf(codes.Unimplemented, "method ReleaseFunds not implemented")
}
func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
func (UnimplementedMsgServer) testEmbeddedByValue() {}
// UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MsgServer will
@ -143,6 +156,13 @@ type UnsafeMsgServer interface {
}
func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
// If the following call pancis, it indicates UnimplementedMsgServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Msg_ServiceDesc, srv)
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/bond/v1/query.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Query_Params_FullMethodName = "/cerc.bond.v1.Query/Params"
@ -29,6 +29,8 @@ const (
// QueryClient is the client API for Query service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Query defines the gRPC querier service for bond module
type QueryClient interface {
// Params queries bonds module params.
Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
@ -51,8 +53,9 @@ func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
}
func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryParamsResponse)
err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -60,8 +63,9 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts .
}
func (c *queryClient) Bonds(ctx context.Context, in *QueryBondsRequest, opts ...grpc.CallOption) (*QueryBondsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryBondsResponse)
err := c.cc.Invoke(ctx, Query_Bonds_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Bonds_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -69,8 +73,9 @@ func (c *queryClient) Bonds(ctx context.Context, in *QueryBondsRequest, opts ...
}
func (c *queryClient) GetBondById(ctx context.Context, in *QueryGetBondByIdRequest, opts ...grpc.CallOption) (*QueryGetBondByIdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetBondByIdResponse)
err := c.cc.Invoke(ctx, Query_GetBondById_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetBondById_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -78,8 +83,9 @@ func (c *queryClient) GetBondById(ctx context.Context, in *QueryGetBondByIdReque
}
func (c *queryClient) GetBondsByOwner(ctx context.Context, in *QueryGetBondsByOwnerRequest, opts ...grpc.CallOption) (*QueryGetBondsByOwnerResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetBondsByOwnerResponse)
err := c.cc.Invoke(ctx, Query_GetBondsByOwner_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetBondsByOwner_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -87,8 +93,9 @@ func (c *queryClient) GetBondsByOwner(ctx context.Context, in *QueryGetBondsByOw
}
func (c *queryClient) GetBondModuleBalance(ctx context.Context, in *QueryGetBondModuleBalanceRequest, opts ...grpc.CallOption) (*QueryGetBondModuleBalanceResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetBondModuleBalanceResponse)
err := c.cc.Invoke(ctx, Query_GetBondModuleBalance_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetBondModuleBalance_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -97,7 +104,9 @@ func (c *queryClient) GetBondModuleBalance(ctx context.Context, in *QueryGetBond
// QueryServer is the server API for Query service.
// All implementations must embed UnimplementedQueryServer
// for forward compatibility
// for forward compatibility.
//
// Query defines the gRPC querier service for bond module
type QueryServer interface {
// Params queries bonds module params.
Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
@ -112,9 +121,12 @@ type QueryServer interface {
mustEmbedUnimplementedQueryServer()
}
// UnimplementedQueryServer must be embedded to have forward compatible implementations.
type UnimplementedQueryServer struct {
}
// UnimplementedQueryServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedQueryServer struct{}
func (UnimplementedQueryServer) Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
@ -132,6 +144,7 @@ func (UnimplementedQueryServer) GetBondModuleBalance(context.Context, *QueryGetB
return nil, status.Errorf(codes.Unimplemented, "method GetBondModuleBalance not implemented")
}
func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}
func (UnimplementedQueryServer) testEmbeddedByValue() {}
// UnsafeQueryServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to QueryServer will
@ -141,6 +154,13 @@ type UnsafeQueryServer interface {
}
func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) {
// If the following call pancis, it indicates UnimplementedQueryServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Query_ServiceDesc, srv)
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/bond/v1/tx.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Msg_CreateBond_FullMethodName = "/cerc.bond.v1.Msg/CreateBond"
@ -29,6 +29,8 @@ const (
// MsgClient is the client API for Msg service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Msg defines the bond Msg service.
type MsgClient interface {
// CreateBond defines a method for creating a new bond.
CreateBond(ctx context.Context, in *MsgCreateBond, opts ...grpc.CallOption) (*MsgCreateBondResponse, error)
@ -52,8 +54,9 @@ func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
}
func (c *msgClient) CreateBond(ctx context.Context, in *MsgCreateBond, opts ...grpc.CallOption) (*MsgCreateBondResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgCreateBondResponse)
err := c.cc.Invoke(ctx, Msg_CreateBond_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_CreateBond_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -61,8 +64,9 @@ func (c *msgClient) CreateBond(ctx context.Context, in *MsgCreateBond, opts ...g
}
func (c *msgClient) RefillBond(ctx context.Context, in *MsgRefillBond, opts ...grpc.CallOption) (*MsgRefillBondResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgRefillBondResponse)
err := c.cc.Invoke(ctx, Msg_RefillBond_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_RefillBond_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -70,8 +74,9 @@ func (c *msgClient) RefillBond(ctx context.Context, in *MsgRefillBond, opts ...g
}
func (c *msgClient) WithdrawBond(ctx context.Context, in *MsgWithdrawBond, opts ...grpc.CallOption) (*MsgWithdrawBondResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgWithdrawBondResponse)
err := c.cc.Invoke(ctx, Msg_WithdrawBond_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_WithdrawBond_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -79,8 +84,9 @@ func (c *msgClient) WithdrawBond(ctx context.Context, in *MsgWithdrawBond, opts
}
func (c *msgClient) CancelBond(ctx context.Context, in *MsgCancelBond, opts ...grpc.CallOption) (*MsgCancelBondResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgCancelBondResponse)
err := c.cc.Invoke(ctx, Msg_CancelBond_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_CancelBond_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -88,8 +94,9 @@ func (c *msgClient) CancelBond(ctx context.Context, in *MsgCancelBond, opts ...g
}
func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgUpdateParamsResponse)
err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -98,7 +105,9 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts
// MsgServer is the server API for Msg service.
// All implementations must embed UnimplementedMsgServer
// for forward compatibility
// for forward compatibility.
//
// Msg defines the bond Msg service.
type MsgServer interface {
// CreateBond defines a method for creating a new bond.
CreateBond(context.Context, *MsgCreateBond) (*MsgCreateBondResponse, error)
@ -114,9 +123,12 @@ type MsgServer interface {
mustEmbedUnimplementedMsgServer()
}
// UnimplementedMsgServer must be embedded to have forward compatible implementations.
type UnimplementedMsgServer struct {
}
// UnimplementedMsgServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMsgServer struct{}
func (UnimplementedMsgServer) CreateBond(context.Context, *MsgCreateBond) (*MsgCreateBondResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateBond not implemented")
@ -134,6 +146,7 @@ func (UnimplementedMsgServer) UpdateParams(context.Context, *MsgUpdateParams) (*
return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented")
}
func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
func (UnimplementedMsgServer) testEmbeddedByValue() {}
// UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MsgServer will
@ -143,6 +156,13 @@ type UnsafeMsgServer interface {
}
func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
// If the following call pancis, it indicates UnimplementedMsgServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Msg_ServiceDesc, srv)
}

View File

@ -426,7 +426,7 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods {
var (
md_Participant protoreflect.MessageDescriptor
fd_Participant_cosmos_address protoreflect.FieldDescriptor
fd_Participant_nitro_address protoreflect.FieldDescriptor
fd_Participant_public_key protoreflect.FieldDescriptor
fd_Participant_role protoreflect.FieldDescriptor
fd_Participant_kyc_id protoreflect.FieldDescriptor
)
@ -435,7 +435,7 @@ func init() {
file_cerc_onboarding_v1_onboarding_proto_init()
md_Participant = File_cerc_onboarding_v1_onboarding_proto.Messages().ByName("Participant")
fd_Participant_cosmos_address = md_Participant.Fields().ByName("cosmos_address")
fd_Participant_nitro_address = md_Participant.Fields().ByName("nitro_address")
fd_Participant_public_key = md_Participant.Fields().ByName("public_key")
fd_Participant_role = md_Participant.Fields().ByName("role")
fd_Participant_kyc_id = md_Participant.Fields().ByName("kyc_id")
}
@ -511,9 +511,9 @@ func (x *fastReflection_Participant) Range(f func(protoreflect.FieldDescriptor,
return
}
}
if x.NitroAddress != "" {
value := protoreflect.ValueOfString(x.NitroAddress)
if !f(fd_Participant_nitro_address, value) {
if len(x.PublicKey) != 0 {
value := protoreflect.ValueOfBytes(x.PublicKey)
if !f(fd_Participant_public_key, value) {
return
}
}
@ -546,8 +546,8 @@ func (x *fastReflection_Participant) Has(fd protoreflect.FieldDescriptor) bool {
switch fd.FullName() {
case "cerc.onboarding.v1.Participant.cosmos_address":
return x.CosmosAddress != ""
case "cerc.onboarding.v1.Participant.nitro_address":
return x.NitroAddress != ""
case "cerc.onboarding.v1.Participant.public_key":
return len(x.PublicKey) != 0
case "cerc.onboarding.v1.Participant.role":
return x.Role != ""
case "cerc.onboarding.v1.Participant.kyc_id":
@ -570,8 +570,8 @@ func (x *fastReflection_Participant) Clear(fd protoreflect.FieldDescriptor) {
switch fd.FullName() {
case "cerc.onboarding.v1.Participant.cosmos_address":
x.CosmosAddress = ""
case "cerc.onboarding.v1.Participant.nitro_address":
x.NitroAddress = ""
case "cerc.onboarding.v1.Participant.public_key":
x.PublicKey = nil
case "cerc.onboarding.v1.Participant.role":
x.Role = ""
case "cerc.onboarding.v1.Participant.kyc_id":
@ -595,9 +595,9 @@ func (x *fastReflection_Participant) Get(descriptor protoreflect.FieldDescriptor
case "cerc.onboarding.v1.Participant.cosmos_address":
value := x.CosmosAddress
return protoreflect.ValueOfString(value)
case "cerc.onboarding.v1.Participant.nitro_address":
value := x.NitroAddress
return protoreflect.ValueOfString(value)
case "cerc.onboarding.v1.Participant.public_key":
value := x.PublicKey
return protoreflect.ValueOfBytes(value)
case "cerc.onboarding.v1.Participant.role":
value := x.Role
return protoreflect.ValueOfString(value)
@ -626,8 +626,8 @@ func (x *fastReflection_Participant) Set(fd protoreflect.FieldDescriptor, value
switch fd.FullName() {
case "cerc.onboarding.v1.Participant.cosmos_address":
x.CosmosAddress = value.Interface().(string)
case "cerc.onboarding.v1.Participant.nitro_address":
x.NitroAddress = value.Interface().(string)
case "cerc.onboarding.v1.Participant.public_key":
x.PublicKey = value.Bytes()
case "cerc.onboarding.v1.Participant.role":
x.Role = value.Interface().(string)
case "cerc.onboarding.v1.Participant.kyc_id":
@ -654,8 +654,8 @@ func (x *fastReflection_Participant) Mutable(fd protoreflect.FieldDescriptor) pr
switch fd.FullName() {
case "cerc.onboarding.v1.Participant.cosmos_address":
panic(fmt.Errorf("field cosmos_address of message cerc.onboarding.v1.Participant is not mutable"))
case "cerc.onboarding.v1.Participant.nitro_address":
panic(fmt.Errorf("field nitro_address of message cerc.onboarding.v1.Participant is not mutable"))
case "cerc.onboarding.v1.Participant.public_key":
panic(fmt.Errorf("field public_key of message cerc.onboarding.v1.Participant is not mutable"))
case "cerc.onboarding.v1.Participant.role":
panic(fmt.Errorf("field role of message cerc.onboarding.v1.Participant is not mutable"))
case "cerc.onboarding.v1.Participant.kyc_id":
@ -675,8 +675,8 @@ func (x *fastReflection_Participant) NewField(fd protoreflect.FieldDescriptor) p
switch fd.FullName() {
case "cerc.onboarding.v1.Participant.cosmos_address":
return protoreflect.ValueOfString("")
case "cerc.onboarding.v1.Participant.nitro_address":
return protoreflect.ValueOfString("")
case "cerc.onboarding.v1.Participant.public_key":
return protoreflect.ValueOfBytes(nil)
case "cerc.onboarding.v1.Participant.role":
return protoreflect.ValueOfString("")
case "cerc.onboarding.v1.Participant.kyc_id":
@ -754,7 +754,7 @@ func (x *fastReflection_Participant) ProtoMethods() *protoiface.Methods {
if l > 0 {
n += 1 + l + runtime.Sov(uint64(l))
}
l = len(x.NitroAddress)
l = len(x.PublicKey)
if l > 0 {
n += 1 + l + runtime.Sov(uint64(l))
}
@ -809,10 +809,10 @@ func (x *fastReflection_Participant) ProtoMethods() *protoiface.Methods {
i--
dAtA[i] = 0x1a
}
if len(x.NitroAddress) > 0 {
i -= len(x.NitroAddress)
copy(dAtA[i:], x.NitroAddress)
i = runtime.EncodeVarint(dAtA, i, uint64(len(x.NitroAddress)))
if len(x.PublicKey) > 0 {
i -= len(x.PublicKey)
copy(dAtA[i:], x.PublicKey)
i = runtime.EncodeVarint(dAtA, i, uint64(len(x.PublicKey)))
i--
dAtA[i] = 0x12
}
@ -906,9 +906,9 @@ func (x *fastReflection_Participant) ProtoMethods() *protoiface.Methods {
iNdEx = postIndex
case 2:
if wireType != 2 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field NitroAddress", wireType)
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType)
}
var stringLen uint64
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow
@ -918,23 +918,25 @@ func (x *fastReflection_Participant) ProtoMethods() *protoiface.Methods {
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
if byteLen < 0 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
postIndex := iNdEx + byteLen
if postIndex < 0 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength
}
if postIndex > l {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF
}
x.NitroAddress = string(dAtA[iNdEx:postIndex])
x.PublicKey = append(x.PublicKey[:0], dAtA[iNdEx:postIndex]...)
if x.PublicKey == nil {
x.PublicKey = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
@ -1577,8 +1579,8 @@ type Participant struct {
// participant's cosmos (laconic) address
CosmosAddress string `protobuf:"bytes,1,opt,name=cosmos_address,json=cosmosAddress,proto3" json:"cosmos_address,omitempty"`
// participant's Nitro address
NitroAddress string `protobuf:"bytes,2,opt,name=nitro_address,json=nitroAddress,proto3" json:"nitro_address,omitempty"`
// full public key used to derive participant's Nitro address
PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
// participant's role (participant | validator)
Role string `protobuf:"bytes,3,opt,name=role,proto3" json:"role,omitempty"`
// participant's KYC receipt ID
@ -1612,11 +1614,11 @@ func (x *Participant) GetCosmosAddress() string {
return ""
}
func (x *Participant) GetNitroAddress() string {
func (x *Participant) GetPublicKey() []byte {
if x != nil {
return x.NitroAddress
return x.PublicKey
}
return ""
return nil
}
func (x *Participant) GetRole() string {
@ -1692,46 +1694,43 @@ var file_cerc_onboarding_v1_onboarding_proto_rawDesc = []byte{
0x6c, 0x65, 0x64, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x6f, 0x6e, 0x62, 0x6f, 0x61,
0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x52, 0x11,
0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
0x64, 0x22, 0xa2, 0x02, 0x0a, 0x0b, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e,
0x64, 0x22, 0xed, 0x01, 0x0a, 0x0b, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e,
0x74, 0x12, 0x56, 0x0a, 0x0e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2f, 0xf2, 0xde, 0x1f, 0x2b, 0x6a,
0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0d, 0x63, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x52, 0x0a, 0x0d, 0x6e, 0x69, 0x74,
0x72, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x42, 0x2d, 0xf2, 0xde, 0x1f, 0x29, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6e, 0x69, 0x74, 0x72,
0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a,
0x22, 0x6e, 0x69, 0x74, 0x72, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52,
0x0c, 0x6e, 0x69, 0x74, 0x72, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2f, 0x0a,
0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1b, 0xf2, 0xde, 0x1f,
0x17, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x20, 0x79, 0x61, 0x6d,
0x6c, 0x3a, 0x22, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x36,
0x0a, 0x06, 0x6b, 0x79, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1f,
0xf2, 0xde, 0x1f, 0x1b, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6b, 0x79, 0x63, 0x5f, 0x69, 0x64,
0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x6b, 0x79, 0x63, 0x5f, 0x69, 0x64, 0x22, 0x52,
0x05, 0x6b, 0x79, 0x63, 0x49, 0x64, 0x22, 0x76, 0x0a, 0x0a, 0x45, 0x74, 0x68, 0x50, 0x61, 0x79,
0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3b, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21, 0xf2, 0xde, 0x1f, 0x1d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a,
0x22, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x12, 0x2b, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x19,
0xf2, 0xde, 0x1f, 0x15, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6d, 0x73, 0x67, 0x22, 0x20, 0x79,
0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x6d, 0x73, 0x67, 0x22, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x42, 0xd4,
0x01, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x6f, 0x6e, 0x62, 0x6f,
0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x42, 0x0f, 0x4f, 0x6e, 0x62, 0x6f, 0x61,
0x72, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69,
0x74, 0x2e, 0x76, 0x64, 0x62, 0x2e, 0x74, 0x6f, 0x2f, 0x63, 0x65, 0x72, 0x63, 0x2d, 0x69, 0x6f,
0x2f, 0x6c, 0x61, 0x63, 0x6f, 0x6e, 0x69, 0x63, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x65,
0x72, 0x63, 0x2f, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31,
0x3b, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x76, 0x31, 0xa2, 0x02, 0x03,
0x43, 0x4f, 0x58, 0xaa, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63, 0x2e, 0x4f, 0x6e, 0x62, 0x6f, 0x61,
0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63, 0x5c,
0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1e,
0x43, 0x65, 0x72, 0x63, 0x5c, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5c,
0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02,
0x14, 0x43, 0x65, 0x72, 0x63, 0x3a, 0x3a, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e,
0x67, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1b, 0xf2, 0xde, 0x1f, 0x17, 0x6a, 0x73, 0x6f, 0x6e,
0x3a, 0x22, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x72, 0x6f,
0x6c, 0x65, 0x22, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x36, 0x0a, 0x06, 0x6b, 0x79, 0x63,
0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1f, 0xf2, 0xde, 0x1f, 0x1b, 0x6a,
0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6b, 0x79, 0x63, 0x5f, 0x69, 0x64, 0x22, 0x20, 0x79, 0x61, 0x6d,
0x6c, 0x3a, 0x22, 0x6b, 0x79, 0x63, 0x5f, 0x69, 0x64, 0x22, 0x52, 0x05, 0x6b, 0x79, 0x63, 0x49,
0x64, 0x22, 0x76, 0x0a, 0x0a, 0x45, 0x74, 0x68, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12,
0x3b, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x42, 0x21, 0xf2, 0xde, 0x1f, 0x1d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x61, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x22, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, 0x03,
0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x19, 0xf2, 0xde, 0x1f, 0x15, 0x6a,
0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6d, 0x73, 0x67, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22,
0x6d, 0x73, 0x67, 0x22, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x42, 0xd4, 0x01, 0x0a, 0x16, 0x63, 0x6f,
0x6d, 0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e,
0x67, 0x2e, 0x76, 0x31, 0x42, 0x0f, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x2e, 0x76, 0x64, 0x62,
0x2e, 0x74, 0x6f, 0x2f, 0x63, 0x65, 0x72, 0x63, 0x2d, 0x69, 0x6f, 0x2f, 0x6c, 0x61, 0x63, 0x6f,
0x6e, 0x69, 0x63, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x65, 0x72, 0x63, 0x2f, 0x6f, 0x6e,
0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x6f, 0x6e, 0x62, 0x6f,
0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x4f, 0x58, 0xaa, 0x02,
0x12, 0x43, 0x65, 0x72, 0x63, 0x2e, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67,
0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63, 0x5c, 0x4f, 0x6e, 0x62, 0x6f, 0x61,
0x72, 0x64, 0x69, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1e, 0x43, 0x65, 0x72, 0x63, 0x5c,
0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50,
0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x14, 0x43, 0x65, 0x72, 0x63,
0x3a, 0x3a, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x3a, 0x56, 0x31,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -3037,7 +3037,7 @@ var file_cerc_onboarding_v1_query_proto_rawDesc = []byte{
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x6f, 0x6e, 0x62,
0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69,
0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
0x61, 0x6e, 0x74, 0x32, 0xba, 0x04, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x95, 0x01,
0x61, 0x6e, 0x74, 0x32, 0xc3, 0x04, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x95, 0x01,
0x0a, 0x0c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2c,
0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67,
0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69,
@ -3059,7 +3059,7 @@ var file_cerc_onboarding_v1_query_proto_rawDesc = []byte{
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x63,
0x65, 0x72, 0x63, 0x2f, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x76,
0x31, 0x2f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x2f, 0x7b,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0xd5, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0xde, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74,
0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x42, 0x79, 0x4e, 0x69, 0x74,
0x72, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3c, 0x2e, 0x63, 0x65, 0x72, 0x63,
0x2e, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x51,
@ -3069,24 +3069,25 @@ var file_cerc_onboarding_v1_query_proto_rawDesc = []byte{
0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65,
0x72, 0x79, 0x47, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74,
0x42, 0x79, 0x4e, 0x69, 0x74, 0x72, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x12, 0x30,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3b, 0x12, 0x39,
0x2f, 0x63, 0x65, 0x72, 0x63, 0x2f, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67,
0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73,
0x2f, 0x7b, 0x6e, 0x69, 0x74, 0x72, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d,
0x42, 0xcf, 0x01, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x6f, 0x6e,
0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65,
0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x2e, 0x76,
0x64, 0x62, 0x2e, 0x74, 0x6f, 0x2f, 0x63, 0x65, 0x72, 0x63, 0x2d, 0x69, 0x6f, 0x2f, 0x6c, 0x61,
0x63, 0x6f, 0x6e, 0x69, 0x63, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x65, 0x72, 0x63, 0x2f,
0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x6f, 0x6e,
0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x4f, 0x58,
0xaa, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63, 0x2e, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69,
0x6e, 0x67, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63, 0x5c, 0x4f, 0x6e, 0x62,
0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1e, 0x43, 0x65, 0x72,
0x63, 0x5c, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0x5c,
0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x14, 0x43, 0x65,
0x72, 0x63, 0x3a, 0x3a, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x3a,
0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x2d, 0x62, 0x79, 0x2d, 0x6e, 0x69, 0x74, 0x72, 0x6f, 0x2f, 0x7b, 0x6e, 0x69, 0x74, 0x72, 0x6f,
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x42, 0xcf, 0x01, 0x0a, 0x16, 0x63, 0x6f,
0x6d, 0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e,
0x67, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f,
0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x2e, 0x76, 0x64, 0x62, 0x2e, 0x74, 0x6f, 0x2f, 0x63,
0x65, 0x72, 0x63, 0x2d, 0x69, 0x6f, 0x2f, 0x6c, 0x61, 0x63, 0x6f, 0x6e, 0x69, 0x63, 0x64, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x63, 0x65, 0x72, 0x63, 0x2f, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64,
0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e,
0x67, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x4f, 0x58, 0xaa, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63,
0x2e, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x56, 0x31, 0xca, 0x02,
0x12, 0x43, 0x65, 0x72, 0x63, 0x5c, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67,
0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1e, 0x43, 0x65, 0x72, 0x63, 0x5c, 0x4f, 0x6e, 0x62, 0x6f, 0x61,
0x72, 0x64, 0x69, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x14, 0x43, 0x65, 0x72, 0x63, 0x3a, 0x3a, 0x4f, 0x6e, 0x62,
0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/onboarding/v1/query.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Query_Participants_FullMethodName = "/cerc.onboarding.v1.Query/Participants"
@ -27,6 +27,8 @@ const (
// QueryClient is the client API for Query service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Query defines the gRPC querier service for onboarding module
type QueryClient interface {
// Participants queries Participants list
Participants(ctx context.Context, in *QueryParticipantsRequest, opts ...grpc.CallOption) (*QueryParticipantsResponse, error)
@ -45,8 +47,9 @@ func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
}
func (c *queryClient) Participants(ctx context.Context, in *QueryParticipantsRequest, opts ...grpc.CallOption) (*QueryParticipantsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryParticipantsResponse)
err := c.cc.Invoke(ctx, Query_Participants_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Participants_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -54,8 +57,9 @@ func (c *queryClient) Participants(ctx context.Context, in *QueryParticipantsReq
}
func (c *queryClient) GetParticipantByAddress(ctx context.Context, in *QueryGetParticipantByAddressRequest, opts ...grpc.CallOption) (*QueryGetParticipantByAddressResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetParticipantByAddressResponse)
err := c.cc.Invoke(ctx, Query_GetParticipantByAddress_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetParticipantByAddress_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -63,8 +67,9 @@ func (c *queryClient) GetParticipantByAddress(ctx context.Context, in *QueryGetP
}
func (c *queryClient) GetParticipantByNitroAddress(ctx context.Context, in *QueryGetParticipantByNitroAddressRequest, opts ...grpc.CallOption) (*QueryGetParticipantByNitroAddressResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetParticipantByNitroAddressResponse)
err := c.cc.Invoke(ctx, Query_GetParticipantByNitroAddress_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetParticipantByNitroAddress_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -73,7 +78,9 @@ func (c *queryClient) GetParticipantByNitroAddress(ctx context.Context, in *Quer
// QueryServer is the server API for Query service.
// All implementations must embed UnimplementedQueryServer
// for forward compatibility
// for forward compatibility.
//
// Query defines the gRPC querier service for onboarding module
type QueryServer interface {
// Participants queries Participants list
Participants(context.Context, *QueryParticipantsRequest) (*QueryParticipantsResponse, error)
@ -84,9 +91,12 @@ type QueryServer interface {
mustEmbedUnimplementedQueryServer()
}
// UnimplementedQueryServer must be embedded to have forward compatible implementations.
type UnimplementedQueryServer struct {
}
// UnimplementedQueryServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedQueryServer struct{}
func (UnimplementedQueryServer) Participants(context.Context, *QueryParticipantsRequest) (*QueryParticipantsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Participants not implemented")
@ -98,6 +108,7 @@ func (UnimplementedQueryServer) GetParticipantByNitroAddress(context.Context, *Q
return nil, status.Errorf(codes.Unimplemented, "method GetParticipantByNitroAddress not implemented")
}
func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}
func (UnimplementedQueryServer) testEmbeddedByValue() {}
// UnsafeQueryServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to QueryServer will
@ -107,6 +118,13 @@ type UnsafeQueryServer interface {
}
func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) {
// If the following call pancis, it indicates UnimplementedQueryServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Query_ServiceDesc, srv)
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/onboarding/v1/tx.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Msg_OnboardParticipant_FullMethodName = "/cerc.onboarding.v1.Msg/OnboardParticipant"
@ -25,6 +25,8 @@ const (
// MsgClient is the client API for Msg service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Msg defines the onboarding Msg service.
type MsgClient interface {
// OnboardParticipant defines a method for enrolling a new validator.
OnboardParticipant(ctx context.Context, in *MsgOnboardParticipant, opts ...grpc.CallOption) (*MsgOnboardParticipantResponse, error)
@ -39,8 +41,9 @@ func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
}
func (c *msgClient) OnboardParticipant(ctx context.Context, in *MsgOnboardParticipant, opts ...grpc.CallOption) (*MsgOnboardParticipantResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgOnboardParticipantResponse)
err := c.cc.Invoke(ctx, Msg_OnboardParticipant_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_OnboardParticipant_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -49,21 +52,27 @@ func (c *msgClient) OnboardParticipant(ctx context.Context, in *MsgOnboardPartic
// MsgServer is the server API for Msg service.
// All implementations must embed UnimplementedMsgServer
// for forward compatibility
// for forward compatibility.
//
// Msg defines the onboarding Msg service.
type MsgServer interface {
// OnboardParticipant defines a method for enrolling a new validator.
OnboardParticipant(context.Context, *MsgOnboardParticipant) (*MsgOnboardParticipantResponse, error)
mustEmbedUnimplementedMsgServer()
}
// UnimplementedMsgServer must be embedded to have forward compatible implementations.
type UnimplementedMsgServer struct {
}
// UnimplementedMsgServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMsgServer struct{}
func (UnimplementedMsgServer) OnboardParticipant(context.Context, *MsgOnboardParticipant) (*MsgOnboardParticipantResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method OnboardParticipant not implemented")
}
func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
func (UnimplementedMsgServer) testEmbeddedByValue() {}
// UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MsgServer will
@ -73,6 +82,13 @@ type UnsafeMsgServer interface {
}
func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
// If the following call pancis, it indicates UnimplementedMsgServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Msg_ServiceDesc, srv)
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/registry/v1/query.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Query_Params_FullMethodName = "/cerc.registry.v1.Query/Params"
@ -34,6 +34,8 @@ const (
// QueryClient is the client API for Query service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Query defines the gRPC querier service for registry module
type QueryClient interface {
// Params queries the registry module params.
Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
@ -66,8 +68,9 @@ func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
}
func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryParamsResponse)
err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -75,8 +78,9 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts .
}
func (c *queryClient) Records(ctx context.Context, in *QueryRecordsRequest, opts ...grpc.CallOption) (*QueryRecordsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryRecordsResponse)
err := c.cc.Invoke(ctx, Query_Records_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Records_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -84,8 +88,9 @@ func (c *queryClient) Records(ctx context.Context, in *QueryRecordsRequest, opts
}
func (c *queryClient) GetRecord(ctx context.Context, in *QueryGetRecordRequest, opts ...grpc.CallOption) (*QueryGetRecordResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetRecordResponse)
err := c.cc.Invoke(ctx, Query_GetRecord_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetRecord_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -93,8 +98,9 @@ func (c *queryClient) GetRecord(ctx context.Context, in *QueryGetRecordRequest,
}
func (c *queryClient) GetRecordsByBondId(ctx context.Context, in *QueryGetRecordsByBondIdRequest, opts ...grpc.CallOption) (*QueryGetRecordsByBondIdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetRecordsByBondIdResponse)
err := c.cc.Invoke(ctx, Query_GetRecordsByBondId_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetRecordsByBondId_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -102,8 +108,9 @@ func (c *queryClient) GetRecordsByBondId(ctx context.Context, in *QueryGetRecord
}
func (c *queryClient) NameRecords(ctx context.Context, in *QueryNameRecordsRequest, opts ...grpc.CallOption) (*QueryNameRecordsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryNameRecordsResponse)
err := c.cc.Invoke(ctx, Query_NameRecords_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_NameRecords_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -111,8 +118,9 @@ func (c *queryClient) NameRecords(ctx context.Context, in *QueryNameRecordsReque
}
func (c *queryClient) Whois(ctx context.Context, in *QueryWhoisRequest, opts ...grpc.CallOption) (*QueryWhoisResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryWhoisResponse)
err := c.cc.Invoke(ctx, Query_Whois_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Whois_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -120,8 +128,9 @@ func (c *queryClient) Whois(ctx context.Context, in *QueryWhoisRequest, opts ...
}
func (c *queryClient) LookupLrn(ctx context.Context, in *QueryLookupLrnRequest, opts ...grpc.CallOption) (*QueryLookupLrnResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryLookupLrnResponse)
err := c.cc.Invoke(ctx, Query_LookupLrn_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_LookupLrn_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -129,8 +138,9 @@ func (c *queryClient) LookupLrn(ctx context.Context, in *QueryLookupLrnRequest,
}
func (c *queryClient) ResolveLrn(ctx context.Context, in *QueryResolveLrnRequest, opts ...grpc.CallOption) (*QueryResolveLrnResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryResolveLrnResponse)
err := c.cc.Invoke(ctx, Query_ResolveLrn_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_ResolveLrn_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -138,8 +148,9 @@ func (c *queryClient) ResolveLrn(ctx context.Context, in *QueryResolveLrnRequest
}
func (c *queryClient) GetRegistryModuleBalance(ctx context.Context, in *QueryGetRegistryModuleBalanceRequest, opts ...grpc.CallOption) (*QueryGetRegistryModuleBalanceResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryGetRegistryModuleBalanceResponse)
err := c.cc.Invoke(ctx, Query_GetRegistryModuleBalance_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_GetRegistryModuleBalance_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -147,8 +158,9 @@ func (c *queryClient) GetRegistryModuleBalance(ctx context.Context, in *QueryGet
}
func (c *queryClient) Authorities(ctx context.Context, in *QueryAuthoritiesRequest, opts ...grpc.CallOption) (*QueryAuthoritiesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(QueryAuthoritiesResponse)
err := c.cc.Invoke(ctx, Query_Authorities_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Query_Authorities_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -157,7 +169,9 @@ func (c *queryClient) Authorities(ctx context.Context, in *QueryAuthoritiesReque
// QueryServer is the server API for Query service.
// All implementations must embed UnimplementedQueryServer
// for forward compatibility
// for forward compatibility.
//
// Query defines the gRPC querier service for registry module
type QueryServer interface {
// Params queries the registry module params.
Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
@ -182,9 +196,12 @@ type QueryServer interface {
mustEmbedUnimplementedQueryServer()
}
// UnimplementedQueryServer must be embedded to have forward compatible implementations.
type UnimplementedQueryServer struct {
}
// UnimplementedQueryServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedQueryServer struct{}
func (UnimplementedQueryServer) Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
@ -217,6 +234,7 @@ func (UnimplementedQueryServer) Authorities(context.Context, *QueryAuthoritiesRe
return nil, status.Errorf(codes.Unimplemented, "method Authorities not implemented")
}
func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}
func (UnimplementedQueryServer) testEmbeddedByValue() {}
// UnsafeQueryServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to QueryServer will
@ -226,6 +244,13 @@ type UnsafeQueryServer interface {
}
func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) {
// If the following call pancis, it indicates UnimplementedQueryServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Query_ServiceDesc, srv)
}

View File

@ -1318,7 +1318,7 @@ var (
fd_Record_owners protoreflect.FieldDescriptor
fd_Record_attributes protoreflect.FieldDescriptor
fd_Record_names protoreflect.FieldDescriptor
fd_Record_type protoreflect.FieldDescriptor
fd_Record_types protoreflect.FieldDescriptor
)
func init() {
@ -1332,7 +1332,7 @@ func init() {
fd_Record_owners = md_Record.Fields().ByName("owners")
fd_Record_attributes = md_Record.Fields().ByName("attributes")
fd_Record_names = md_Record.Fields().ByName("names")
fd_Record_type = md_Record.Fields().ByName("type")
fd_Record_types = md_Record.Fields().ByName("types")
}
var _ protoreflect.Message = (*fastReflection_Record)(nil)
@ -1448,9 +1448,9 @@ func (x *fastReflection_Record) Range(f func(protoreflect.FieldDescriptor, proto
return
}
}
if x.Type_ != "" {
value := protoreflect.ValueOfString(x.Type_)
if !f(fd_Record_type, value) {
if x.Types != "" {
value := protoreflect.ValueOfString(x.Types)
if !f(fd_Record_types, value) {
return
}
}
@ -1485,8 +1485,8 @@ func (x *fastReflection_Record) Has(fd protoreflect.FieldDescriptor) bool {
return len(x.Attributes) != 0
case "cerc.registry.v1.Record.names":
return len(x.Names) != 0
case "cerc.registry.v1.Record.type":
return x.Type_ != ""
case "cerc.registry.v1.Record.types":
return x.Types != ""
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cerc.registry.v1.Record"))
@ -1519,8 +1519,8 @@ func (x *fastReflection_Record) Clear(fd protoreflect.FieldDescriptor) {
x.Attributes = nil
case "cerc.registry.v1.Record.names":
x.Names = nil
case "cerc.registry.v1.Record.type":
x.Type_ = ""
case "cerc.registry.v1.Record.types":
x.Types = ""
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cerc.registry.v1.Record"))
@ -1567,8 +1567,8 @@ func (x *fastReflection_Record) Get(descriptor protoreflect.FieldDescriptor) pro
}
listValue := &_Record_8_list{list: &x.Names}
return protoreflect.ValueOfList(listValue)
case "cerc.registry.v1.Record.type":
value := x.Type_
case "cerc.registry.v1.Record.types":
value := x.Types
return protoreflect.ValueOfString(value)
default:
if descriptor.IsExtension() {
@ -1610,8 +1610,8 @@ func (x *fastReflection_Record) Set(fd protoreflect.FieldDescriptor, value proto
lv := value.List()
clv := lv.(*_Record_8_list)
x.Names = *clv.list
case "cerc.registry.v1.Record.type":
x.Type_ = value.Interface().(string)
case "cerc.registry.v1.Record.types":
x.Types = value.Interface().(string)
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cerc.registry.v1.Record"))
@ -1656,8 +1656,8 @@ func (x *fastReflection_Record) Mutable(fd protoreflect.FieldDescriptor) protore
panic(fmt.Errorf("field deleted of message cerc.registry.v1.Record is not mutable"))
case "cerc.registry.v1.Record.attributes":
panic(fmt.Errorf("field attributes of message cerc.registry.v1.Record is not mutable"))
case "cerc.registry.v1.Record.type":
panic(fmt.Errorf("field type of message cerc.registry.v1.Record is not mutable"))
case "cerc.registry.v1.Record.types":
panic(fmt.Errorf("field types of message cerc.registry.v1.Record is not mutable"))
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cerc.registry.v1.Record"))
@ -1689,7 +1689,7 @@ func (x *fastReflection_Record) NewField(fd protoreflect.FieldDescriptor) protor
case "cerc.registry.v1.Record.names":
list := []string{}
return protoreflect.ValueOfList(&_Record_8_list{list: &list})
case "cerc.registry.v1.Record.type":
case "cerc.registry.v1.Record.types":
return protoreflect.ValueOfString("")
default:
if fd.IsExtension() {
@ -1795,7 +1795,7 @@ func (x *fastReflection_Record) ProtoMethods() *protoiface.Methods {
n += 1 + l + runtime.Sov(uint64(l))
}
}
l = len(x.Type_)
l = len(x.Types)
if l > 0 {
n += 1 + l + runtime.Sov(uint64(l))
}
@ -1828,10 +1828,10 @@ func (x *fastReflection_Record) ProtoMethods() *protoiface.Methods {
i -= len(x.unknownFields)
copy(dAtA[i:], x.unknownFields)
}
if len(x.Type_) > 0 {
i -= len(x.Type_)
copy(dAtA[i:], x.Type_)
i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Type_)))
if len(x.Types) > 0 {
i -= len(x.Types)
copy(dAtA[i:], x.Types)
i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Types)))
i--
dAtA[i] = 0x4a
}
@ -2195,7 +2195,7 @@ func (x *fastReflection_Record) ProtoMethods() *protoiface.Methods {
iNdEx = postIndex
case 9:
if wireType != 2 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Type_", wireType)
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Types", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
@ -2223,7 +2223,7 @@ func (x *fastReflection_Record) ProtoMethods() *protoiface.Methods {
if postIndex > l {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF
}
x.Type_ = string(dAtA[iNdEx:postIndex])
x.Types = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
@ -6753,7 +6753,7 @@ type Record struct {
Owners []string `protobuf:"bytes,6,rep,name=owners,proto3" json:"owners,omitempty"`
Attributes []byte `protobuf:"bytes,7,opt,name=attributes,proto3" json:"attributes,omitempty"`
Names []string `protobuf:"bytes,8,rep,name=names,proto3" json:"names,omitempty"`
Type_ string `protobuf:"bytes,9,opt,name=type,proto3" json:"type,omitempty"`
Types string `protobuf:"bytes,9,opt,name=types,proto3" json:"types,omitempty"`
}
func (x *Record) Reset() {
@ -6832,9 +6832,9 @@ func (x *Record) GetNames() []string {
return nil
}
func (x *Record) GetType_() string {
func (x *Record) GetTypes() string {
if x != nil {
return x.Type_
return x.Types
}
return ""
}
@ -7352,7 +7352,7 @@ var file_cerc_registry_v1_registry_proto_rawDesc = []byte{
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x61, 0x75, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x62, 0x69, 0x64, 0x22, 0x52, 0x1a,
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x41, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x42, 0x69, 0x64, 0x22, 0x89, 0x04, 0x0a, 0x06, 0x52,
0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x42, 0x69, 0x64, 0x22, 0x8b, 0x04, 0x0a, 0x06, 0x52,
0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x27, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x42, 0x17, 0xf2, 0xde, 0x1f, 0x13, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x69, 0x64, 0x22,
0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x69, 0x64, 0x22, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3a,
@ -7382,90 +7382,90 @@ var file_cerc_registry_v1_registry_proto_rawDesc = []byte{
0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x42, 0x1d, 0xf2,
0xde, 0x1f, 0x19, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x20,
0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x52, 0x05, 0x6e, 0x61,
0x6d, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28,
0x09, 0x42, 0x1d, 0xf2, 0xde, 0x1f, 0x19, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x74, 0x79, 0x70,
0x65, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22,
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x5b, 0x0a, 0x0e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x05,
0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x65,
0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4e,
0x61, 0x6d, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x05, 0x65, 0x6e,
0x74, 0x72, 0x79, 0x22, 0xe6, 0x03, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x5d, 0x0a, 0x10, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42,
0x33, 0xf2, 0xde, 0x1f, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x20, 0x79, 0x61, 0x6d,
0x6c, 0x3a, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
0x6b, 0x65, 0x79, 0x22, 0x52, 0x0e, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x4b, 0x65, 0x79, 0x12, 0x52, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2d, 0xf2, 0xde, 0x1f,
0x29, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x6f, 0x77, 0x6e, 0x65,
0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0c, 0x6f, 0x77, 0x6e, 0x65,
0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67,
0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x0a, 0x61, 0x75, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xf2, 0xde,
0x1f, 0x23, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x61, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x61, 0x75, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x69, 0x64, 0x22, 0x52, 0x09, 0x61, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64,
0x12, 0x3a, 0x0a, 0x07, 0x62, 0x6f, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x42, 0x21, 0xf2, 0xde, 0x1f, 0x1d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x62, 0x6f, 0x6e,
0x64, 0x5f, 0x69, 0x64, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x62, 0x6f, 0x6e, 0x64,
0x5f, 0x69, 0x64, 0x22, 0x52, 0x06, 0x62, 0x6f, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x6e, 0x0a, 0x0b,
0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x31, 0xc8,
0xde, 0x1f, 0x00, 0xf2, 0xde, 0x1f, 0x25, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x65, 0x78, 0x70,
0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22,
0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x90, 0xdf, 0x1f, 0x01,
0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x53, 0x0a, 0x09,
0x4e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x32, 0x0a,
0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63,
0x65, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e,
0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72,
0x79, 0x22, 0x84, 0x01, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
0x12, 0x39, 0x0a, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x21, 0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x07, 0x68,
0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63,
0x65, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e,
0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x39, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65,
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69,
0x67, 0x68, 0x74, 0x22, 0x74, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
0x12, 0x2b, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x19, 0xf2,
0xde, 0x1f, 0x15, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x69, 0x67, 0x22, 0x20, 0x79, 0x61,
0x6d, 0x6c, 0x3a, 0x22, 0x73, 0x69, 0x67, 0x22, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x3a, 0x0a,
0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21,
0xf2, 0xde, 0x1f, 0x1d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65,
0x79, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79,
0x22, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x33, 0x0a, 0x0b, 0x45, 0x78, 0x70,
0x69, 0x72, 0x79, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23,
0x0a, 0x0b, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x42, 0xc4, 0x01, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x65, 0x72, 0x63,
0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x52, 0x65,
0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3b, 0x67,
0x69, 0x74, 0x2e, 0x76, 0x64, 0x62, 0x2e, 0x74, 0x6f, 0x2f, 0x63, 0x65, 0x72, 0x63, 0x2d, 0x69,
0x6f, 0x2f, 0x6c, 0x61, 0x63, 0x6f, 0x6e, 0x69, 0x63, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63,
0x65, 0x72, 0x63, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x76, 0x31, 0x3b,
0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x52, 0x58,
0xaa, 0x02, 0x10, 0x43, 0x65, 0x72, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
0x2e, 0x56, 0x31, 0xca, 0x02, 0x10, 0x43, 0x65, 0x72, 0x63, 0x5c, 0x52, 0x65, 0x67, 0x69, 0x73,
0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1c, 0x43, 0x65, 0x72, 0x63, 0x5c, 0x52, 0x65,
0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74,
0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63, 0x3a, 0x3a, 0x52, 0x65,
0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01,
0x28, 0x09, 0x42, 0x1d, 0xf2, 0xde, 0x1f, 0x19, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x74, 0x79,
0x70, 0x65, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x74, 0x79, 0x70, 0x65, 0x73,
0x22, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x5b, 0x0a, 0x0e, 0x41, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35,
0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
0x63, 0x65, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31,
0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x05,
0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xe6, 0x03, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x75,
0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x5d, 0x0a, 0x10, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x42, 0x33, 0xf2, 0xde, 0x1f, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6f, 0x77, 0x6e,
0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x20, 0x79,
0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x52, 0x0e, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x50, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x52, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2d, 0xf2,
0xde, 0x1f, 0x29, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x6f, 0x77,
0x6e, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0c, 0x6f, 0x77,
0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65,
0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67,
0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x0a, 0x61, 0x75,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27,
0xf2, 0xde, 0x1f, 0x23, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x61, 0x75, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x69, 0x64, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x61, 0x75, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0x52, 0x09, 0x61, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x49, 0x64, 0x12, 0x3a, 0x0a, 0x07, 0x62, 0x6f, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20,
0x01, 0x28, 0x09, 0x42, 0x21, 0xf2, 0xde, 0x1f, 0x1d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x62,
0x6f, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x62, 0x6f,
0x6e, 0x64, 0x5f, 0x69, 0x64, 0x22, 0x52, 0x06, 0x62, 0x6f, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x6e,
0x0a, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42,
0x31, 0xc8, 0xde, 0x1f, 0x00, 0xf2, 0xde, 0x1f, 0x25, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x65,
0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c,
0x3a, 0x22, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x90, 0xdf,
0x1f, 0x01, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x53,
0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x32, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76,
0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x05, 0x65, 0x6e,
0x74, 0x72, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f,
0x72, 0x64, 0x12, 0x39, 0x0a, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a,
0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21,
0x2e, 0x63, 0x65, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76,
0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x39, 0x0a, 0x0f, 0x4e, 0x61,
0x6d, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a,
0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x74, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
0x72, 0x65, 0x12, 0x2b, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42,
0x19, 0xf2, 0xde, 0x1f, 0x15, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x69, 0x67, 0x22, 0x20,
0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x73, 0x69, 0x67, 0x22, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12,
0x3a, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x42, 0x21, 0xf2, 0xde, 0x1f, 0x1d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x70, 0x75, 0x62, 0x5f,
0x6b, 0x65, 0x79, 0x22, 0x20, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x70, 0x75, 0x62, 0x5f, 0x6b,
0x65, 0x79, 0x22, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x33, 0x0a, 0x0b, 0x45,
0x78, 0x70, 0x69, 0x72, 0x79, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x22, 0x23, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12,
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0xc4, 0x01, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x65,
0x72, 0x63, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x42, 0x0d,
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a,
0x3b, 0x67, 0x69, 0x74, 0x2e, 0x76, 0x64, 0x62, 0x2e, 0x74, 0x6f, 0x2f, 0x63, 0x65, 0x72, 0x63,
0x2d, 0x69, 0x6f, 0x2f, 0x6c, 0x61, 0x63, 0x6f, 0x6e, 0x69, 0x63, 0x64, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x63, 0x65, 0x72, 0x63, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x76,
0x31, 0x3b, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43,
0x52, 0x58, 0xaa, 0x02, 0x10, 0x43, 0x65, 0x72, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
0x72, 0x79, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x10, 0x43, 0x65, 0x72, 0x63, 0x5c, 0x52, 0x65, 0x67,
0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1c, 0x43, 0x65, 0x72, 0x63, 0x5c,
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x12, 0x43, 0x65, 0x72, 0x63, 0x3a, 0x3a,
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: cerc/registry/v1/tx.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Msg_SetRecord_FullMethodName = "/cerc.registry.v1.Msg/SetRecord"
@ -35,6 +35,8 @@ const (
// MsgClient is the client API for Msg service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Msg is a service which exposes the registry functionality
type MsgClient interface {
// SetRecord records a new record with given payload and bond id
SetRecord(ctx context.Context, in *MsgSetRecord, opts ...grpc.CallOption) (*MsgSetRecordResponse, error)
@ -70,8 +72,9 @@ func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
}
func (c *msgClient) SetRecord(ctx context.Context, in *MsgSetRecord, opts ...grpc.CallOption) (*MsgSetRecordResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgSetRecordResponse)
err := c.cc.Invoke(ctx, Msg_SetRecord_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_SetRecord_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -79,8 +82,9 @@ func (c *msgClient) SetRecord(ctx context.Context, in *MsgSetRecord, opts ...grp
}
func (c *msgClient) RenewRecord(ctx context.Context, in *MsgRenewRecord, opts ...grpc.CallOption) (*MsgRenewRecordResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgRenewRecordResponse)
err := c.cc.Invoke(ctx, Msg_RenewRecord_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_RenewRecord_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -88,8 +92,9 @@ func (c *msgClient) RenewRecord(ctx context.Context, in *MsgRenewRecord, opts ..
}
func (c *msgClient) AssociateBond(ctx context.Context, in *MsgAssociateBond, opts ...grpc.CallOption) (*MsgAssociateBondResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgAssociateBondResponse)
err := c.cc.Invoke(ctx, Msg_AssociateBond_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_AssociateBond_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -97,8 +102,9 @@ func (c *msgClient) AssociateBond(ctx context.Context, in *MsgAssociateBond, opt
}
func (c *msgClient) DissociateBond(ctx context.Context, in *MsgDissociateBond, opts ...grpc.CallOption) (*MsgDissociateBondResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgDissociateBondResponse)
err := c.cc.Invoke(ctx, Msg_DissociateBond_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_DissociateBond_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -106,8 +112,9 @@ func (c *msgClient) DissociateBond(ctx context.Context, in *MsgDissociateBond, o
}
func (c *msgClient) DissociateRecords(ctx context.Context, in *MsgDissociateRecords, opts ...grpc.CallOption) (*MsgDissociateRecordsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgDissociateRecordsResponse)
err := c.cc.Invoke(ctx, Msg_DissociateRecords_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_DissociateRecords_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -115,8 +122,9 @@ func (c *msgClient) DissociateRecords(ctx context.Context, in *MsgDissociateReco
}
func (c *msgClient) ReassociateRecords(ctx context.Context, in *MsgReassociateRecords, opts ...grpc.CallOption) (*MsgReassociateRecordsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgReassociateRecordsResponse)
err := c.cc.Invoke(ctx, Msg_ReassociateRecords_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_ReassociateRecords_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -124,8 +132,9 @@ func (c *msgClient) ReassociateRecords(ctx context.Context, in *MsgReassociateRe
}
func (c *msgClient) SetName(ctx context.Context, in *MsgSetName, opts ...grpc.CallOption) (*MsgSetNameResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgSetNameResponse)
err := c.cc.Invoke(ctx, Msg_SetName_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_SetName_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -133,8 +142,9 @@ func (c *msgClient) SetName(ctx context.Context, in *MsgSetName, opts ...grpc.Ca
}
func (c *msgClient) DeleteName(ctx context.Context, in *MsgDeleteName, opts ...grpc.CallOption) (*MsgDeleteNameResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgDeleteNameResponse)
err := c.cc.Invoke(ctx, Msg_DeleteName_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_DeleteName_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -142,8 +152,9 @@ func (c *msgClient) DeleteName(ctx context.Context, in *MsgDeleteName, opts ...g
}
func (c *msgClient) ReserveAuthority(ctx context.Context, in *MsgReserveAuthority, opts ...grpc.CallOption) (*MsgReserveAuthorityResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgReserveAuthorityResponse)
err := c.cc.Invoke(ctx, Msg_ReserveAuthority_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_ReserveAuthority_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -151,8 +162,9 @@ func (c *msgClient) ReserveAuthority(ctx context.Context, in *MsgReserveAuthorit
}
func (c *msgClient) SetAuthorityBond(ctx context.Context, in *MsgSetAuthorityBond, opts ...grpc.CallOption) (*MsgSetAuthorityBondResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgSetAuthorityBondResponse)
err := c.cc.Invoke(ctx, Msg_SetAuthorityBond_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_SetAuthorityBond_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -160,8 +172,9 @@ func (c *msgClient) SetAuthorityBond(ctx context.Context, in *MsgSetAuthorityBon
}
func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MsgUpdateParamsResponse)
err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -170,7 +183,9 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts
// MsgServer is the server API for Msg service.
// All implementations must embed UnimplementedMsgServer
// for forward compatibility
// for forward compatibility.
//
// Msg is a service which exposes the registry functionality
type MsgServer interface {
// SetRecord records a new record with given payload and bond id
SetRecord(context.Context, *MsgSetRecord) (*MsgSetRecordResponse, error)
@ -198,9 +213,12 @@ type MsgServer interface {
mustEmbedUnimplementedMsgServer()
}
// UnimplementedMsgServer must be embedded to have forward compatible implementations.
type UnimplementedMsgServer struct {
}
// UnimplementedMsgServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMsgServer struct{}
func (UnimplementedMsgServer) SetRecord(context.Context, *MsgSetRecord) (*MsgSetRecordResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetRecord not implemented")
@ -236,6 +254,7 @@ func (UnimplementedMsgServer) UpdateParams(context.Context, *MsgUpdateParams) (*
return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented")
}
func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
func (UnimplementedMsgServer) testEmbeddedByValue() {}
// UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MsgServer will
@ -245,6 +264,13 @@ type UnsafeMsgServer interface {
}
func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
// If the following call pancis, it indicates UnimplementedMsgServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Msg_ServiceDesc, srv)
}

180
app/abci.go Normal file
View File

@ -0,0 +1,180 @@
package app
import (
"bytes"
"crypto/rand"
"encoding/gob"
"encoding/json"
"fmt"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"git.vdb.to/cerc-io/laconicd/server/distsig"
)
type (
// VoteExtensionHandler defines the vote extension handler for LaconicApp.
VoteExtensionHandler struct {
app *LaconicApp
}
// VoteExtension defines the structure used to create a vote extension.
VoteExtension struct {
Hash []byte
Height int64
Data []byte
}
)
func NewVoteExtensionHandler() *VoteExtensionHandler {
return &VoteExtensionHandler{}
}
func (app *LaconicApp) NewVoteExtensionHandler() *VoteExtensionHandler {
return &VoteExtensionHandler{app: app}
}
func (h *VoteExtensionHandler) SetHandlers(bApp *baseapp.BaseApp) {
if h.app != nil {
// Use the real laconic handlers when app is available
bApp.SetExtendVoteHandler(h.ExtendVoteWithLaconic())
bApp.SetVerifyVoteExtensionHandler(h.VerifyVoteExtensionWithLaconic())
bApp.SetPrepareProposal(h.PrepareProposal())
bApp.SetProcessProposal(h.ProcessProposal())
} else {
// Use dummy handlers for testing
bApp.SetExtendVoteHandler(h.ExtendVote())
bApp.SetVerifyVoteExtensionHandler(h.VerifyVoteExtension())
}
}
func (h *VoteExtensionHandler) ExtendVote() sdk.ExtendVoteHandler {
return func(_ sdk.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
buf := make([]byte, 1024)
_, err := rand.Read(buf)
if err != nil {
return nil, fmt.Errorf("failed to generate random vote extension data: %w", err)
}
ve := VoteExtension{
Hash: req.Hash,
Height: req.Height,
Data: buf,
}
bz, err := json.Marshal(ve)
if err != nil {
return nil, fmt.Errorf("failed to encode vote extension: %w", err)
}
return &abci.ResponseExtendVote{VoteExtension: bz}, nil
}
}
func (h *VoteExtensionHandler) VerifyVoteExtension() sdk.VerifyVoteExtensionHandler {
return func(ctx sdk.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) {
var ve VoteExtension
if err := json.Unmarshal(req.VoteExtension, &ve); err != nil {
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, nil
}
switch {
case req.Height != ve.Height:
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, nil
case !bytes.Equal(req.Hash, ve.Hash):
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, nil
case len(ve.Data) != 1024:
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, nil
}
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil
}
}
// PrepareProposal is responsible for:
// - collecting Nitro state updates and applying them as inserted transactions in the block proposal
func (h *VoteExtensionHandler) PrepareProposal() sdk.PrepareProposalHandler {
return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
// paramsResp, err := h.app.ConsensusParamsKeeper.Params(ctx, &consensustypes.QueryParamsRequest{})
// if err != nil {
// return nil, err
// }
// var maxBlockGas uint64
// if b := paramsResp.GetParams().Block; b != nil {
// maxBlockGas = uint64(b.MaxGas)
// }
// Decode transactions from bytes
txs := make([]sdk.Tx, 0, len(req.Txs))
for _, txBz := range req.Txs {
tx, err := h.app.txConfig.TxDecoder()(txBz)
if err != nil {
continue // Skip invalid transactions
}
txs = append(txs, tx)
}
// For now, just return the transactions as-is
// TODO: Add custom transaction selection logic here
return &abci.ResponsePrepareProposal{Txs: req.Txs}, nil
}
}
func (h *VoteExtensionHandler) ProcessProposal() sdk.ProcessProposalHandler {
return func(ctx sdk.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
// For now, accept all proposals
// TODO: Add custom proposal validation logic here
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}
}
// ExtendVoteWithLaconic implements the real Laconic-specific vote extension logic
// - renews distsig key (DKG) if validator set has changed
// - broadcasts prepared DKG and distsig messages
func (h *VoteExtensionHandler) ExtendVoteWithLaconic() sdk.ExtendVoteHandler {
return func(ctx sdk.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
// Check if we have a distributed signature manager
// Since we can't access DistSigManager() directly in v1, we'll implement a simpler version
// TODO: Integrate with actual distributed signature manager when available
// Check if we are a participant
if h.app.OnboardingKeeper != nil {
// For now, return empty vote extension
// TODO: Implement the full distsig logic here
return &abci.ResponseExtendVote{VoteExtension: []byte{}}, nil
}
return &abci.ResponseExtendVote{VoteExtension: []byte{}}, nil
}
}
// VerifyVoteExtensionWithLaconic implements the real Laconic-specific vote extension verification
func (h *VoteExtensionHandler) VerifyVoteExtensionWithLaconic() sdk.VerifyVoteExtensionHandler {
return func(ctx sdk.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) {
if len(req.VoteExtension) == 0 {
// Empty vote extension is acceptable
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil
}
// Decode and verify the vote extension
dec := gob.NewDecoder(bytes.NewReader(req.VoteExtension))
var messages distsig.PeerMessages
if err := dec.Decode(&messages); err != nil {
// If we can't decode, reject
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, nil
}
// TODO: Actually verify the messages with the distributed signature manager
// For now, just accept
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil
}
}

View File

@ -3,22 +3,15 @@ package app
import (
_ "embed"
"io"
"os"
"path/filepath"
dbm "github.com/cosmos/cosmos-db"
"cosmossdk.io/core/appconfig"
clienthelpers "cosmossdk.io/client/v2/helpers"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
evidencekeeper "cosmossdk.io/x/evidence/keeper"
auctionkeeper "git.vdb.to/cerc-io/laconicd/x/auction/keeper"
bondkeeper "git.vdb.to/cerc-io/laconicd/x/bond/keeper"
onboardingkeeper "git.vdb.to/cerc-io/laconicd/x/onboarding/keeper"
registrykeeper "git.vdb.to/cerc-io/laconicd/x/registry/keeper"
types "git.vdb.to/cerc-io/laconicd/x/types/v1"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
@ -35,41 +28,40 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper"
crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
"github.com/cosmos/cosmos-sdk/x/gov"
govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
protocolpoolkeeper "github.com/cosmos/cosmos-sdk/x/protocolpool/keeper"
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
_ "cosmossdk.io/api/cosmos/tx/config/v1" // import for side-effects
_ "cosmossdk.io/x/evidence" // import for side-effects
auctionkeeper "git.vdb.to/cerc-io/laconicd/x/auction/keeper"
bondkeeper "git.vdb.to/cerc-io/laconicd/x/bond/keeper"
nitrokeeper "git.vdb.to/cerc-io/laconicd/x/nitro/keeper"
onboardingkeeper "git.vdb.to/cerc-io/laconicd/x/onboarding/keeper"
registrykeeper "git.vdb.to/cerc-io/laconicd/x/registry/keeper"
types "git.vdb.to/cerc-io/laconicd/x/types/v1"
_ "cosmossdk.io/api/cosmos/tx/config/v1" // import for side-effects
_ "cosmossdk.io/x/evidence" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/auth" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/bank" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/consensus" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/distribution" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/mint" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/protocolpool" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/slashing" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/staking" // import for side-effects
_ "git.vdb.to/cerc-io/laconicd/x/auction/module" // import for side-effects
_ "git.vdb.to/cerc-io/laconicd/x/bond/module" // import for side-effects
_ "git.vdb.to/cerc-io/laconicd/x/onboarding/module" // import for side-effects
_ "git.vdb.to/cerc-io/laconicd/x/registry/module" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/auth" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/bank" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/consensus" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/crisis" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/distribution" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/mint" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/slashing" // import for side-effects
_ "github.com/cosmos/cosmos-sdk/x/staking" // import for side-effects
)
// DefaultNodeHome default home directories for the application daemon
var DefaultNodeHome string
//go:embed app.yaml
var AppConfigYAML []byte
var (
_ runtime.AppI = (*LaconicApp)(nil)
_ servertypes.Application = (*LaconicApp)(nil)
@ -85,52 +77,36 @@ type LaconicApp struct {
txConfig client.TxConfig
interfaceRegistry codectypes.InterfaceRegistry
// keepers
// essential keepers
AccountKeeper authkeeper.AccountKeeper
BankKeeper bankkeeper.Keeper
BankKeeper bankkeeper.BaseKeeper
StakingKeeper *stakingkeeper.Keeper
SlashingKeeper slashingkeeper.Keeper
DistrKeeper distrkeeper.Keeper
GovKeeper *govkeeper.Keeper
CrisisKeeper *crisiskeeper.Keeper
ConsensusParamsKeeper consensuskeeper.Keeper
EvidenceKeeper evidencekeeper.Keeper
// supplementary keepers
ProtocolPoolKeeper protocolpoolkeeper.Keeper
// laconic keepers
AuctionKeeper *auctionkeeper.Keeper // (Use * as per ProvideModule implementation)
AuctionKeeper *auctionkeeper.Keeper
BondKeeper *bondkeeper.Keeper
RegistryKeeper registrykeeper.Keeper
OnboardingKeeper *onboardingkeeper.Keeper
NitroKeeper nitrokeeper.Keeper
// simulation manager
sm *module.SimulationManager
}
func init() {
userHomeDir, err := os.UserHomeDir()
var err error
DefaultNodeHome, err = clienthelpers.GetNodeHomeDirectory(".laconicd")
if err != nil {
panic(err)
}
DefaultNodeHome = filepath.Join(userHomeDir, ".laconicd")
}
// AppConfig returns the default app config.
func AppConfig() depinject.Config {
return depinject.Configs(
appconfig.LoadYAML(AppConfigYAML),
depinject.Supply(
// supply custom module basics
map[string]module.AppModuleBasic{
genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
govtypes.ModuleName: gov.NewAppModuleBasic(
[]govclient.ProposalHandler{
paramsclient.ProposalHandler,
},
),
},
),
)
}
// NewLaconicApp returns a reference to an initialized LaconicApp.
@ -145,16 +121,20 @@ func NewLaconicApp(
var (
app = &LaconicApp{}
appBuilder *runtime.AppBuilder
)
if err := depinject.Inject(
depinject.Configs(
AppConfig(),
appConfig = depinject.Configs(
AppModuleConfig,
depinject.Supply(
logger,
appOpts,
),
),
)
)
// if err := depinject.Inject(appConfig,
if err := depinject.InjectDebug(
depinject.FileLogger("/Users/roy/vulcanize/dump/laconic-debug/depinject_app.log"),
appConfig,
&appBuilder,
&app.appCodec,
&app.legacyAmino,
@ -166,13 +146,14 @@ func NewLaconicApp(
&app.SlashingKeeper,
&app.DistrKeeper,
&app.GovKeeper,
&app.CrisisKeeper,
&app.ConsensusParamsKeeper,
&app.EvidenceKeeper,
&app.ProtocolPoolKeeper,
&app.AuctionKeeper,
&app.BondKeeper,
&app.RegistryKeeper,
&app.OnboardingKeeper,
&app.NitroKeeper,
); err != nil {
return nil, err
}
@ -181,6 +162,13 @@ func NewLaconicApp(
RegisterCustomInterfaces(app.interfaceRegistry)
RegisterCustomLegacyAminoCodec(app.legacyAmino)
// create and set vote extension handler
voteExtOp := func(bApp *baseapp.BaseApp) {
voteExtHandler := app.NewVoteExtensionHandler()
voteExtHandler.SetHandlers(bApp)
}
baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
// register streaming services
@ -190,15 +178,13 @@ func NewLaconicApp(
/**** Module Options ****/
app.ModuleManager.RegisterInvariants(app.CrisisKeeper)
// create the simulation manager and define the order of the modules for deterministic simulations
// NOTE: this is not required apps that don't use the simulator for fuzz testing transactions
app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, make(map[string]module.AppModuleSimulation, 0))
app.sm.RegisterStoreDecoders()
// set custom ante handlers
app.setCustomAnteHandler()
// set custom ante handler
app.setAnteHandler()
if err := app.Load(loadLatest); err != nil {
return nil, err
@ -207,12 +193,56 @@ func NewLaconicApp(
return app, nil
}
// setCustomAnteHandler overwrites default ante handlers with custom ante handlers
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.50.10/x/auth/tx/config/config.go#L149
func (app *LaconicApp) setAnteHandler() error {
anteHandler, err := ante.NewAnteHandler(
ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
SignModeHandler: app.txConfig.SignModeHandler(),
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
TxFeeChecker: checkTxFeeWithValidatorMinGasPrices,
},
)
if err != nil {
return err
}
// Set the AnteHandler for the app
app.SetAnteHandler(anteHandler)
return nil
}
// LegacyAmino returns LaconicApp's amino codec.
//
// NOTE: This is solely to be used for testing purposes as it may be desirable
// for modules to register their own custom testing types.
func (app *LaconicApp) LegacyAmino() *codec.LegacyAmino {
return app.legacyAmino
}
// AppCodec returns LaconicApp's app codec.
//
// NOTE: This is solely to be used for testing purposes as it may be desirable
// for modules to register their own custom testing types.
func (app *LaconicApp) AppCodec() codec.Codec {
return app.appCodec
}
// InterfaceRegistry returns LaconicApp's InterfaceRegistry.
func (app *LaconicApp) InterfaceRegistry() codectypes.InterfaceRegistry {
return app.interfaceRegistry
}
// TxConfig returns LaconicApp's TxConfig
func (app *LaconicApp) TxConfig() client.TxConfig {
return app.txConfig
}
// GetKey returns the KVStoreKey for the provided store key.
//
// NOTE: This is solely to be used for testing purposes.
func (app *LaconicApp) GetKey(storeKey string) *storetypes.KVStoreKey {
sk := app.UnsafeFindStoreKey(storeKey)
kvStoreKey, ok := sk.(*storetypes.KVStoreKey)

View File

@ -6,14 +6,35 @@ modules:
# During begin block slashing happens after distr.BeginBlocker so that
# there is nothing left over in the validator fee pool, so as to keep the CanWithdrawInvariant invariant.
# NOTE: staking module is required if HistoricalEntries param > 0
begin_blockers: [distribution, slashing, evidence, staking]
end_blockers: [crisis, gov, staking, auction, registry]
begin_blockers: [distribution, protocolpool, slashing, evidence, staking]
end_blockers: [gov, staking, protocolpool, auction, registry]
# NOTE: The genutils module must occur after staking so that pools are properly initialized with tokens from genesis accounts.
# NOTE: The genutils module must also occur after auth so that it can access the params from auth.
init_genesis: [auth, bank, distribution, staking, slashing, gov, crisis, genutil, evidence, auction, bond, registry, onboarding]
init_genesis:
- consensus
- auth
- bank
- distribution
- staking
- slashing
- gov
- genutil
- evidence
- protocolpool
- auction
- bond
- registry
- onboarding
- nitro
override_store_keys:
- module_name: auth
kv_store_key: acc
skip_store_keys:
- tx
- validate
- genutil
- runtime
- vesting
- name: auth
config:
"@type": cosmos.auth.module.v1.Module
@ -27,6 +48,8 @@ modules:
permissions: [burner, staking]
- account: gov
permissions: [burner]
- account: protocolpool
- account: protocolpool_escrow
- account: auction
- account: auction_burn
- account: bond
@ -34,11 +57,12 @@ modules:
- account: record_rent
- account: authority_rent
- account: lps_lockup
enable_unordered_transactions: true
- name: bank
config:
"@type": cosmos.bank.module.v1.Module
blocked_module_accounts_override:
[auth, distribution, bonded_tokens_pool, not_bonded_tokens_pool]
[fee_collector, distribution, bonded_tokens_pool, not_bonded_tokens_pool]
- name: staking
config:
"@type": cosmos.staking.module.v1.Module
@ -60,12 +84,12 @@ modules:
- name: gov
config:
"@type": cosmos.gov.module.v1.Module
- name: crisis
config:
"@type": cosmos.crisis.module.v1.Module
- name: evidence
config:
"@type": cosmos.evidence.module.v1.Module
- name: protocolpool
config:
"@type": cosmos.protocolpool.module.v1.Module
- name: bond
config:
"@type": cerc.bond.module.v1.Module
@ -78,3 +102,6 @@ modules:
- name: onboarding
config:
"@type": cerc.onboarding.module.v1.Module
- name: nitro
config:
"@type": cerc.nitro.module.v1.Module

50
app/app_test.go Normal file
View File

@ -0,0 +1,50 @@
package app
import (
"fmt"
"testing"
abci "github.com/cometbft/cometbft/abci/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/stretchr/testify/require"
"cosmossdk.io/log"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
)
func TestLaconicAppExportAndBlockedAccounts(t *testing.T) {
db := dbm.NewMemDB()
logger := log.NewTestLogger(t)
app := NewLaconicAppWithCustomOptions(t, false, SetupOptions{
Logger: logger.With("instance", "first"),
DB: db,
AppOpts: simtestutil.NewAppOptionsWithFlagHome(t.TempDir()),
})
// BlockedAddresses returns a map of addresses in app v1 and a map of modules name in app di.
for _, acc := range BlockedAccounts() {
addr := app.AccountKeeper.GetModuleAddress(acc)
require.True(
t,
app.BankKeeper.BlockedAddr(addr),
fmt.Sprintf("ensure that blocked addresses are properly set in bank keeper: %s should be blocked", acc),
)
}
// finalize block so we have CheckTx state set
_, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{
Height: 1,
})
require.NoError(t, err)
_, err = app.Commit()
require.NoError(t, err)
// Making a new app object with the db, so that initchain hasn't been called
app2, err := NewLaconicApp(logger.With("instance", "second"), db, nil, true, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()))
require.NoError(t, err)
_, err = app2.ExportAppStateAndValidators(false, []string{}, []string{})
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
}

42
app/config.go Normal file
View File

@ -0,0 +1,42 @@
package app
import (
_ "embed"
"cosmossdk.io/depinject"
"cosmossdk.io/depinject/appconfig"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
"github.com/cosmos/cosmos-sdk/x/gov"
govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/utils"
)
var (
//go:embed app.yaml
AppModuleConfigYAML []byte
// AppModuleConfig returns the default app config.
AppModuleConfig = depinject.Configs(
appconfig.LoadYAML(AppModuleConfigYAML),
depinject.Provide(
server.NewGasService,
),
depinject.Supply(
utils.NewAddressCodec,
utils.NewValidatorAddressCodec,
utils.NewConsensusAddressCodec,
// supply custom module basics
map[string]module.AppModuleBasic{
genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
govtypes.ModuleName: gov.NewAppModuleBasic(
[]govclient.ProposalHandler{},
),
},
),
)
)

62
app/config/config.go Normal file
View File

@ -0,0 +1,62 @@
package config
import (
"fmt"
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
"github.com/pelletier/go-toml/v2"
"git.vdb.to/cerc-io/laconicd/app/params"
"git.vdb.to/cerc-io/laconicd/server/nitro"
"git.vdb.to/cerc-io/laconicd/server/relay"
)
type LaconicAppConfig struct {
serverconfig.Config `mapstructure:",squash"`
customConfigs `mapstructure:",squash"`
}
// the custom parts of the full config, so we can encode them separately
type customConfigs struct {
Nitro nitro.Config `mapstructure:"nitro" toml:"nitro"`
Relay relay.Config `mapstructure:"relay" toml:"relay"`
}
var DefaultConfigTemplate string
func init() {
var err error
DefaultConfigTemplate, err = createConfigTemplate()
if err != nil {
panic(err)
}
}
func DefaultConfig() LaconicAppConfig {
srvCfg := serverconfig.DefaultConfig()
// In laconicd, we set the min gas prices to 0.
srvCfg.MinGasPrices = "0" + params.CoinUnit
// Now we set the custom config default values.
customAppConfig := LaconicAppConfig{
*srvCfg,
customConfigs{
Nitro: *nitro.DefaultConfig(),
Relay: *relay.DefaultConfig(),
},
}
return customAppConfig
}
func createConfigTemplate() (string, error) {
defaultCustomConfigs := customConfigs{
Nitro: *nitro.DefaultConfig(),
Relay: *relay.DefaultConfig(),
}
b, err := toml.Marshal(defaultCustomConfigs)
if err != nil {
return "", err
}
return fmt.Sprintf("%s\n%s", serverconfig.DefaultConfigTemplate, b), nil
}

View File

@ -2,12 +2,13 @@ package app
import (
"encoding/json"
"errors"
"fmt"
"log"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
storetypes "cosmossdk.io/store/types"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
sdk "github.com/cosmos/cosmos-sdk/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
@ -15,14 +16,11 @@ import (
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// ExportAppStateAndValidators exports the state of the application for a genesis file.
func (app *LaconicApp) ExportAppStateAndValidators(
forZeroHeight bool,
jailAllowedAddrs []string,
modulesToExport []string,
) (servertypes.ExportedApp, error) {
// ExportAppStateAndValidators exports the state of the application for a genesis
// file.
func (app *LaconicApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs, modulesToExport []string) (servertypes.ExportedApp, error) {
// as if they could withdraw from the start of the next block
ctx := app.NewContextLegacy(true, tmproto.Header{Height: app.LastBlockHeight()})
ctx := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
// We export at last height + 1, because that's the height at which
// CometBFT will start InitChain.
@ -32,9 +30,9 @@ func (app *LaconicApp) ExportAppStateAndValidators(
app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
}
genState, err := app.ModuleManager.ExportGenesis(ctx, app.appCodec)
genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport)
if err != nil {
return servertypes.ExportedApp{}, fmt.Errorf("failed to export genesis state: %w", err)
return servertypes.ExportedApp{}, err
}
appState, err := json.MarshalIndent(genState, "", " ")
@ -47,19 +45,18 @@ func (app *LaconicApp) ExportAppStateAndValidators(
AppState: appState,
Validators: validators,
Height: height,
ConsensusParams: app.BaseApp.GetConsensusParams(ctx),
ConsensusParams: app.GetConsensusParams(ctx),
}, err
}
// prepare for fresh start at zero height
// NOTE zero height genesis is a temporary feature, which will be deprecated in favor of export at a block height
// NOTE zero height genesis is a temporary feature which will be deprecated
//
// in favor of export at a block height
func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) {
applyAllowedAddrs := false
applyAllowedAddrs := len(jailAllowedAddrs) > 0
// check if there is a allowed address list
if len(jailAllowedAddrs) > 0 {
applyAllowedAddrs = true
}
allowedAddrsMap := make(map[string]bool)
@ -74,15 +71,17 @@ func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr
/* Handle fee distribution state. */
// withdraw all validator commission
_ = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
err := app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
if err != nil {
panic(err)
}
_, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, valBz)
return false
})
if err != nil {
panic(err)
}
// withdraw all delegator rewards
dels, err := app.StakingKeeper.GetAllDelegations(ctx)
@ -96,10 +95,7 @@ func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr
panic(err)
}
delAddr, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress)
if err != nil {
panic(err)
}
delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress)
_, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr)
}
@ -115,23 +111,20 @@ func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr
ctx = ctx.WithBlockHeight(0)
// reinitialize all validators
_ = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
err = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
if err != nil {
panic(err)
}
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, valBz)
if err != nil {
panic(err)
}
feePool, err := app.DistrKeeper.FeePool.Get(ctx)
if err != nil {
panic(err)
}
feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
if err := app.DistrKeeper.FeePool.Set(ctx, feePool); err != nil {
panic(err)
@ -149,11 +142,7 @@ func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr
if err != nil {
panic(err)
}
delAddr, err := sdk.AccAddressFromBech32(del.DelegatorAddress)
if err != nil {
panic(err)
}
delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress)
if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil {
// never called as BeforeDelegationCreated always returns nil
@ -172,36 +161,45 @@ func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr
/* Handle staking state. */
// iterate through redelegations, reset creation height
_ = app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) {
err = app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) {
for i := range red.Entries {
red.Entries[i].CreationHeight = 0
}
_ = app.StakingKeeper.SetRedelegation(ctx, red)
err = app.StakingKeeper.SetRedelegation(ctx, red)
if err != nil {
panic(err)
}
return false
})
if err != nil {
panic(fmt.Errorf("error while iterating redelegations: %w", err))
}
// iterate through unbonding delegations, reset creation height
_ = app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) {
err = app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) {
for i := range ubd.Entries {
ubd.Entries[i].CreationHeight = 0
}
_ = app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
err = app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
if err != nil {
panic(err)
}
return false
})
if err != nil {
panic(fmt.Errorf("error while iterating unbonding delegations: %w", err))
}
// Iterate through validators by power descending, reset bond heights, and
// update bond intra-tx counters.
store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey))
iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey)
counter := int16(0)
for ; iter.Valid(); iter.Next() {
addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key()))
validator, err := app.StakingKeeper.GetValidator(ctx, addr)
if errors.Is(err, stakingtypes.ErrNoValidatorFound) {
if err != nil {
panic("expected validator, not found")
} else if err != nil {
panic(err)
}
validator.UnbondingHeight = 0
@ -209,8 +207,10 @@ func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr
validator.Jailed = true
}
_ = app.StakingKeeper.SetValidator(ctx, validator)
counter++
err = app.StakingKeeper.SetValidator(ctx, validator)
if err != nil {
panic(fmt.Errorf("unable to set validator: %w", err))
}
}
if err := iter.Close(); err != nil {
@ -232,12 +232,12 @@ func (app *LaconicApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddr
info.StartHeight = 0
err = app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
if err != nil {
log.Fatal(err)
panic("unable to set validator signing info")
}
return false
},
)
if err != nil {
log.Fatal(err)
panic(fmt.Errorf("error while iterating validator signing info: %w", err))
}
}

View File

@ -39,6 +39,7 @@ var (
func init() {
SetAddressPrefixes()
RegisterDenoms()
sdk.DefaultBondDenom = CoinUnit
}
func RegisterDenoms() {

View File

@ -1,21 +1,18 @@
package cmd
import (
"context"
"errors"
"io"
"github.com/cometbft/cometbft/node"
dbm "github.com/cosmos/cosmos-db"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/sync/errgroup"
"cosmossdk.io/log"
confixcmd "cosmossdk.io/tools/confix/cmd"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/debug"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/pruning"
"github.com/cosmos/cosmos-sdk/client/rpc"
@ -25,37 +22,41 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/gql"
laconicserver "git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/server/nitro"
"git.vdb.to/cerc-io/laconicd/server/relay"
)
func initRootCmd(rootCmd *cobra.Command, txConfig client.TxConfig, basicManager module.BasicManager) {
func initRootCmd(
rootCmd *cobra.Command,
txConfig client.TxConfig,
basicManager module.BasicManager,
) *laconicserver.ServerAux {
cfg := sdk.GetConfig()
cfg.Seal()
rootCmd.AddCommand(
genutilcli.InitCmd(basicManager, app.DefaultNodeHome),
NewTestnetCmd(basicManager, banktypes.GenesisBalancesIterator{}),
debug.Cmd(),
confixcmd.ConfigCommand(),
pruning.Cmd(newApp, app.DefaultNodeHome),
snapshot.Cmd(newApp),
)
server.AddCommands(rootCmd, app.DefaultNodeHome, newApp, appExport, func(startCmd *cobra.Command) {
// Override start command to run the GQL server
newStartCmd := server.StartCmdWithOptions(newApp, app.DefaultNodeHome, server.StartCmdOptions{
PostSetup: func(svrCtx *server.Context, clientCtx client.Context, ctx context.Context, g *errgroup.Group) error {
g.Go(func() error {
return gql.Server(ctx, clientCtx, svrCtx.Logger.With("module", "gql-server"))
})
return nil
},
})
startCmd.RunE = newStartCmd.RunE
addStartComponents := func(startCmd *cobra.Command) {
laconicserver.SetRequiredComponents(startCmd, &gql.Server{}, &nitro.Server{}, &relay.Server{})
}
var srv laconicserver.ServerAux
server.AddCommandsWithStartCmdOptions(rootCmd, app.DefaultNodeHome, newApp, appExport, server.StartCmdOptions{
AddFlags: addStartComponents,
// reactors will be configured at start, after the command is fully initialized
CometNodeOptions: []node.Option{srv.AddReactors},
})
// Capture the genesis command from genutilcli and add new commands
@ -65,11 +66,60 @@ func initRootCmd(rootCmd *cobra.Command, txConfig client.TxConfig, basicManager
// add keybase, auxiliary RPC, query, genesis, and tx child commands
rootCmd.AddCommand(
server.StatusCommand(),
genesisCmd,
genesisCommand(txConfig, basicManager),
queryCommand(),
txCommand(),
keys.Commands(),
)
return &srv
}
// initializes all server components needed by a command
func initComponents(
configMap laconicserver.ConfigMap,
clientCtx client.Context,
logger log.Logger,
needComponent func(string) bool,
) ([]laconicserver.ServerComponent, error) {
var (
components []laconicserver.ServerComponent
gqlServer = &gql.Server{}
nitroServer = &nitro.Server{}
relayServer = &relay.Server{}
err error
)
if needComponent(gqlServer.Name()) {
gqlServer, err = gql.New(clientCtx, configMap, logger.With("module", "gql-server"))
if err != nil {
return nil, err
}
components = append(components, gqlServer)
}
if needComponent(nitroServer.Name()) {
nitroServer, err = nitro.New(configMap, logger, clientCtx.Keyring)
if err != nil {
return nil, err
}
components = append(components, nitroServer)
}
if needComponent(relayServer.Name()) {
relayServer, err = relay.New(configMap, logger)
if err != nil {
return nil, err
}
components = append(components, relayServer)
}
return components, nil
}
// genesisCommand builds genesis-related `laconicd genesis` command. Users may provide application specific commands as a parameter
func genesisCommand(txConfig client.TxConfig, basicManager module.BasicManager, cmds ...*cobra.Command) *cobra.Command {
cmd := genutilcli.Commands(txConfig, basicManager, app.DefaultNodeHome)
for _, subCmd := range cmds {
cmd.AddCommand(subCmd)
}
return cmd
}
func queryCommand() *cobra.Command {
@ -83,7 +133,7 @@ func queryCommand() *cobra.Command {
}
cmd.AddCommand(
rpc.ValidatorCommand(),
rpc.WaitTxCmd(),
server.QueryBlockCmd(),
authcmd.QueryTxsByEventsCmd(),
server.QueryBlocksCmd(),
@ -118,18 +168,26 @@ func txCommand() *cobra.Command {
return cmd
}
// newApp is an appCreator
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application {
// newApp creates the application
func newApp(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) servertypes.Application {
baseappOptions := server.DefaultBaseappOptions(appOpts)
app, err := app.NewLaconicApp(logger, db, traceStore, true, appOpts, baseappOptions...)
ret, err := app.NewLaconicApp(
logger, db, traceStore, true,
appOpts,
baseappOptions...,
)
if err != nil {
panic(err)
}
return app
return ret
}
// appExport creates a new app (optionally at a given height) and exports state.
// appExport creates a new laconicd (optionally at a given height) and exports state.
func appExport(
logger log.Logger,
db dbm.DB,
@ -140,18 +198,6 @@ func appExport(
appOpts servertypes.AppOptions,
modulesToExport []string,
) (servertypes.ExportedApp, error) {
var (
laconicApp *app.LaconicApp
err error
)
// this check is necessary as we use the flag in x/upgrade.
// we can exit more gracefully by checking the flag here.
homePath, ok := appOpts.Get(flags.FlagHome).(string)
if !ok || homePath == "" {
return servertypes.ExportedApp{}, errors.New("application home not set")
}
viperAppOpts, ok := appOpts.(*viper.Viper)
if !ok {
return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper")
@ -161,18 +207,17 @@ func appExport(
viperAppOpts.Set(server.FlagInvCheckPeriod, 1)
appOpts = viperAppOpts
var laconicApp *app.LaconicApp
var err error
if height != -1 {
laconicApp, err = app.NewLaconicApp(logger, db, traceStore, false, appOpts)
if err != nil {
if laconicApp, err = app.NewLaconicApp(logger, db, traceStore, false, appOpts); err != nil {
return servertypes.ExportedApp{}, err
}
if err := laconicApp.LoadHeight(height); err != nil {
if err = laconicApp.LoadHeight(height); err != nil {
return servertypes.ExportedApp{}, err
}
} else {
laconicApp, err = app.NewLaconicApp(logger, db, traceStore, true, appOpts)
if err != nil {
if laconicApp, err = app.NewLaconicApp(logger, db, traceStore, true, appOpts); err != nil {
return servertypes.ExportedApp{}, err
}
}

View File

@ -0,0 +1,36 @@
package cmd
import (
"time"
cmtcfg "github.com/cometbft/cometbft/config"
)
// initCometBFTConfig helps to override default CometBFT config values.
func initCometBFTConfig() *cmtcfg.Config {
cfg := cmtcfg.DefaultConfig()
// display only warn logs by default for builtin modules except server, p2p, state
cfg.LogLevel = "*:warn,server:info,p2p:info,state:info"
cfg.LogLevel += ",auction:info,bond:info,registry:info,gql-server:info"
// // TODO: understand full meaning of these settings
cfg.Consensus.TimeoutPropose = 5000 * time.Millisecond
// cfg.Consensus.TimeoutProposeDelta = 500 * time.Millisecond
// cfg.Consensus.TimeoutPrevote = 1000 * time.Millisecond
// cfg.Consensus.TimeoutPrevoteDelta = 500 * time.Millisecond
// cfg.Consensus.TimeoutPrecommit = 1000 * time.Millisecond
// cfg.Consensus.TimeoutPrecommitDelta = 500 * time.Millisecond
// // start new block as soon as 2/3 precommits are received
// cfg.Consensus.TimeoutCommit = 0 * time.Second
cfg.Consensus.CreateEmptyBlocks = false
// overwrite default pprof listen address
cfg.RPC.PprofListenAddress = "localhost:6060"
// use previous db backend
cfg.DBBackend = "goleveldb"
return cfg
}

87
cmd/laconicd/cmd/log.go Normal file
View File

@ -0,0 +1,87 @@
package cmd
import (
"fmt"
"io"
"log/slog"
"math"
"time"
"cosmossdk.io/log"
sdk_slog "cosmossdk.io/log/slog"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/lmittmann/tint"
"git.vdb.to/cerc-io/laconicd/server"
)
func createSlogLogger(cfg server.ConfigMap, out io.Writer) (log.Logger, error) {
handler, err := createSlogHandler(cfg, out)
if err != nil {
return nil, err
}
return sdk_slog.NewCustomLogger(slog.New(handler)), nil
}
func createSlogHandler(cfg server.ConfigMap, out io.Writer) (slog.Handler, error) {
var (
format string
noColor bool
level string
)
if v, ok := cfg[flags.FlagLogFormat]; ok {
format = v.(string)
}
if v, ok := cfg[flags.FlagLogNoColor]; ok {
noColor = v.(bool)
}
if v, ok := cfg[flags.FlagLogLevel]; ok {
level = v.(string)
}
logLvl, err := parseSlogLevel(level)
if err != nil {
// If the log level is not a valid zerolog level, then we try to parse it as a key filter.
filterFunc, err := log.ParseLogLevelWithParser(level, parseSlogLevel)
if err != nil {
return nil, err
}
out = log.NewFilterWriter(out, filterFunc)
}
var handler slog.Handler
// if json format is requested, ignore no_color
if format == flags.OutputFormatJSON {
handler = slog.NewJSONHandler(out, &slog.HandlerOptions{
Level: logLvl,
})
} else {
handler = tint.NewHandler(out, &tint.Options{
Level: logLvl,
TimeFormat: time.RFC3339Nano,
NoColor: noColor,
})
}
return handler, nil
}
func parseSlogLevel(l string) (slog.Level, error) {
// Legal log level values are hardcoded into server/v2 command factory:
// (trace|debug|info|warn|error|fatal|panic|disabled or '*:<level>,<key>:<level>')
// however, slog only has debug, info, warn, error
switch l {
case "debug", "trace":
return slog.LevelDebug, nil
case "info":
return slog.LevelInfo, nil
case "warn":
return slog.LevelWarn, nil
case "error":
return slog.LevelError, nil
case "fatal", "panic":
return slog.LevelError, nil
case "disabled":
return slog.Level(math.MaxInt), nil
}
return 0, fmt.Errorf("invalid log level: %s", l)
}

View File

@ -1,26 +1,23 @@
package cmd
import (
"fmt"
"os"
"time"
cmtcfg "github.com/cometbft/cometbft/config"
"github.com/spf13/cobra"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
"cosmossdk.io/client/v2/autocli"
clientv2keyring "cosmossdk.io/client/v2/autocli/keyring"
"cosmossdk.io/core/address"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/config"
clientconfig "github.com/cosmos/cosmos-sdk/client/config"
nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
@ -28,16 +25,19 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/app/params"
"git.vdb.to/cerc-io/laconicd/gql"
types "git.vdb.to/cerc-io/laconicd/x/types/v1"
appconfig "git.vdb.to/cerc-io/laconicd/app/config"
)
const EnvPrefix = "LACONIC"
const (
// EnvPrefix is the environment variable prefix for the application
EnvPrefix = "LACONIC"
// NewRootCmd creates a new root command for laconicd. It is called once in the
// main function.
func NewRootCmd() *cobra.Command {
// DefaultNodeHome is the default data directory name for the application
DefaultNodeHome = ".laconicd"
)
// NewRootCmd creates a new root command for laconicd. It is called once in the main function.
func NewRootCmd(args ...string) (*cobra.Command, error) {
var (
txConfigOpts tx.ConfigOptions
autoCliOpts autocli.AppOptions
@ -46,13 +46,12 @@ func NewRootCmd() *cobra.Command {
)
if err := depinject.Inject(
depinject.Configs(app.AppConfig(),
depinject.Configs(app.AppModuleConfig,
depinject.Supply(
log.NewNopLogger(),
),
depinject.Provide(
ProvideClientContext,
ProvideKeyring,
),
),
&txConfigOpts,
@ -60,68 +59,34 @@ func NewRootCmd() *cobra.Command {
&moduleBasicManager,
&clientCtx,
); err != nil {
panic(err)
return nil, err
}
rootCmd := &cobra.Command{
Use: "laconicd",
Short: "Laconic Daemon",
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
// set the default command outputs
cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.ErrOrStderr())
clientCtx = clientCtx.WithCmdContext(cmd.Context()).WithViper(EnvPrefix)
clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
clientCtx, err = config.ReadFromClientConfig(clientCtx)
if err != nil {
return err
}
// sign mode textual is only available in online mode
if !clientCtx.Offline {
// This needs to go after ReadFromClientConfig, as that function ets the RPC client needed for SIGN_MODE_TEXTUAL.
txConfigOpts.EnabledSignModes = append(txConfigOpts.EnabledSignModes, signing.SignMode_SIGN_MODE_TEXTUAL)
txConfigOpts.TextualCoinMetadataQueryFn = txmodule.NewGRPCCoinMetadataQueryFn(clientCtx)
txConfigWithTextual, err := tx.NewTxConfigWithOptions(codec.NewProtoCodec(clientCtx.InterfaceRegistry), txConfigOpts)
if err != nil {
return err
}
clientCtx = clientCtx.WithTxConfig(txConfigWithTextual)
}
if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
return err
}
// overwrite the minimum gas price from the app configuration
srvCfg := serverconfig.DefaultConfig()
srvCfg.MinGasPrices = fmt.Sprintf("0%s", params.CoinUnit)
// overwrite the block timeout
cmtCfg := cmtcfg.DefaultConfig()
cmtCfg.Consensus.TimeoutCommit = 3 * time.Second
cmtCfg.LogLevel = "*:error,p2p:info,state:info,auction:info,bond:info,registry:info,gql-server:info" // better default logging
return server.InterceptConfigsPreRunHandler(cmd, serverconfig.DefaultConfigTemplate, srvCfg, cmtCfg)
},
Use: "laconicd",
SilenceErrors: true,
SilenceUsage: true, // prevent usage printing on every error
PersistentPreRunE: RootCommandPersistentPreRun(clientCtx, txConfigOpts),
}
initRootCmd(rootCmd, clientCtx.TxConfig, moduleBasicManager)
initCtx := initRootCmd(rootCmd, clientCtx.TxConfig, moduleBasicManager)
nodeCmds := nodeservice.NewNodeCommands()
autoCliOpts.ModuleOptions = make(map[string]*autocliv1.ModuleOptions)
autoCliOpts.ModuleOptions[nodeCmds.Name()] = nodeCmds.AutoCLIOptions()
if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
panic(err)
return nil, err
}
rootCmd.SetArgs(args)
// Add flags for GQL server.
rootCmd = gql.AddGQLFlags(rootCmd)
return rootCmd
// now enhance the subcommand
subCmd, _, err := rootCmd.Find(args)
if err != nil {
return nil, err
}
initCtx.AddComponents(subCmd, initComponents)
return rootCmd, nil
}
func ProvideClientContext(
@ -141,7 +106,7 @@ func ProvideClientContext(
WithViper(EnvPrefix) // env variable prefix
// Read the config again to overwrite the default values with the values from the config file
clientCtx, _ = config.ReadFromClientConfig(clientCtx)
clientCtx, _ = clientconfig.ReadFromClientConfig(clientCtx)
// Workaround: Unset clientCtx.HomeDir and clientCtx.KeyringDir from depinject clientCtx as they are given precedence over
// the CLI args (--home flag) in some commands
@ -149,9 +114,10 @@ func ProvideClientContext(
clientCtx.HomeDir = ""
clientCtx.KeyringDir = ""
// XXX TODO fix after rebase
// Custom LockupAccount type needs to be registered
interfaceRegistry.RegisterImplementations((*types.LockupAccountI)(nil), &types.LockupAccount{})
interfaceRegistry.RegisterImplementations((*authtypes.GenesisAccount)(nil), &types.LockupAccount{})
// interfaceRegistry.RegisterImplementations((*types.LockupAccountI)(nil), &types.LockupAccount{})
// interfaceRegistry.RegisterImplementations((*authtypes.GenesisAccount)(nil), &types.LockupAccount{})
return clientCtx
}
@ -164,3 +130,61 @@ func ProvideKeyring(clientCtx client.Context, addressCodec address.Codec) (clien
return keyring.NewAutoCLIKeyring(kb)
}
func RootCommandPersistentPreRun(
clientCtx client.Context,
txConfigOpts tx.ConfigOptions,
) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, _ []string) error {
// set the default command outputs
cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.ErrOrStderr())
clientCtx = clientCtx.WithCmdContext(cmd.Context()).WithViper("")
clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
clientCtx, err = clientconfig.ReadFromClientConfig(clientCtx)
if err != nil {
return err
}
// This needs to go after ReadFromClientConfig, as that function
// sets the RPC client needed for SIGN_MODE_TEXTUAL. This sign mode
// is only available if the client is online.
if !clientCtx.Offline {
txConfigOpts.EnabledSignModes = append(txConfigOpts.EnabledSignModes, signing.SignMode_SIGN_MODE_TEXTUAL)
txConfigOpts.TextualCoinMetadataQueryFn = txmodule.NewGRPCCoinMetadataQueryFn(clientCtx)
txConfigWithTextual, err := tx.NewTxConfigWithOptions(codec.NewProtoCodec(clientCtx.InterfaceRegistry), txConfigOpts)
if err != nil {
return err
}
clientCtx = clientCtx.WithTxConfig(txConfigWithTextual)
}
if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
return err
}
appConfig := appconfig.DefaultConfig()
cmtConfig := initCometBFTConfig()
serverCtx, err := server.InterceptConfigsAndCreateContext(
cmd, appconfig.DefaultConfigTemplate, appConfig, cmtConfig,
)
if err != nil {
return err
}
// use slog-based logger to combine with nitro logs
logger, err := createSlogLogger(serverCtx.Viper.AllSettings(), cmd.OutOrStdout())
if err != nil {
return err
}
serverCtx.Logger = logger.With(log.ModuleKey, "server")
return server.SetCmdServerContext(cmd, serverCtx)
}
}

View File

@ -0,0 +1,67 @@
package cmd_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/flags"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/cmd/laconicd/cmd"
)
func TestInitCmd(t *testing.T) {
rootCmd, err := cmd.NewRootCmd()
require.NoError(t, err)
rootCmd.SetArgs([]string{
"init", // Test the init cmd
"simapp-test", // Moniker
fmt.Sprintf("--%s=%s", cli.FlagOverwrite, "true"), // Overwrite genesis.json, in case it already exists
})
require.NoError(t, svrcmd.Execute(rootCmd, "", app.DefaultNodeHome))
}
func TestHomeFlagRegistration(t *testing.T) {
homeDir := "/tmp/foo"
rootCmd, err := cmd.NewRootCmd()
require.NoError(t, err)
rootCmd.SetArgs([]string{
"query",
fmt.Sprintf("--%s", flags.FlagHome),
homeDir,
})
require.NoError(t, svrcmd.Execute(rootCmd, "", app.DefaultNodeHome))
result, err := rootCmd.Flags().GetString(flags.FlagHome)
require.NoError(t, err)
require.Equal(t, result, homeDir)
}
func TestHelpRequested(t *testing.T) {
argz := [][]string{
{"query", "--help"},
{"query", "tx", "-h"},
{"--help"},
{"start", "-h"},
}
for _, args := range argz {
rootCmd, err := cmd.NewRootCmd(args...)
require.NoError(t, err)
var out bytes.Buffer
rootCmd.SetArgs(args)
rootCmd.SetOut(&out)
require.NoError(t, rootCmd.Execute())
require.Contains(t, out.String(), args[0])
require.Contains(t, out.String(), "--help")
require.Contains(t, out.String(), "Usage:")
}
}

598
cmd/laconicd/cmd/testnet.go Normal file
View File

@ -0,0 +1,598 @@
package cmd
import (
"bufio"
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"time"
cmtcfg "github.com/cometbft/cometbft/config"
cmtconfig "github.com/cometbft/cometbft/config"
cmttime "github.com/cometbft/cometbft/types/time"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"cosmossdk.io/math"
"cosmossdk.io/math/unsafe"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/server"
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/network"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"git.vdb.to/cerc-io/laconicd/app"
appconfig "git.vdb.to/cerc-io/laconicd/app/config"
)
var (
flagNodeDirPrefix = "node-dir-prefix"
flagNumValidators = "validator-count"
flagOutputDir = "output-dir"
flagNodeDaemonHome = "node-daemon-home"
flagStartingIPAddress = "starting-ip-address"
flagListenIPAddress = "listen-ip-address"
flagEnableLogging = "enable-logging"
flagGRPCAddress = "grpc.address"
flagRPCAddress = "rpc.address"
flagAPIAddress = "api.address"
flagPrintMnemonic = "print-mnemonic"
flagStakingDenom = "staking-denom"
flagCommitTimeout = "commit-timeout"
flagSingleHost = "single-host"
)
type initArgs struct {
algo string
chainID string
keyringBackend string
minGasPrices string
nodeDaemonHome string
nodeDirPrefix string
numValidators int
outputDir string
startingIPAddress string
listenIPAddress string
singleMachine bool
bondTokenDenom string
}
type startArgs struct {
algo string
apiAddress string
chainID string
enableLogging bool
grpcAddress string
minGasPrices string
numValidators int
outputDir string
printMnemonic bool
rpcAddress string
timeoutCommit time.Duration
}
func addTestnetFlagsToCmd(cmd *cobra.Command) {
cmd.Flags().IntP(flagNumValidators, "v", 4, "Number of validators to initialize the testnet with")
cmd.Flags().StringP(flagOutputDir, "o", "./.testnets", "Directory to store initialization data for the testnet")
cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)")
cmd.Flags().String(flags.FlagKeyType, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for")
// support old flags name for backwards compatibility
cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
if name == flags.FlagKeyAlgorithm {
name = flags.FlagKeyType
}
return pflag.NormalizedName(name)
})
}
// NewTestnetCmd creates a root testnet command with subcommands to run an in-process testnet or initialize
// validator configuration files for running a multi-validator testnet in a separate process
func NewTestnetCmd(mm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator) *cobra.Command {
testnetCmd := &cobra.Command{
Use: "testnet",
Short: "subcommands for starting or configuring local testnets",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
testnetCmd.AddCommand(testnetStartCmd())
testnetCmd.AddCommand(testnetInitFilesCmd(mm, genBalIterator))
return testnetCmd
}
// testnetInitFilesCmd returns a cmd to initialize all files for CometBFT testnet and application
func testnetInitFilesCmd(mm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator) *cobra.Command {
cmd := &cobra.Command{
Use: "init-files",
Short: "Initialize config directories & files for a multi-validator testnet running locally via separate processes (e.g. Docker Compose or similar)",
Long: fmt.Sprintf(`init-files will setup one directory per validator and populate each with
necessary files (private validator, genesis, config, etc.) for running validator nodes.
Booting up a network with these validator folders is intended to be used with Docker Compose,
or a similar setup where each node has a manually configurable IP address.
Note, strict routability for addresses is turned off in the config file.
Example:
%s testnet init-files --validator-count 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2
`, version.AppName),
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
config := cmtcfg.DefaultConfig()
args := initArgs{}
args.outputDir, _ = cmd.Flags().GetString(flagOutputDir)
args.keyringBackend, _ = cmd.Flags().GetString(flags.FlagKeyringBackend)
args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID)
args.minGasPrices, _ = cmd.Flags().GetString(server.FlagMinGasPrices)
args.nodeDirPrefix, _ = cmd.Flags().GetString(flagNodeDirPrefix)
args.nodeDaemonHome, _ = cmd.Flags().GetString(flagNodeDaemonHome)
args.startingIPAddress, _ = cmd.Flags().GetString(flagStartingIPAddress)
args.listenIPAddress, _ = cmd.Flags().GetString(flagListenIPAddress)
args.numValidators, _ = cmd.Flags().GetInt(flagNumValidators)
args.algo, _ = cmd.Flags().GetString(flags.FlagKeyType)
args.bondTokenDenom, _ = cmd.Flags().GetString(flagStakingDenom)
args.singleMachine, _ = cmd.Flags().GetBool(flagSingleHost)
config.Consensus.TimeoutCommit, err = cmd.Flags().GetDuration(flagCommitTimeout)
if err != nil {
return err
}
return initTestnetFiles(clientCtx, cmd, config, mm, genBalIterator, args)
},
}
addTestnetFlagsToCmd(cmd)
cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix for the name of per-validator subdirectories (to be number-suffixed like node0, node1, ...)")
cmd.Flags().String(flagNodeDaemonHome, "laconicd", "Home directory of the node's daemon configuration")
cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
cmd.Flags().String(flagListenIPAddress, "127.0.0.1", "TCP or UNIX socket IP address for the RPC server to listen on")
cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)")
cmd.Flags().Duration(flagCommitTimeout, 5*time.Second, "Time to wait after a block commit before starting on the new height")
cmd.Flags().Bool(flagSingleHost, false, "Cluster runs on a single host machine with different ports")
cmd.Flags().String(flagStakingDenom, sdk.DefaultBondDenom, "Default staking token denominator")
return cmd
}
// testnetStartCmd returns a cmd to start multi validator in-process testnet
func testnetStartCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "start",
Short: "Launch an in-process multi-validator testnet",
Long: fmt.Sprintf(`testnet will launch an in-process multi-validator testnet,
and generate a directory for each validator populated with necessary
configuration files (private validator, genesis, config, etc.).
Example:
%s testnet --validator-count 4 --output-dir ./.testnets
`, version.AppName),
RunE: func(cmd *cobra.Command, _ []string) (err error) {
args := startArgs{}
args.outputDir, _ = cmd.Flags().GetString(flagOutputDir)
args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID)
args.minGasPrices, _ = cmd.Flags().GetString(server.FlagMinGasPrices)
args.numValidators, _ = cmd.Flags().GetInt(flagNumValidators)
args.algo, _ = cmd.Flags().GetString(flags.FlagKeyType)
args.enableLogging, _ = cmd.Flags().GetBool(flagEnableLogging)
args.rpcAddress, _ = cmd.Flags().GetString(flagRPCAddress)
args.apiAddress, _ = cmd.Flags().GetString(flagAPIAddress)
args.grpcAddress, _ = cmd.Flags().GetString(flagGRPCAddress)
args.printMnemonic, _ = cmd.Flags().GetBool(flagPrintMnemonic)
return startTestnet(cmd, args)
},
}
addTestnetFlagsToCmd(cmd)
cmd.Flags().Bool(flagEnableLogging, false, "Enable INFO logging of CometBFT validator nodes")
cmd.Flags().String(flagRPCAddress, "tcp://0.0.0.0:26657", "the RPC address to listen on")
cmd.Flags().String(flagAPIAddress, "tcp://0.0.0.0:1317", "the address to listen on for REST API")
cmd.Flags().String(flagGRPCAddress, "0.0.0.0:9090", "the gRPC server address to listen on")
cmd.Flags().Bool(flagPrintMnemonic, true, "print mnemonic of first validator to stdout for manual testing")
return cmd
}
const nodeDirPerm = 0o755
// initTestnetFiles initializes testnet files for a testnet to be run in a separate process
func initTestnetFiles(
clientCtx client.Context,
cmd *cobra.Command,
nodeConfig *cmtconfig.Config,
mm module.BasicManager,
genBalIterator banktypes.GenesisBalancesIterator,
args initArgs,
) error {
if args.chainID == "" {
args.chainID = "chain-" + unsafe.Str(6)
}
nodeIDs := make([]string, args.numValidators)
valPubKeys := make([]cryptotypes.PubKey, args.numValidators)
appConfig := appconfig.DefaultConfig()
appConfig.MinGasPrices = args.minGasPrices
appConfig.API.Enable = true
appConfig.Telemetry.Enabled = true
appConfig.Telemetry.PrometheusRetentionTime = 60
appConfig.Telemetry.EnableHostnameLabel = false
appConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", args.chainID}}
var (
genAccounts []authtypes.GenesisAccount
genBalances []banktypes.Balance
genFiles []string
)
const (
rpcPort = 26657
apiPort = 1317
grpcPort = 9090
)
p2pPortStart := 26656
inBuf := bufio.NewReader(cmd.InOrStdin())
// generate private keys, node IDs, and initial transactions
for i := 0; i < args.numValidators; i++ {
var portOffset int
if args.singleMachine {
portOffset = i
p2pPortStart = 16656 // use different start point to not conflict with rpc port
nodeConfig.P2P.AddrBookStrict = false
nodeConfig.P2P.PexReactor = false
nodeConfig.P2P.AllowDuplicateIP = true
appConfig.API.Address = fmt.Sprintf("tcp://0.0.0.0:%d", apiPort+portOffset)
appConfig.GRPC.Address = fmt.Sprintf("0.0.0.0:%d", grpcPort+portOffset)
}
nodeDirName := fmt.Sprintf("%s%d", args.nodeDirPrefix, i)
nodeDir := filepath.Join(args.outputDir, nodeDirName, args.nodeDaemonHome)
gentxsDir := filepath.Join(args.outputDir, "gentxs")
nodeConfig.SetRoot(nodeDir)
nodeConfig.Moniker = nodeDirName
nodeConfig.RPC.ListenAddress = fmt.Sprintf("tcp://%s:%d", args.listenIPAddress, rpcPort+portOffset)
if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil {
_ = os.RemoveAll(args.outputDir)
return err
}
var (
err error
ip string
)
if args.singleMachine {
ip = "127.0.0.1"
} else {
ip, err = getIP(i, args.startingIPAddress)
if err != nil {
_ = os.RemoveAll(args.outputDir)
return err
}
}
nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig)
if err != nil {
_ = os.RemoveAll(args.outputDir)
return err
}
memo := fmt.Sprintf("%s@%s:%d", nodeIDs[i], ip, p2pPortStart+portOffset)
genFiles = append(genFiles, nodeConfig.GenesisFile())
kb, err := keyring.New(sdk.KeyringServiceName(), args.keyringBackend, nodeDir, inBuf, clientCtx.Codec)
if err != nil {
return err
}
keyringAlgos, _ := kb.SupportedAlgorithms()
algo, err := keyring.NewSigningAlgoFromString(args.algo, keyringAlgos)
if err != nil {
return err
}
addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo)
if err != nil {
_ = os.RemoveAll(args.outputDir)
return err
}
info := map[string]string{"secret": secret}
cliPrint, err := json.Marshal(info)
if err != nil {
return err
}
// save private key seed words
if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), nodeDir, cliPrint); err != nil {
return err
}
accTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction)
accStakingTokens := sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction)
coins := sdk.Coins{
sdk.NewCoin("testtoken", accTokens),
sdk.NewCoin(args.bondTokenDenom, accStakingTokens),
}
genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()})
genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0))
valAddr := sdk.ValAddress(addr)
valStr := valAddr.String()
valTokens := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction)
createValMsg, err := stakingtypes.NewMsgCreateValidator(
valStr,
valPubKeys[i],
sdk.NewCoin(args.bondTokenDenom, valTokens),
stakingtypes.NewDescription(nodeDirName, "", "", "", ""),
stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()),
math.OneInt(),
)
if err != nil {
return err
}
txBuilder := clientCtx.TxConfig.NewTxBuilder()
if err := txBuilder.SetMsgs(createValMsg); err != nil {
return err
}
txBuilder.SetMemo(memo)
txFactory := tx.Factory{}
txFactory = txFactory.
WithChainID(args.chainID).
WithMemo(memo).
WithKeybase(kb).
WithTxConfig(clientCtx.TxConfig)
if err := tx.Sign(cmd.Context(), txFactory, nodeDirName, txBuilder, true); err != nil {
return err
}
txBz, err := clientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx())
if err != nil {
return err
}
if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil {
return err
}
srvconfig.SetConfigTemplate(appconfig.DefaultConfigTemplate)
srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), appConfig)
}
if err := initGenFiles(clientCtx, mm, args.chainID, genAccounts, genBalances, genFiles, args.numValidators); err != nil {
return err
}
err := collectGenFiles(
clientCtx, nodeConfig, args.chainID, nodeIDs, valPubKeys, args.numValidators,
args.outputDir, args.nodeDirPrefix, args.nodeDaemonHome, genBalIterator,
rpcPort, p2pPortStart, args.singleMachine,
)
if err != nil {
return err
}
server.GetServerContextFromCmd(cmd).Viper.Set(flags.FlagHome, nodeConfig.RootDir)
cmd.PrintErrf("Successfully initialized %d node directories\n", args.numValidators)
return nil
}
func initGenFiles(
clientCtx client.Context, mm module.BasicManager, chainID string,
genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance,
genFiles []string, numValidators int,
) error {
appGenState := mm.DefaultGenesis(clientCtx.Codec)
// set the accounts in the genesis state
var authGenState authtypes.GenesisState
clientCtx.Codec.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState)
accounts, err := authtypes.PackAccounts(genAccounts)
if err != nil {
return err
}
authGenState.Accounts = accounts
appGenState[authtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&authGenState)
// set the balances in the genesis state
var bankGenState banktypes.GenesisState
clientCtx.Codec.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState)
bankGenState.Balances = banktypes.SanitizeGenesisBalances(genBalances)
for _, bal := range bankGenState.Balances {
bankGenState.Supply = bankGenState.Supply.Add(bal.Coins...)
}
appGenState[banktypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&bankGenState)
appGenStateJSON, err := json.MarshalIndent(appGenState, "", " ")
if err != nil {
return err
}
appGenesis := genutiltypes.NewAppGenesisWithVersion(chainID, appGenStateJSON)
// generate empty genesis files for each validator and save
for i := 0; i < numValidators; i++ {
if err := appGenesis.SaveAs(genFiles[i]); err != nil {
return err
}
}
return nil
}
func collectGenFiles(
clientCtx client.Context, nodeConfig *cmtconfig.Config, chainID string,
nodeIDs []string, valPubKeys []cryptotypes.PubKey, numValidators int,
outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator,
rpcPortStart, p2pPortStart int,
singleMachine bool,
) error {
var appState json.RawMessage
genTime := cmttime.Now()
for i := 0; i < numValidators; i++ {
if singleMachine {
portOffset := i
nodeConfig.RPC.ListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", rpcPortStart+portOffset)
nodeConfig.P2P.ListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", p2pPortStart+portOffset)
}
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome)
gentxsDir := filepath.Join(outputDir, "gentxs")
nodeConfig.Moniker = nodeDirName
nodeConfig.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, valPubKey)
appGenesis, err := genutiltypes.AppGenesisFromFile(nodeConfig.GenesisFile())
if err != nil {
return err
}
nodeAppState, err := genutil.GenAppStateFromConfig(
clientCtx.Codec,
clientCtx.TxConfig,
nodeConfig,
initCfg,
appGenesis,
genBalIterator,
genutiltypes.DefaultMessageValidator,
clientCtx.TxConfig.SigningContext().ValidatorAddressCodec(),
)
if err != nil {
return err
}
if appState == nil {
// set the canonical application state (they should not differ)
appState = nodeAppState
}
genFile := nodeConfig.GenesisFile()
// overwrite each validator's genesis file to have a canonical genesis time
if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil {
return err
}
}
return nil
}
func getIP(i int, startingIPAddr string) (ip string, err error) {
if len(startingIPAddr) == 0 {
ip, err = server.ExternalIP()
if err != nil {
return "", err
}
return ip, nil
}
return calculateIP(startingIPAddr, i)
}
func calculateIP(ip string, i int) (string, error) {
ipv4 := net.ParseIP(ip).To4()
if ipv4 == nil {
return "", fmt.Errorf("%v: non ipv4 address", ip)
}
for j := 0; j < i; j++ {
ipv4[3]++
}
return ipv4.String(), nil
}
func writeFile(name, dir string, contents []byte) error {
file := filepath.Join(dir, name)
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("could not create directory %q: %w", dir, err)
}
if err := os.WriteFile(file, contents, 0o600); err != nil {
return err
}
return nil
}
// startTestnet starts an in-process testnet
func startTestnet(cmd *cobra.Command, args startArgs) error {
networkConfig := network.DefaultConfig(app.NewTestNetworkFixture)
// Default networkConfig.ChainID is random, and we should only override it if chainID provided
// is non-empty
if args.chainID != "" {
networkConfig.ChainID = args.chainID
}
networkConfig.SigningAlgo = args.algo
networkConfig.MinGasPrices = args.minGasPrices
networkConfig.NumValidators = args.numValidators
networkConfig.EnableLogging = args.enableLogging
networkConfig.RPCAddress = args.rpcAddress
networkConfig.APIAddress = args.apiAddress
networkConfig.GRPCAddress = args.grpcAddress
networkConfig.PrintMnemonic = args.printMnemonic
networkConfig.TimeoutCommit = args.timeoutCommit
networkLogger := network.NewCLILogger(cmd)
baseDir := fmt.Sprintf("%s/%s", args.outputDir, networkConfig.ChainID)
if _, err := os.Stat(baseDir); !os.IsNotExist(err) {
return fmt.Errorf(
"testnests directory already exists for chain-id '%s': %s, please remove or select a new --chain-id",
networkConfig.ChainID, baseDir)
}
testnet, err := network.New(networkLogger, baseDir, networkConfig)
if err != nil {
return err
}
if _, err := testnet.WaitForHeight(1); err != nil {
return err
}
cmd.Println("press the Enter Key to terminate")
if _, err := fmt.Scanln(); err != nil { // wait for Enter Key
return err
}
testnet.Cleanup()
return nil
}

View File

@ -0,0 +1,72 @@
package cmd
import (
"context"
"fmt"
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/types/module"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/consensus"
"github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
"github.com/cosmos/cosmos-sdk/x/staking"
)
func Test_TestnetCmd(t *testing.T) {
moduleBasic := module.NewBasicManager(
auth.AppModuleBasic{},
genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
bank.AppModuleBasic{},
staking.AppModuleBasic{},
distribution.AppModuleBasic{},
consensus.AppModuleBasic{},
)
home := t.TempDir()
encodingConfig := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, staking.AppModuleBasic{})
logger := log.NewNopLogger()
cfg, err := genutiltest.CreateDefaultCometConfig(home)
require.NoError(t, err)
err = genutiltest.ExecInitCmd(moduleBasic, home, encodingConfig.Codec)
require.NoError(t, err)
serverCtx := server.NewContext(viper.New(), cfg, logger)
clientCtx := client.Context{}.
WithCodec(encodingConfig.Codec).
WithHomeDir(home).
WithTxConfig(encodingConfig.TxConfig)
ctx := context.Background()
ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx)
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
cmd := testnetInitFilesCmd(moduleBasic, banktypes.GenesisBalancesIterator{})
cmd.SetArgs([]string{
fmt.Sprintf("--%s=test", flags.FlagKeyringBackend, keyring.BackendTest),
fmt.Sprintf("--output-dir=%s", home),
})
err = cmd.ExecuteContext(ctx)
require.NoError(t, err)
genFile := cfg.GenesisFile()
appState, _, err := genutiltypes.GenesisStateFromGenFile(genFile)
require.NoError(t, err)
bankGenState := banktypes.GetGenesisStateFromAppState(encodingConfig.Codec, appState)
require.NotEmpty(t, bankGenState.Supply.String())
}

View File

@ -1,28 +0,0 @@
package main_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/flags"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/cmd/laconicd/cmd"
)
func TestInitCmd(t *testing.T) {
rootCmd := cmd.NewRootCmd()
rootCmd.SetArgs([]string{
"init", // Test the init cmd
"localtestnet", // Moniker
fmt.Sprintf("--%s=%s", cli.FlagOverwrite, "true"), // Overwrite genesis.json, in case it already exists
fmt.Sprintf("--%s=%s", flags.FlagChainID, "laconic_9000-1"),
})
err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome)
require.NoError(t, err)
}

View File

@ -1,22 +1,32 @@
package main
import (
"errors"
"fmt"
"os"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/app/params"
_ "git.vdb.to/cerc-io/laconicd/app/params" // import for side-effects
"git.vdb.to/cerc-io/laconicd/cmd/laconicd/cmd"
)
func main() {
params.SetAddressPrefixes()
rootCmd := cmd.NewRootCmd()
if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
fmt.Fprintln(rootCmd.OutOrStderr(), err)
// reproduce default cobra behavior so that eager parsing of flags is possible.
// see: https://github.com/spf13/cobra/blob/e94f6d0dd9a5e5738dca6bce03c4b1207ffbc0ec/command.go#L1082
args := os.Args[1:]
rootCmd, err := cmd.NewRootCmd(args...)
if err != nil {
if _, pErr := fmt.Fprintln(os.Stderr, err); pErr != nil {
panic(errors.Join(err, pErr))
}
os.Exit(1)
}
if err := svrcmd.Execute(rootCmd, cmd.EnvPrefix, app.DefaultNodeHome); err != nil {
if _, pErr := fmt.Fprintln(rootCmd.OutOrStderr(), err); pErr != nil {
panic(errors.Join(err, pErr))
}
os.Exit(1)
}
}

296
go.mod
View File

@ -1,8 +1,8 @@
module git.vdb.to/cerc-io/laconicd
go 1.21
go 1.23.5
toolchain go1.21.0
toolchain go1.23.6
replace (
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
@ -12,138 +12,192 @@ replace (
github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
)
// DEV
replace (
cosmossdk.io/client/v2 => ../mod/cosmos-sdk/client/v2
cosmossdk.io/log => ../mod/cosmos-sdk/log
cosmossdk.io/systemtests => ../mod/cosmos-sdk/systemtests
cosmossdk.io/tools/confix => ../mod/cosmos-sdk/tools/confix
github.com/cometbft/cometbft => ../mod/cometbft
// github.com/cometbft/cometbft/api => ../mod/cometbft/api
github.com/cosmos/cosmos-sdk => ../mod/cosmos-sdk
)
// DEV - nitro deps
replace (
// github.com/ethereum/go-ethereum => ../mod/go-ethereum
git.vdb.to/cerc-io/chainsig => ../mod/chainsig
github.com/statechannels/go-nitro => ../mod/go-nitro
go.dedis.ch/kyber/v3 => ../mod/kyber
// go.dedis.ch/kyber/v3 => github.com/cerc-io/kyber/v3 v3.0.0-20250728035006-f80208a7f291 // branch dev-3.x
)
require (
cosmossdk.io/api v0.7.5
cosmossdk.io/client/v2 v2.0.0-beta.1
cosmossdk.io/collections v0.4.0
cosmossdk.io/core v0.11.1
cosmossdk.io/depinject v1.0.0
cosmossdk.io/errors v1.0.1
cosmossdk.io/log v1.4.1
cosmossdk.io/math v1.3.0
cosmossdk.io/store v1.1.1
cosmossdk.io/tools/confix v0.1.0
cosmossdk.io/x/evidence v0.1.1
cosmossdk.io/api v0.9.2
cosmossdk.io/client/v2 v2.0.0-00010101000000-000000000000
cosmossdk.io/collections v1.3.1
cosmossdk.io/core v0.11.3
cosmossdk.io/depinject v1.2.1
cosmossdk.io/errors v1.0.2
cosmossdk.io/log v1.6.0
cosmossdk.io/math v1.5.3
cosmossdk.io/store v1.1.2
cosmossdk.io/systemtests v1.2.1
cosmossdk.io/tools/confix v0.1.2
cosmossdk.io/x/evidence v0.2.0
git.vdb.to/cerc-io/chain-signatures v0.1.0
github.com/99designs/gqlgen v0.17.22
github.com/cometbft/cometbft v0.38.12
github.com/cosmos/cosmos-db v1.0.2
github.com/cometbft/cometbft v0.38.17
github.com/cosmos/cosmos-db v1.1.3
github.com/cosmos/cosmos-proto v1.0.0-beta.5
github.com/cosmos/cosmos-sdk v0.50.10
github.com/cosmos/cosmos-sdk v0.53.0
github.com/cosmos/go-bip39 v1.0.0
github.com/cosmos/gogoproto v1.7.0
github.com/deckarep/golang-set v1.8.0
github.com/ethereum/go-ethereum v1.14.5
github.com/ethereum/go-ethereum v1.16.1
github.com/gibson042/canonicaljson-go v1.0.3
github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/chi/v5 v5.2.2
github.com/golang/protobuf v1.5.4
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-cid v0.5.0
github.com/ipld/go-ipld-prime v0.21.0
github.com/lmittmann/tint v1.1.2
github.com/mitchellh/mapstructure v1.5.0
github.com/pelletier/go-toml/v2 v2.2.4
github.com/rs/cors v1.11.1
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/statechannels/go-nitro v0.1.2
github.com/stretchr/testify v1.9.0
github.com/vektah/gqlparser/v2 v2.5.11
golang.org/x/sync v0.8.0
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237
google.golang.org/grpc v1.64.1
google.golang.org/protobuf v1.34.2
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.7
github.com/spf13/viper v1.20.1
github.com/statechannels/go-nitro v0.0.0-00010101000000-000000000000
github.com/stretchr/testify v1.10.0
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
github.com/vektah/gqlparser/v2 v2.5.30
go.dedis.ch/kyber/v3 v3.1.0
golang.org/x/time v0.10.0
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a
google.golang.org/grpc v1.72.2
google.golang.org/protobuf v1.36.6
gopkg.in/yaml.v3 v3.0.1
)
require (
cosmossdk.io/x/tx v0.13.5 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
cosmossdk.io/schema v1.1.0 // indirect
cosmossdk.io/x/tx v0.14.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
github.com/99designs/keyring v1.2.2 // indirect
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/DataDog/zstd v1.5.7 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/bgentry/speakeasy v0.2.0 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
github.com/cockroachdb/errors v1.11.3 // indirect
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v1.1.1 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/errors v1.12.0 // indirect
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
github.com/cockroachdb/pebble v1.1.5 // indirect
github.com/cockroachdb/redact v1.1.6 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/cometbft/cometbft-db v0.11.0 // indirect
github.com/cometbft/cometbft-db v0.14.1 // indirect
github.com/consensys/gnark-crypto v0.18.0 // indirect
github.com/cosmos/btcutil v1.0.5 // indirect
github.com/cosmos/gogogateway v1.2.0 // indirect
github.com/cosmos/iavl v1.2.0 // indirect
github.com/cosmos/iavl v1.2.2 // indirect
github.com/cosmos/ics23/go v0.11.0 // indirect
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
github.com/creachadair/atomicfile v0.3.1 // indirect
github.com/creachadair/tomledit v0.0.24 // indirect
github.com/cosmos/ledger-cosmos-go v0.14.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/creachadair/atomicfile v0.3.7 // indirect
github.com/creachadair/tomledit v0.0.27 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
github.com/emicklei/dot v1.6.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/emicklei/dot v1.6.2 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/ferranbt/fastssz v0.1.2 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/getsentry/sentry-go v0.33.0 // indirect
github.com/go-kit/kit v0.13.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang/glog v1.2.4 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/flatbuffers v2.0.8+incompatible // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/orderedcode v0.0.1 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-metrics v0.5.3 // indirect
github.com/hashicorp/go-plugin v1.5.2 // indirect
github.com/hashicorp/go-metrics v0.5.4 // indirect
github.com/hashicorp/go-plugin v1.6.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/huandu/skiplist v1.2.0 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/huandu/skiplist v1.2.1 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/improbable-eng/grpc-web v0.15.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.10.7 // indirect
github.com/linxGnu/grocksdb v1.8.14 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/linxGnu/grocksdb v1.9.7 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/highwayhash v1.0.2 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mdp/qrterminal/v3 v3.2.1 // indirect
github.com/minio/highwayhash v1.0.3 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
@ -154,49 +208,75 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pion/dtls/v2 v2.2.12 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/stun/v2 v2.0.0 // indirect
github.com/pion/transport/v2 v2.2.10 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/polydawn/refmt v0.89.0 // indirect
github.com/prometheus/client_golang v1.20.1 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.63.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.14 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/tidwall/btree v1.7.0 // indirect
github.com/tidwall/buntdb v1.2.10 // indirect
github.com/tidwall/grect v0.1.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/rtred v0.1.2 // indirect
github.com/tidwall/tinyqueue v0.1.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/urfave/cli/v2 v2.27.7 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/zondax/hid v0.9.2 // indirect
github.com/zondax/ledger-go v0.14.3 // indirect
go.etcd.io/bbolt v1.3.10 // indirect
go.dedis.ch/fixbuf v1.0.3 // indirect
go.dedis.ch/protobuf v1.0.11 // indirect
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/mock v0.5.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gotest.tools/v3 v3.5.1 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/arch v0.15.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools/v3 v3.5.2 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
nhooyr.io/websocket v1.8.6 // indirect
pgregory.net/rapid v1.1.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
pgregory.net/rapid v1.2.0 // indirect
rsc.io/qr v0.2.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace github.com/statechannels/go-nitro v0.1.2 => github.com/cerc-io/go-nitro v0.1.3-ts-port-0.1.10

873
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@ On having some change in the GQL schema (for example: adding a new query) update
## Start server
```shell
laconicd start --gql-playground --gql-server
laconicd start --gql.playground --gql.server
```
Basic node status:

19
gql/config.go Normal file
View File

@ -0,0 +1,19 @@
package gql
func DefaultConfig() *Config {
return &Config{
Enable: false,
Playground: false,
PlaygroundAPIBase: "",
Port: 9473,
// LogFile: "",
}
}
type Config struct {
Enable bool `mapstructure:"enable" toml:"enable" comment:"Enable the GraphQL server."`
Playground bool `mapstructure:"playground" toml:"playground" comment:"Enable the GraphQL playground."`
PlaygroundAPIBase string `mapstructure:"playground-api-base" toml:"playground-api-base" comment:"GraphQL API base path to use in the playground."`
Port uint `mapstructure:"port" toml:"port" comment:"Port to use for the GraphQL server."`
// LogFile string `mapstructure:"log-file" toml:"log-file" comment:"File to tail for the 'getLogs' API."`
}

View File

@ -1,15 +1,24 @@
package gql
import "github.com/spf13/cobra"
import "github.com/spf13/pflag"
// AddGQLFlags adds gql flags for
func AddGQLFlags(cmd *cobra.Command) *cobra.Command {
// Add flags for GQL server.
cmd.PersistentFlags().Bool("gql-server", false, "Start GQL server.")
cmd.PersistentFlags().Bool("gql-playground", false, "Enable GQL playground.")
cmd.PersistentFlags().String("gql-playground-api-base", "", "GQL API base path to use in GQL playground.")
cmd.PersistentFlags().String("gql-port", "9473", "Port to use for the GQL server.")
cmd.PersistentFlags().String("log-file", "", "File to tail for GQL 'getLogs' API.")
var (
FlagEnable = prefix("enable")
FlagPlayground = prefix("playground")
FlagPlaygroundAPIBase = prefix("playground-api-base")
FlagPort = prefix("port")
//FlagLogFile = prefix("log-file")
)
return cmd
// AddGQLFlags adds flags for the GraphQL server.
func AddGQLFlags(flags *pflag.FlagSet) {
flags.Bool(FlagEnable, false, "Enable the GraphQL server")
flags.Bool(FlagPlayground, false, "Enable the GraphQL playground")
flags.String(FlagPlaygroundAPIBase, "", "GraphQL API base path to use in the playground")
flags.String(FlagPort, "9473", "Port to use for the GraphQL server.")
// cmd.PersistentFlags().String(FlagLogFile, "", "File to tail for GraphQL 'getLogs' API")
}
func prefix(f string) string {
return ServerName + "." + f
}

View File

@ -4,9 +4,9 @@
schema:
- cerc-io/laconicd/*.graphql
exec:
filename: generated.go
filename: schema/generated.go
model:
filename: models_gen.go
filename: schema/models_gen.go
resolver:
filename: resolver.go
type: Resolver
@ -14,4 +14,4 @@ resolver:
models:
Link:
model:
- git.vdb.to/cerc-io/laconicd/gql.Link
- git.vdb.to/cerc-io/laconicd/gql/schema.Link

View File

@ -12,6 +12,8 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"git.vdb.to/cerc-io/laconicd/gql/schema"
"git.vdb.to/cerc-io/laconicd/utils"
auctiontypes "git.vdb.to/cerc-io/laconicd/x/auction"
bondtypes "git.vdb.to/cerc-io/laconicd/x/bond"
onboardingTypes "git.vdb.to/cerc-io/laconicd/x/onboarding"
@ -33,13 +35,13 @@ type Resolver struct {
}
// Query is the entry point to query execution.
func (r *Resolver) Query() QueryResolver {
func (r *Resolver) Query() schema.QueryResolver {
return &queryResolver{r}
}
type queryResolver struct{ *Resolver }
func (q queryResolver) GetAuthorities(ctx context.Context, owner *string) ([]*Authority, error) {
func (q queryResolver) GetAuthorities(ctx context.Context, owner *string) ([]*schema.Authority, error) {
nsQueryClient := registrytypes.NewQueryClient(q.ctx)
auctionQueryClient := auctiontypes.NewQueryClient(q.ctx)
@ -53,14 +55,14 @@ func (q queryResolver) GetAuthorities(ctx context.Context, owner *string) ([]*Au
return nil, err
}
authorities := make([]*Authority, len(authoritiesResp.GetAuthorities()))
authorities := make([]*schema.Authority, len(authoritiesResp.GetAuthorities()))
for i, a := range authoritiesResp.Authorities {
entry, err := getAuthorityRecord(*a.Entry, auctionQueryClient)
if err != nil {
return nil, err
}
authorities[i] = &Authority{
authorities[i] = &schema.Authority{
Name: a.Name,
Entry: entry,
}
@ -69,10 +71,10 @@ func (q queryResolver) GetAuthorities(ctx context.Context, owner *string) ([]*Au
return authorities, nil
}
func (q queryResolver) LookupAuthorities(ctx context.Context, names []string) ([]*AuthorityRecord, error) {
func (q queryResolver) LookupAuthorities(ctx context.Context, names []string) ([]*schema.AuthorityRecord, error) {
nsQueryClient := registrytypes.NewQueryClient(q.ctx)
auctionQueryClient := auctiontypes.NewQueryClient(q.ctx)
gqlResponse := []*AuthorityRecord{}
gqlResponse := []*schema.AuthorityRecord{}
for _, name := range names {
res, err := nsQueryClient.Whois(context.Background(), &registrytypes.QueryWhoisRequest{Name: name})
@ -97,9 +99,9 @@ func (q queryResolver) LookupAuthorities(ctx context.Context, names []string) ([
return gqlResponse, nil
}
func (q queryResolver) ResolveNames(ctx context.Context, names []string) ([]*Record, error) {
func (q queryResolver) ResolveNames(ctx context.Context, names []string) ([]*schema.Record, error) {
nsQueryClient := registrytypes.NewQueryClient(q.ctx)
var gqlResponse []*Record
var gqlResponse []*schema.Record
for _, name := range names {
res, err := nsQueryClient.ResolveLrn(context.Background(), &registrytypes.QueryResolveLrnRequest{Lrn: name})
if err != nil {
@ -118,9 +120,9 @@ func (q queryResolver) ResolveNames(ctx context.Context, names []string) ([]*Rec
return gqlResponse, nil
}
func (q queryResolver) LookupNames(ctx context.Context, names []string) ([]*NameRecord, error) {
func (q queryResolver) LookupNames(ctx context.Context, names []string) ([]*schema.NameRecord, error) {
nsQueryClient := registrytypes.NewQueryClient(q.ctx)
var gqlResponse []*NameRecord
var gqlResponse []*schema.NameRecord
for _, name := range names {
res, err := nsQueryClient.LookupLrn(context.Background(), &registrytypes.QueryLookupLrnRequest{Lrn: name})
@ -140,7 +142,7 @@ func (q queryResolver) LookupNames(ctx context.Context, names []string) ([]*Name
return gqlResponse, nil
}
func (q queryResolver) QueryRecords(ctx context.Context, attributes []*KeyValueInput, all *bool, limit *int, offset *int) ([]*Record, error) {
func (q queryResolver) QueryRecords(ctx context.Context, attributes []*schema.KeyValueInput, all *bool, limit *int, offset *int) ([]*schema.Record, error) {
nsQueryClient := registrytypes.NewQueryClient(q.ctx)
var pagination *query.PageRequest
@ -175,7 +177,7 @@ func (q queryResolver) QueryRecords(ctx context.Context, attributes []*KeyValueI
}
records := res.GetRecords()
gqlResponse := make([]*Record, len(records))
gqlResponse := make([]*schema.Record, len(records))
for i, record := range records {
gqlRecord, err := getGQLRecord(context.Background(), q, record)
@ -188,9 +190,9 @@ func (q queryResolver) QueryRecords(ctx context.Context, attributes []*KeyValueI
return gqlResponse, nil
}
func (q queryResolver) GetRecordsByIds(ctx context.Context, ids []string) ([]*Record, error) {
func (q queryResolver) GetRecordsByIds(ctx context.Context, ids []string) ([]*schema.Record, error) {
nsQueryClient := registrytypes.NewQueryClient(q.ctx)
gqlResponse := make([]*Record, len(ids))
gqlResponse := make([]*schema.Record, len(ids))
for i, id := range ids {
res, err := nsQueryClient.GetRecord(context.Background(), &registrytypes.QueryGetRecordRequest{Id: id})
@ -209,7 +211,7 @@ func (q queryResolver) GetRecordsByIds(ctx context.Context, ids []string) ([]*Re
return gqlResponse, nil
}
func (q queryResolver) GetStatus(ctx context.Context) (*Status, error) {
func (q queryResolver) GetStatus(ctx context.Context) (*schema.Status, error) {
nodeInfo, syncInfo, validatorInfo, err := getStatusInfo(q.ctx)
if err != nil {
return nil, err
@ -230,7 +232,7 @@ func (q queryResolver) GetStatus(ctx context.Context) (*Status, error) {
return nil, err
}
return &Status{
return &schema.Status{
Version: RegistryVersion,
Node: nodeInfo,
Sync: syncInfo,
@ -242,8 +244,8 @@ func (q queryResolver) GetStatus(ctx context.Context) (*Status, error) {
}, nil
}
func (q queryResolver) GetAccounts(ctx context.Context, addresses []string) ([]*Account, error) {
accounts := make([]*Account, len(addresses))
func (q queryResolver) GetAccounts(ctx context.Context, addresses []string) ([]*schema.Account, error) {
accounts := make([]*schema.Account, len(addresses))
for index, address := range addresses {
account, err := q.GetAccount(ctx, address)
if err != nil {
@ -254,7 +256,7 @@ func (q queryResolver) GetAccounts(ctx context.Context, addresses []string) ([]*
return accounts, nil
}
func (q queryResolver) GetAccount(ctx context.Context, address string) (*Account, error) {
func (q queryResolver) GetAccount(ctx context.Context, address string) (*schema.Account, error) {
authQueryClient := authtypes.NewQueryClient(q.ctx)
accountResponse, err := authQueryClient.Account(ctx, &authtypes.QueryAccountRequest{Address: address})
if err != nil {
@ -280,7 +282,7 @@ func (q queryResolver) GetAccount(ctx context.Context, address string) (*Account
accNum := strconv.FormatUint(account.GetAccountNumber(), 10)
seq := strconv.FormatUint(account.GetSequence(), 10)
return &Account{
return &schema.Account{
Address: address,
Number: accNum,
Sequence: seq,
@ -289,8 +291,8 @@ func (q queryResolver) GetAccount(ctx context.Context, address string) (*Account
}, nil
}
func (q queryResolver) GetBondsByIds(ctx context.Context, ids []string) ([]*Bond, error) {
bonds := make([]*Bond, len(ids))
func (q queryResolver) GetBondsByIds(ctx context.Context, ids []string) ([]*schema.Bond, error) {
bonds := make([]*schema.Bond, len(ids))
for index, id := range ids {
bondObj, err := q.GetBond(ctx, id)
if err != nil {
@ -302,7 +304,7 @@ func (q queryResolver) GetBondsByIds(ctx context.Context, ids []string) ([]*Bond
return bonds, nil
}
func (q *queryResolver) GetBond(ctx context.Context, id string) (*Bond, error) {
func (q *queryResolver) GetBond(ctx context.Context, id string) (*schema.Bond, error) {
bondQueryClient := bondtypes.NewQueryClient(q.ctx)
bondResp, err := bondQueryClient.GetBondById(context.Background(), &bondtypes.QueryGetBondByIdRequest{Id: id})
if err != nil {
@ -320,14 +322,14 @@ func (q *queryResolver) GetBond(ctx context.Context, id string) (*Bond, error) {
return getGQLBond(bondResp.GetBond())
}
func (q queryResolver) QueryBonds(ctx context.Context) ([]*Bond, error) {
func (q queryResolver) QueryBonds(ctx context.Context) ([]*schema.Bond, error) {
bondQueryClient := bondtypes.NewQueryClient(q.ctx)
bonds, err := bondQueryClient.Bonds(context.Background(), &bondtypes.QueryBondsRequest{})
if err != nil {
return nil, err
}
gqlResponse := make([]*Bond, len(bonds.GetBonds()))
gqlResponse := make([]*schema.Bond, len(bonds.GetBonds()))
for i, bondObj := range bonds.GetBonds() {
gqlBond, err := getGQLBond(bondObj)
if err != nil {
@ -340,8 +342,8 @@ func (q queryResolver) QueryBonds(ctx context.Context) ([]*Bond, error) {
}
// QueryBondsByOwner will return bonds by owner
func (q queryResolver) QueryBondsByOwner(ctx context.Context, ownerAddresses []string) ([]*OwnerBonds, error) {
ownerBonds := make([]*OwnerBonds, len(ownerAddresses))
func (q queryResolver) QueryBondsByOwner(ctx context.Context, ownerAddresses []string) ([]*schema.OwnerBonds, error) {
ownerBonds := make([]*schema.OwnerBonds, len(ownerAddresses))
for index, ownerAddress := range ownerAddresses {
bondsObj, err := q.GetBondsByOwner(ctx, ownerAddress)
if err != nil {
@ -353,14 +355,14 @@ func (q queryResolver) QueryBondsByOwner(ctx context.Context, ownerAddresses []s
return ownerBonds, nil
}
func (q queryResolver) GetBondsByOwner(ctx context.Context, address string) (*OwnerBonds, error) {
func (q queryResolver) GetBondsByOwner(ctx context.Context, address string) (*schema.OwnerBonds, error) {
bondQueryClient := bondtypes.NewQueryClient(q.ctx)
bondResp, err := bondQueryClient.GetBondsByOwner(context.Background(), &bondtypes.QueryGetBondsByOwnerRequest{Owner: address})
if err != nil {
return nil, err
}
ownerBonds := make([]*Bond, len(bondResp.GetBonds()))
ownerBonds := make([]*schema.Bond, len(bondResp.GetBonds()))
for i, bond := range bondResp.GetBonds() {
// #nosec G601
bondObj, err := getGQLBond(&bond) //nolint: all
@ -370,12 +372,12 @@ func (q queryResolver) GetBondsByOwner(ctx context.Context, address string) (*Ow
ownerBonds[i] = bondObj
}
return &OwnerBonds{Bonds: ownerBonds, Owner: address}, nil
return &schema.OwnerBonds{Bonds: ownerBonds, Owner: address}, nil
}
func (q queryResolver) GetAuctionsByIds(ctx context.Context, ids []string) ([]*Auction, error) {
func (q queryResolver) GetAuctionsByIds(ctx context.Context, ids []string) ([]*schema.Auction, error) {
auctionQueryClient := auctiontypes.NewQueryClient(q.ctx)
gqlAuctionResponse := make([]*Auction, len(ids))
gqlAuctionResponse := make([]*schema.Auction, len(ids))
for i, id := range ids {
auctionObj, err := auctionQueryClient.GetAuction(context.Background(), &auctiontypes.QueryGetAuctionRequest{Id: id})
if err != nil {
@ -397,18 +399,22 @@ func (q queryResolver) GetAuctionsByIds(ctx context.Context, ids []string) ([]*A
return gqlAuctionResponse, nil
}
func (q queryResolver) GetParticipants(ctx context.Context) ([]*Participant, error) {
func (q queryResolver) GetParticipants(ctx context.Context) ([]*schema.Participant, error) {
onboardingQueryClient := onboardingTypes.NewQueryClient(q.ctx)
participantResp, err := onboardingQueryClient.Participants(context.Background(), &onboardingTypes.QueryParticipantsRequest{})
if err != nil {
return nil, err
}
participants := make([]*Participant, len(participantResp.GetParticipants()))
participants := make([]*schema.Participant, len(participantResp.GetParticipants()))
for i, p := range participantResp.Participants {
participants[i] = &Participant{
nitroAddress, err := utils.EthAddressFromPubKey(p.PublicKey)
if err != nil {
return nil, err
}
participants[i] = &schema.Participant{
CosmosAddress: p.CosmosAddress,
NitroAddress: p.NitroAddress,
NitroAddress: nitroAddress.String(),
Role: p.Role,
KycID: p.KycId,
}
@ -417,7 +423,7 @@ func (q queryResolver) GetParticipants(ctx context.Context) ([]*Participant, err
return participants, nil
}
func (q queryResolver) GetParticipantByAddress(ctx context.Context, address string) (*Participant, error) {
func (q queryResolver) GetParticipantByAddress(ctx context.Context, address string) (*schema.Participant, error) {
onboardingQueryClient := onboardingTypes.NewQueryClient(q.ctx)
participantResp, err := onboardingQueryClient.GetParticipantByAddress(ctx, &onboardingTypes.QueryGetParticipantByAddressRequest{Address: address})
if err != nil {
@ -425,9 +431,13 @@ func (q queryResolver) GetParticipantByAddress(ctx context.Context, address stri
}
p := participantResp.Participant
participant := &Participant{
nitroAddress, err := utils.EthAddressFromPubKey(p.PublicKey)
if err != nil {
return nil, err
}
participant := &schema.Participant{
CosmosAddress: p.CosmosAddress,
NitroAddress: p.NitroAddress,
NitroAddress: nitroAddress.String(),
Role: p.Role,
KycID: p.KycId,
}
@ -435,7 +445,7 @@ func (q queryResolver) GetParticipantByAddress(ctx context.Context, address stri
return participant, nil
}
func (q queryResolver) GetParticipantByNitroAddress(ctx context.Context, nitroAddress string) (*Participant, error) {
func (q queryResolver) GetParticipantByNitroAddress(ctx context.Context, nitroAddress string) (*schema.Participant, error) {
onboardingQueryClient := onboardingTypes.NewQueryClient(q.ctx)
participantResp, err := onboardingQueryClient.GetParticipantByNitroAddress(
ctx,
@ -448,9 +458,9 @@ func (q queryResolver) GetParticipantByNitroAddress(ctx context.Context, nitroAd
}
p := participantResp.Participant
participant := &Participant{
participant := &schema.Participant{
CosmosAddress: p.CosmosAddress,
NitroAddress: p.NitroAddress,
NitroAddress: nitroAddress,
Role: p.Role,
KycID: p.KycId,
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
package gql
package schema
import (
"context"

View File

@ -1,6 +1,6 @@
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package gql
package schema
type Value interface {
IsValue()

View File

@ -2,74 +2,117 @@ package gql
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"cosmossdk.io/log"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/cosmos/cosmos-sdk/client"
"github.com/go-chi/chi/v5"
"github.com/rs/cors"
"github.com/spf13/viper"
"github.com/spf13/pflag"
"github.com/cosmos/cosmos-sdk/client"
"git.vdb.to/cerc-io/laconicd/gql/schema"
"git.vdb.to/cerc-io/laconicd/server"
)
// Server configures and starts the GQL server.
func Server(ctx context.Context, clientCtx client.Context, logger log.Logger) error {
if !viper.GetBool("gql-server") {
return nil
const (
ServerName = "gql"
)
type Server struct {
logger log.Logger
router chi.Router
config *Config
httpServer *http.Server
}
// func New(logger log.Logger, cfg server.ConfigMap, clientCtx client.Context) (*Server, error) {
func New(clientCtx client.Context, cfg server.ConfigMap, logger log.Logger) (*Server, error) {
s := &Server{
logger: logger.With(log.ModuleKey, ServerName),
router: chi.NewRouter(),
}
router := chi.NewRouter()
s.config = s.Config().(*Config)
if len(cfg) > 0 {
if err := server.UnmarshalSubConfig(cfg, s.Name(), &s.config); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}
}
// Add CORS middleware around every request
// See https://github.com/rs/cors for full option listing
router.Use(cors.New(cors.Options{
s.router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"*"},
Debug: false,
}).Handler)
logFile := viper.GetString("log-file")
port := viper.GetString("gql-port")
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: &Resolver{
ctx: clientCtx,
logFile: logFile,
srv := handler.NewDefaultServer(schema.NewExecutableSchema(schema.Config{Resolvers: &Resolver{
ctx: clientCtx,
// logFile: logFile,
}}))
router.Handle("/", PlaygroundHandler("/api"))
s.router.Handle("/", PlaygroundHandler("/api"))
if viper.GetBool("gql-playground") {
apiBase := viper.GetString("gql-playground-api-base")
router.Handle("/webui", PlaygroundHandler(apiBase+"/api"))
router.Handle("/console", PlaygroundHandler(apiBase+"/graphql"))
if s.config.Playground {
apiBase := s.config.PlaygroundAPIBase
s.router.Handle("/webui", PlaygroundHandler(apiBase+"/api"))
s.router.Handle("/console", PlaygroundHandler(apiBase+"/graphql"))
}
router.Handle("/api", srv)
router.Handle("/graphql", srv)
s.router.Handle("/api", srv)
s.router.Handle("/graphql", srv)
return s, nil
}
errCh := make(chan error)
func (s *Server) Name() string {
return ServerName
}
go func() {
logger.Info(fmt.Sprintf("Connect to GraphQL playground url: http://localhost:%s", port))
server := &http.Server{
Addr: ":" + port,
Handler: router,
ReadHeaderTimeout: 3 * time.Second,
}
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
// Gracefully stop the GQL server.
logger.Info("Stopping GQL server...")
// Server configures and starts the GQL server.
func (s *Server) Start(ctx context.Context) error {
if !s.config.Enable {
s.logger.Info(fmt.Sprintf("%s server is disabled via config", s.Name()))
return nil
case err := <-errCh:
logger.Error(fmt.Sprintf("Failed to start GQL server: %s", err))
}
if s.config.Playground {
s.logger.Info(fmt.Sprintf("Connect to GraphQL playground URL at http://localhost:%d", s.config.Port))
}
s.httpServer = &http.Server{
Addr: fmt.Sprintf(":%d", s.config.Port),
Handler: s.router,
ReadHeaderTimeout: 3 * time.Second,
}
if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Error("Failed to start GraphQL server", "error", err)
return err
}
return nil
}
func (s *Server) Stop(ctx context.Context) error {
if !s.config.Enable {
return nil
}
s.logger.Info("stopping GraphQL server")
return s.httpServer.Shutdown(ctx)
}
func (s *Server) Config() any {
if s.config == nil {
return DefaultConfig()
}
return s.config
}
func (s *Server) StartCmdFlags() *pflag.FlagSet {
flags := pflag.NewFlagSet(s.Name(), pflag.ExitOnError)
AddGQLFlags(flags)
return flags
}

View File

@ -7,13 +7,14 @@ import (
"strconv"
"strings"
"git.vdb.to/cerc-io/laconicd/gql/schema"
"github.com/cosmos/cosmos-sdk/client"
)
// NodeDataPath is the path to the laconicd data folder.
var NodeDataPath = os.ExpandEnv("$HOME/.laconicd/data")
func getStatusInfo(client client.Context) (*NodeInfo, *SyncInfo, *ValidatorInfo, error) {
func getStatusInfo(client client.Context) (*schema.NodeInfo, *schema.SyncInfo, *schema.ValidatorInfo, error) {
nodeClient, err := client.GetNode()
if err != nil {
return nil, nil, nil, err
@ -23,16 +24,16 @@ func getStatusInfo(client client.Context) (*NodeInfo, *SyncInfo, *ValidatorInfo,
return nil, nil, nil, err
}
return &NodeInfo{
return &schema.NodeInfo{
ID: string(nodeStatus.NodeInfo.ID()),
Network: nodeStatus.NodeInfo.Network,
Moniker: nodeStatus.NodeInfo.Moniker,
}, &SyncInfo{
}, &schema.SyncInfo{
LatestBlockHash: nodeStatus.SyncInfo.LatestBlockHash.String(),
LatestBlockHeight: strconv.FormatInt(nodeStatus.SyncInfo.LatestBlockHeight, 10),
LatestBlockTime: nodeStatus.SyncInfo.LatestBlockTime.String(),
CatchingUp: nodeStatus.SyncInfo.CatchingUp,
}, &ValidatorInfo{
}, &schema.ValidatorInfo{
Address: nodeStatus.ValidatorInfo.Address.String(),
VotingPower: strconv.FormatInt(nodeStatus.ValidatorInfo.VotingPower, 10),
ProposerPriority: nil,
@ -40,7 +41,7 @@ func getStatusInfo(client client.Context) (*NodeInfo, *SyncInfo, *ValidatorInfo,
}
// nolint: all
func getNetInfo(_ client.Context) (string, []*PeerInfo, error) {
func getNetInfo(_ client.Context) (string, []*schema.PeerInfo, error) {
// TODO: Implement
// nodeClient, err := client.GetNode()
@ -68,10 +69,10 @@ func getNetInfo(_ client.Context) (string, []*PeerInfo, error) {
// return strconv.FormatInt(int64(netInfo.NPeers), 10), peersInfo, nil
return strconv.FormatInt(int64(0), 10), []*PeerInfo{}, nil
return strconv.FormatInt(int64(0), 10), []*schema.PeerInfo{}, nil
}
func getValidatorSet(client client.Context) ([]*ValidatorInfo, error) {
func getValidatorSet(client client.Context) ([]*schema.ValidatorInfo, error) {
nodeClient, err := client.GetNode()
if err != nil {
return nil, err
@ -81,10 +82,10 @@ func getValidatorSet(client client.Context) ([]*ValidatorInfo, error) {
return nil, err
}
validatorSet := make([]*ValidatorInfo, len(res.Validators))
validatorSet := make([]*schema.ValidatorInfo, len(res.Validators))
for index, validator := range res.Validators {
proposerPriority := strconv.FormatInt(validator.ProposerPriority, 10)
validatorSet[index] = &ValidatorInfo{
validatorSet[index] = &schema.ValidatorInfo{
Address: validator.Address.String(),
VotingPower: strconv.FormatInt(validator.VotingPower, 10),
ProposerPriority: &proposerPriority,

View File

@ -9,6 +9,7 @@ import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagjson"
"git.vdb.to/cerc-io/laconicd/gql/schema"
auctiontypes "git.vdb.to/cerc-io/laconicd/x/auction"
bondtypes "git.vdb.to/cerc-io/laconicd/x/bond"
registrytypes "git.vdb.to/cerc-io/laconicd/x/registry"
@ -23,8 +24,8 @@ const BondIDAttributeName = "bondId"
// ExpiryTimeAttributeName denotes the record expiry time.
const ExpiryTimeAttributeName = "expiryTime"
func getGQLCoin(coin sdk.Coin) *Coin {
gqlCoin := Coin{
func getGQLCoin(coin sdk.Coin) *schema.Coin {
gqlCoin := schema.Coin{
Type: coin.Denom,
Quantity: coin.Amount.BigInt().String(),
}
@ -32,8 +33,8 @@ func getGQLCoin(coin sdk.Coin) *Coin {
return &gqlCoin
}
func getGQLCoins(coins sdk.Coins) []*Coin {
gqlCoins := make([]*Coin, len(coins))
func getGQLCoins(coins sdk.Coins) []*schema.Coin {
gqlCoins := make([]*schema.Coin, len(coins))
for index, coin := range coins {
gqlCoins[index] = getGQLCoin(coin)
}
@ -41,12 +42,12 @@ func getGQLCoins(coins sdk.Coins) []*Coin {
return gqlCoins
}
func GetGQLNameAuthorityRecord(record *registrytypes.NameAuthority) (*AuthorityRecord, error) {
func GetGQLNameAuthorityRecord(record *registrytypes.NameAuthority) (*schema.AuthorityRecord, error) {
if record == nil {
return nil, nil
}
return &AuthorityRecord{
return &schema.AuthorityRecord{
OwnerAddress: record.OwnerAddress,
OwnerPublicKey: record.OwnerPublicKey,
Height: strconv.FormatUint(record.Height, 10),
@ -56,7 +57,7 @@ func GetGQLNameAuthorityRecord(record *registrytypes.NameAuthority) (*AuthorityR
}, nil
}
func getGQLRecord(ctx context.Context, resolver QueryResolver, record registrytypes.Record) (*Record, error) {
func getGQLRecord(ctx context.Context, resolver schema.QueryResolver, record registrytypes.Record) (*schema.Record, error) {
// Nil record.
if record.Deleted {
return nil, nil
@ -81,22 +82,22 @@ func getGQLRecord(ctx context.Context, resolver QueryResolver, record registryty
return nil, err
}
return &Record{
return &schema.Record{
ID: record.Id,
BondID: record.GetBondId(),
CreateTime: record.GetCreateTime(),
ExpiryTime: record.GetExpiryTime(),
Owners: record.GetOwners(),
Names: record.GetNames(),
Attributes: attributes.(MapValue).Value,
Attributes: attributes.(schema.MapValue).Value,
References: references,
}, nil
}
func resolveIPLDNode(node ipld.Node, links *[]string) (Value, error) {
func resolveIPLDNode(node ipld.Node, links *[]string) (schema.Value, error) {
switch node.Kind() {
case ipld.Kind_Map:
var entries []*Attribute
var entries []*schema.Attribute
for itr := node.MapIterator(); !itr.Done(); {
k, v, err := itr.Next()
if err != nil {
@ -113,14 +114,14 @@ func resolveIPLDNode(node ipld.Node, links *[]string) (Value, error) {
if err != nil {
return nil, err
}
entries = append(entries, &Attribute{
entries = append(entries, &schema.Attribute{
Key: s,
Value: val,
})
}
return MapValue{entries}, nil
return schema.MapValue{entries}, nil
case ipld.Kind_List:
var values []Value
var values []schema.Value
for itr := node.ListIterator(); !itr.Done(); {
_, v, err := itr.Next()
if err != nil {
@ -132,7 +133,7 @@ func resolveIPLDNode(node ipld.Node, links *[]string) (Value, error) {
}
values = append(values, val)
}
return ArrayValue{values}, nil
return schema.ArrayValue{values}, nil
case ipld.Kind_Null:
return nil, nil
case ipld.Kind_Bool:
@ -140,82 +141,82 @@ func resolveIPLDNode(node ipld.Node, links *[]string) (Value, error) {
if err != nil {
return nil, err
}
return BooleanValue{val}, nil
return schema.BooleanValue{val}, nil
case ipld.Kind_Int:
val, err := node.AsInt()
if err != nil {
return nil, err
}
// TODO: handle bigger ints
return IntValue{int(val)}, nil
return schema.IntValue{int(val)}, nil
case ipld.Kind_Float:
val, err := node.AsFloat()
if err != nil {
return nil, err
}
return FloatValue{val}, nil
return schema.FloatValue{val}, nil
case ipld.Kind_String:
val, err := node.AsString()
if err != nil {
return nil, err
}
return StringValue{val}, nil
return schema.StringValue{val}, nil
case ipld.Kind_Bytes:
val, err := node.AsBytes()
if err != nil {
return nil, err
}
return BytesValue{string(val)}, nil
return schema.BytesValue{string(val)}, nil
case ipld.Kind_Link:
val, err := node.AsLink()
if err != nil {
return nil, err
}
*links = append(*links, val.String())
return LinkValue{Link(val.String())}, nil
return schema.LinkValue{schema.Link(val.String())}, nil
default:
return nil, fmt.Errorf("invalid node kind")
}
}
func getGQLNameRecord(record *registrytypes.NameRecord) (*NameRecord, error) {
func getGQLNameRecord(record *registrytypes.NameRecord) (*schema.NameRecord, error) {
if record == nil {
return nil, fmt.Errorf("got nil record")
}
records := make([]*NameRecordEntry, len(record.History))
records := make([]*schema.NameRecordEntry, len(record.History))
for index, entry := range record.History {
records[index] = getNameRecordEntry(entry)
}
return &NameRecord{
return &schema.NameRecord{
Latest: getNameRecordEntry(record.Latest),
History: records,
}, nil
}
func getNameRecordEntry(record *registrytypes.NameRecordEntry) *NameRecordEntry {
return &NameRecordEntry{
func getNameRecordEntry(record *registrytypes.NameRecordEntry) *schema.NameRecordEntry {
return &schema.NameRecordEntry{
ID: record.Id,
Height: strconv.FormatUint(record.Height, 10),
}
}
func getGQLBond(bondObj *bondtypes.Bond) (*Bond, error) {
func getGQLBond(bondObj *bondtypes.Bond) (*schema.Bond, error) {
// Nil record.
if bondObj == nil {
return nil, nil
}
return &Bond{
return &schema.Bond{
ID: bondObj.Id,
Owner: bondObj.Owner,
Balance: getGQLCoins(bondObj.Balance),
}, nil
}
func getAuctionBid(bid *auctiontypes.Bid) *AuctionBid {
return &AuctionBid{
func getAuctionBid(bid *auctiontypes.Bid) *schema.AuctionBid {
return &schema.AuctionBid{
BidderAddress: bid.BidderAddress,
Status: bid.Status,
CommitHash: bid.CommitHash,
@ -227,14 +228,14 @@ func getAuctionBid(bid *auctiontypes.Bid) *AuctionBid {
}
}
func GetGQLAuction(auction *auctiontypes.Auction, bids []*auctiontypes.Bid) (*Auction, error) {
func GetGQLAuction(auction *auctiontypes.Auction, bids []*auctiontypes.Bid) (*schema.Auction, error) {
if auction == nil {
return nil, nil
}
numProviders := int(auction.NumProviders)
gqlAuction := Auction{
gqlAuction := schema.Auction{
ID: auction.Id,
Status: auction.Status,
OwnerAddress: auction.OwnerAddress,
@ -253,7 +254,7 @@ func GetGQLAuction(auction *auctiontypes.Auction, bids []*auctiontypes.Bid) (*Au
FundsReleased: auction.FundsReleased,
}
auctionBids := make([]*AuctionBid, len(bids))
auctionBids := make([]*schema.AuctionBid, len(bids))
for index, entry := range bids {
auctionBids[index] = getAuctionBid(entry)
}
@ -263,7 +264,7 @@ func GetGQLAuction(auction *auctiontypes.Auction, bids []*auctiontypes.Bid) (*Au
return &gqlAuction, nil
}
func toRPCValue(value *ValueInput) *registrytypes.QueryRecordsRequest_ValueInput {
func toRPCValue(value *schema.ValueInput) *registrytypes.QueryRecordsRequest_ValueInput {
var rpcval registrytypes.QueryRecordsRequest_ValueInput
switch {
@ -295,7 +296,7 @@ func toRPCValue(value *ValueInput) *registrytypes.QueryRecordsRequest_ValueInput
return &rpcval
}
func toRPCAttributes(attrs []*KeyValueInput) []*registrytypes.QueryRecordsRequest_KeyValueInput {
func toRPCAttributes(attrs []*schema.KeyValueInput) []*registrytypes.QueryRecordsRequest_KeyValueInput {
kvPairs := []*registrytypes.QueryRecordsRequest_KeyValueInput{}
for _, value := range attrs {
@ -310,7 +311,7 @@ func toRPCAttributes(attrs []*KeyValueInput) []*registrytypes.QueryRecordsReques
return kvPairs
}
func getAuthorityRecord(nameAuthority registrytypes.NameAuthority, auctionQueryClient auctiontypes.QueryClient) (*AuthorityRecord, error) {
func getAuthorityRecord(nameAuthority registrytypes.NameAuthority, auctionQueryClient auctiontypes.QueryClient) (*schema.AuthorityRecord, error) {
gqlNameAuthorityRecord, err := GetGQLNameAuthorityRecord(&nameAuthority)
if err != nil {
return nil, err

View File

@ -4,6 +4,7 @@ deps:
- buf.build/cosmos/cosmos-proto
- buf.build/cosmos/gogo-proto
- buf.build/googleapis/googleapis
- buf.build/protocolbuffers/wellknowntypes
lint:
use:
- DEFAULT

View File

@ -0,0 +1,20 @@
syntax = "proto3";
package cerc.nitro.module.v1;
import "cosmos/app/v1alpha1/module.proto";
// Module is the app config object of the module.
// Learn more: https://docs.cosmos.network/main/building-modules/depinject
message Module {
option (cosmos.app.v1alpha1.module) = {
go_import : "git.vdb.to/cerc-io/laconicd/x/nitro"
};
// authority defines the custom module authority. If not set, defaults to the
// governance module.
string authority = 2;
// TODO LNT address here?
string token_address = 3;
}

View File

@ -0,0 +1,33 @@
syntax = "proto3";
package cerc.nitro.v1;
import "google/api/annotations.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
option go_package = "git.vdb.to/cerc-io/laconicd/x/nitro/types/v1";
// Params defines the parameters of the nitro module.
message Params {
bytes nitro_adjudicator_address = 1;
bytes virtual_payment_app_address = 2;
bytes consensus_app_address = 3;
}
message PaymentChannel {
string channel_id = 1;
// TODO
}
message Fund {
option (gogoproto.equal) = true;
string token_address = 1;
string amount = 2 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
];
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
package cerc.nitro.v1;
option go_package = "git.vdb.to/cerc-io/laconicd/server/nitro";
// P2PMessage wraps a json-encoded Nitro protocols.Message
// TODO: proper proto message?
message P2PMessage {
string content = 1;
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
package cerc.nitro.v1;
import "google/api/annotations.proto";
import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/msg/v1/msg.proto";
option go_package = "git.vdb.to/cerc-io/laconicd/x/nitro/types/v1";
service Query {
}

View File

@ -0,0 +1,139 @@
syntax = "proto3";
package cerc.nitro.v1;
import "google/api/annotations.proto";
import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/msg/v1/msg.proto";
option go_package = "git.vdb.to/cerc-io/laconicd/x/nitro/types/v1";
// 1. user submits OpenChannel tx (from local relay node)
//
// 2. tx is not processed; instead first block proposer to pick up tx triggers Nitro
// CreateLedgerChannel call (or client makes the call and the tx indicates it was made, then
// proposer pushes an event to Nitro?)
//
// 3. objective is cranked among Nitro nodes over comet p2p
//
// 4. once complete, block proposer includes actual tx; client can detect update via Event api
service Msg {
option (cosmos.msg.v1.service) = true;
// TODO: OpenLedgerChannel?
rpc OpenChannel(MsgOpenChannel) returns (MsgOpenChannelResponse) {
option (google.api.http).post = "/cerc/nitro/v1/open_channel";
}
rpc CloseChannel(MsgCloseChannel) returns (MsgCloseChannelResponse) {
option (google.api.http).post = "/cerc/nitro/v1/close_channel";
}
rpc CreatePaymentChannel(MsgCreatePaymentChannel) returns (MsgCreatePaymentChannelResponse) {
option (google.api.http).post = "/cerc/nitro/v1/create_payment_channel";
}
rpc ClosePaymentChannel(MsgClosePaymentChannel) returns (MsgClosePaymentChannelResponse) {
option (google.api.http).post = "/cerc/nitro/v1/close_payment_channel";
}
rpc Pay(MsgPay) returns (MsgPayResponse) {
option (google.api.http).post = "/cerc/nitro/v1/pay";
}
}
message MsgOpenChannel {
option (cosmos.msg.v1.signer) = "signer";
// Nitro address of proposing party
string nitro_address = 1;
// Nitro address of counterparty
string counterparty = 2;
// Simplified outcome just includes the funding amount from payer
// TODO: review use of coins to represent ETH/ERC20
repeated cosmos.base.v1beta1.Coin funds = 3 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// Nitro channel ID
string channel_id = 4;
// Tx signer address is independent of proposer ETH address
string signer = 5;
}
message MsgOpenChannelResponse {}
message MsgCloseChannel {
option (cosmos.msg.v1.signer) = "signer";
// Nitro channel ID to close
string channel_id = 1;
// Whether to close via challenge (dispute resolution) or cooperative closure
bool is_challenge = 2;
// Laconic address of proposing party
string signer = 3;
}
message MsgCloseChannelResponse {}
message MsgCreatePaymentChannel {
option (cosmos.msg.v1.signer) = "signer";
// Nitro address of proposing party
string nitro_address = 1;
// Nitro address of counterparty
string counterparty = 2;
// Nitro addresses of intermediaries
repeated string intermediaries = 3;
// Challenge duration for the channel
uint32 challenge_duration = 4;
// Funding amount for the payment channel
repeated cosmos.base.v1beta1.Coin funds = 5 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// Nitro channel ID
string channel_id = 6;
// Laconic address of proposing party
string signer = 7;
}
message MsgCreatePaymentChannelResponse {}
message MsgClosePaymentChannel {
option (cosmos.msg.v1.signer) = "signer";
// Nitro channel ID to close
string channel_id = 1;
// Laconic address of proposing party
string signer = 2;
}
message MsgClosePaymentChannelResponse {}
message MsgPay {
option (cosmos.msg.v1.signer) = "signer";
// Nitro channel ID through which to send payment
string channel_id = 1;
// Amount to pay
cosmos.base.v1beta1.Coin amount = 2 [
(gogoproto.nullable) = false
];
// Laconic address of the payer
string signer = 3;
}
message MsgPayResponse {}

View File

@ -21,10 +21,8 @@ message Participant {
[ (gogoproto.moretags) =
"json:\"cosmos_address\" yaml:\"cosmos_address\"" ];
// participant's Nitro address
string nitro_address = 2
[ (gogoproto.moretags) =
"json:\"nitro_address\" yaml:\"nitro_address\"" ];
// full public key used to derive participant's Nitro address
bytes public_key = 2;
// participant's role (participant | validator)
string role = 3 [ (gogoproto.moretags) = "json:\"role\" yaml:\"role\"" ];

View File

@ -27,7 +27,7 @@ service Query {
rpc GetParticipantByNitroAddress(QueryGetParticipantByNitroAddressRequest)
returns (QueryGetParticipantByNitroAddressResponse) {
option (google.api.http).get =
"/cerc/onboarding/v1/participants/{nitro_address}";
"/cerc/onboarding/v1/participants-by-nitro/{nitro_address}";
}
}

View File

@ -96,7 +96,7 @@ message Record {
[ (gogoproto.moretags) = "json:\"attributes\" yaml:\"attributes\"" ];
repeated string names = 8
[ (gogoproto.moretags) = "json:\"names\" yaml:\"names\"" ];
string type = 9 [ (gogoproto.moretags) = "json:\"types\" yaml:\"types\"" ];
string types = 9 [ (gogoproto.moretags) = "json:\"types\" yaml:\"types\"" ];
}
// AuthorityEntry defines a registry authority

View File

@ -1,153 +1,105 @@
#!/bin/bash
set -e
if [[ -n "$CERC_SCRIPT_DEBUG" ]]; then
set -x
fi
KEY="alice"
LACONIC_BIN=${LACONIC_BIN:-laconicd}
LACONIC_HOME="${LACONIC_HOME:-$HOME/.laconicd}"
KEYNAME="node_key"
CHAINID=${CHAINID:-"laconic_9000-1"}
MONIKER=${MONIKER:-"localtestnet"}
KEYRING=${KEYRING:-"test"}
DENOM=${DENOM:-"alnt"}
BALANCE=${BALANCE:-"1000000000000000000000000000000"} # 10^32 alnt
STAKING_AMOUNT=${STAKING_AMOUNT:-"1000000000000000"} # 10^15 alnt
MIN_GAS_PRICE=${MIN_GAS_PRICE:-"0.001"}
ALLOCATION_AMOUNT=1000000000000000000000000000000 # 10^30 alnt | 10^12 lnt
LOGLEVEL=${LOGLEVEL:-"info"}
P2P_EXTERNAL_PORT=${P2P_EXTERNAL_PORT:-26656}
input_genesis_file=${GENESIS_FILE}
import_private_key=${PRIVATE_KEY}
if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data" ]; then
# validate dependencies are installed
command -v jq > /dev/null 2>&1 || {
echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"
exit 1
}
laconicd="$LACONIC_BIN --home=$LACONIC_HOME --log_level=$LOGLEVEL"
# remove existing daemon and client
rm -rf $HOME/.laconicd/*
set -e
if [ -n "`which make`" ]; then
make install
if [ "$1" == "clean" ]; then
echo "Clearing existing data directory in $LACONIC_HOME"
rm -rf "$LACONIC_HOME"
else
echo "Using existing database at $LACONIC_HOME. To replace, run '$(basename $0) clean'"
fi
# Create a new home dir if needed
if [[ ! -d "$LACONIC_HOME/data/application.db" ]]; then
# Import the node key if passed, or generate one
if [[ -n "$import_private_key" ]]; then
$laconicd keys import-hex $KEYNAME 'b0b0000000000000000000000000000000000000000000000000000000000000'
else
printf "y\n" | $laconicd keys add $KEYNAME --keyring-backend $KEYRING --no-backup
fi
laconicd config set client chain-id $CHAINID
laconicd config set client keyring-backend $KEYRING
# Set moniker and chain-id (Moniker can be anything, chain-id must be an integer)
$laconicd init $MONIKER --chain-id $CHAINID --default-denom $DENOM
else
KEYNAME=$($laconicd keys list --list-names --keyring-backend $KEYRING | head -1)
fi
# if $KEY exists it should be deleted
laconicd keys add $KEY --keyring-backend $KEYRING
# Set moniker and chain-id
laconicd init $MONIKER --chain-id $CHAINID --default-denom $DENOM
if [[ ! -d "$db_path" ]]; then
$laconicd config set client chain-id $CHAINID
$laconicd config set client keyring-backend $KEYRING
if [[ -f ${input_genesis_file} ]]; then
# Use provided genesis config
cp $input_genesis_file $HOME/.laconicd/config/genesis.json
cp $input_genesis_file $LACONIC_HOME/config/genesis.json
fi
update_genesis() {
jq "$1" $HOME/.laconicd/config/genesis.json > $HOME/.laconicd/config/tmp_genesis.json &&
mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
$laconicd genesis validate
update_comet_config() { # comet config is not supported by confix cli
local file="$LACONIC_HOME/config/config.toml"
cp -f "$file" "${file}.bak" && tomlq --toml-output ".$1 = $2" "${file}.bak" > "$file"
}
if [[ "$TEST_REGISTRY_EXPIRY" == "true" ]]; then
echo "Setting timers for expiry tests."
# disable empty blocks
update_comet_config consensus.create_empty_blocks 'false'
update_genesis '.app_state["registry"]["params"]["record_rent_duration"]="60s"'
update_genesis '.app_state["registry"]["params"]["authority_grace_period"]="60s"'
update_genesis '.app_state["registry"]["params"]["authority_rent_duration"]="60s"'
fi
# Allow requests from any origin
update_comet_config rpc.cors_allowed_origins '["*"]'
if [[ "$TEST_AUCTION_ENABLED" == "true" ]]; then
echo "Enabling auction and setting timers."
update_genesis '.app_state["registry"]["params"]["authority_auction_enabled"]=true'
update_genesis '.app_state["registry"]["params"]["authority_rent_duration"]="60s"'
update_genesis '.app_state["registry"]["params"]["authority_grace_period"]="300s"'
update_genesis '.app_state["registry"]["params"]["authority_auction_commits_duration"]="60s"'
update_genesis '.app_state["registry"]["params"]["authority_auction_reveals_duration"]="60s"'
fi
if [[ "$ONBOARDING_ENABLED" == "true" ]]; then
echo "Enabling onboarding."
update_genesis '.app_state["onboarding"]["params"]["onboarding_enabled"]=true'
fi
if [[ "$AUTHORITY_AUCTION_ENABLED" == "true" ]]; then
echo "Enabling authority auctions."
update_genesis '.app_state["registry"]["params"]["authority_auction_enabled"]=true'
fi
if [[ -n $AUTHORITY_AUCTION_COMMITS_DURATION ]]; then
echo "Setting authority_auction_commits_duration to $AUTHORITY_AUCTION_COMMITS_DURATION seconds."
update_genesis ".app_state[\"registry\"][\"params\"][\"authority_auction_commits_duration\"]=\"${AUTHORITY_AUCTION_COMMITS_DURATION}s\""
fi
if [[ -n $AUTHORITY_AUCTION_REVEALS_DURATION ]]; then
echo "Setting authority_auction_reveals_duration to $AUTHORITY_AUCTION_REVEALS_DURATION seconds."
update_genesis ".app_state[\"registry\"][\"params\"][\"authority_auction_reveals_duration\"]=\"${AUTHORITY_AUCTION_REVEALS_DURATION}s\""
fi
if [[ -n $AUTHORITY_GRACE_PERIOD ]]; then
echo "Setting authority_grace_period to $AUTHORITY_GRACE_PERIOD seconds."
update_genesis ".app_state[\"registry\"][\"params\"][\"authority_grace_period\"]=\"${AUTHORITY_GRACE_PERIOD}s\""
fi
# increase block time (?)
update_genesis '.consensus["params"]["block"]["time_iota_ms"]="1000"'
# Set gas limit in genesis
update_genesis '.consensus["params"]["block"]["max_gas"]="10000000"'
# Set distribution community tax to 1 for disabling staking rewards
update_genesis '.app_state["distribution"]["params"]["community_tax"]="1.000000000000000000"'
echo "Setting high threshold for accepting governance proposal"
update_genesis '.app_state["gov"]["params"]["quorum"]="1.000000000000000000"'
# Set expedited threshold to 100%
update_genesis '.app_state["gov"]["params"]["expedited_threshold"]="1.000000000000000000"'
# Set normal threshold to 99% since it needs to be lesser than expedited threshold
update_genesis '.app_state["gov"]["params"]["threshold"]="0.990000000000000000"'
# disable produce empty block
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' 's/create_empty_blocks = true/create_empty_blocks = false/g' $HOME/.laconicd/config/config.toml
else
sed -i 's/create_empty_blocks = true/create_empty_blocks = false/g' $HOME/.laconicd/config/config.toml
fi
# Run this to allow requests from any origin
sed -i 's/cors_allowed_origins.*$/cors_allowed_origins = ["*"]/' $HOME/.laconicd/config/config.toml
# Set persistent peers
update_comet_config p2p.seeds "\"$P2P_PEERS\""
update_comet_config p2p.persistent_peers "\"$P2P_PEERS\""
update_comet_config p2p.external_address "\"tcp://127.0.0.1:${P2P_EXTERNAL_PORT}\""
update_comet_config p2p.addr_book_strict false
# Enable telemetry (prometheus metrics: http://localhost:1317/metrics?format=prometheus)
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' 's/enabled = false/enabled = true/g' $HOME/.laconicd/config/app.toml
sed -i '' 's/prometheus-retention-time = 0/prometheus-retention-time = 60/g' $HOME/.laconicd/config/app.toml
sed -i '' 's/prometheus = false/prometheus = true/g' $HOME/.laconicd/config/config.toml
else
sed -i 's/enabled = false/enabled = true/g' $HOME/.laconicd/config/app.toml
sed -i 's/prometheus-retention-time = 0/prometheus-retention-time = 60/g' $HOME/.laconicd/config/app.toml
sed -i 's/prometheus = false/prometheus = true/g' $HOME/.laconicd/config/config.toml
fi
$laconicd config set app telemetry.enable true
$laconicd config set app telemetry.prometheus-retention-time 60
update_comet_config instrumentation.prometheus true
# Allocate genesis accounts (cosmos formatted addresses)
laconicd genesis add-genesis-account $KEY ${BALANCE}${DENOM} --keyring-backend $KEYRING
# Enable validator distsig
$laconicd config set app distsig.enable true
$laconicd config set app distsig.longterm-key "$KEYNAME"
# Sign genesis transaction
laconicd genesis gentx $KEY $STAKING_AMOUNT$DENOM --keyring-backend $KEYRING --chain-id $CHAINID
# Collect genesis tx
laconicd genesis collect-gentxs
# Run this to ensure everything worked and that the genesis file is setup correctly
laconicd genesis validate
# Nitro config, WIP
# $laconicd config set app nitro.use-distsig true # TODO
$laconicd config set app nitro.eth-key "$KEYNAME"
$laconicd config set app nitro.eth-url "$NITRO_ETH_URL"
$laconicd config set app nitro.eth-start-block "$NITRO_ETH_START_BLOCK"
$laconicd config set app nitro.eth-na-address "'$NITRO_ADJUDICATOR_ADDRESS'"
$laconicd config set app nitro.eth-vpa-address "'$VIRTUAL_PAYMENT_APP_ADDRESS'"
$laconicd config set app nitro.eth-ca-address "'$CONSENSUS_APP_ADDRESS'"
else
echo "Using existing database at $HOME/.laconicd. To replace, run '`basename $0` clean'"
echo "Using existing database at $LACONIC_HOME."
fi
# Start the node (remove the --pruning=nothing flag if historical queries are not needed)
laconicd start \
--pruning=nothing \
--log_level $LOGLEVEL \
--minimum-gas-prices=$MIN_GAS_PRICE$DENOM \
--api.enable \
# TODO new pruning config
$laconicd start \
--server.minimum-gas-prices="1$DENOM" \
--rpc.laddr="tcp://0.0.0.0:26657" \
--gql-server --gql-playground
--grpc.address="127.0.0.1:9091" \
--gql.enable --gql.playground

120
server/components.go Normal file
View File

@ -0,0 +1,120 @@
package server
import (
"context"
"errors"
"fmt"
"cosmossdk.io/log"
"github.com/cometbft/cometbft/node"
"github.com/cometbft/cometbft/p2p"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
)
// HasContextKey is implemented by servers that have a dedicated key under which they should be
// associated with a context.
type HasContextKey interface {
ContextKey() string
}
type HasP2PReactors interface {
P2PReactors() map[string]p2p.Reactor
}
// maps commands to server components they require
var cmdRequirements = map[*cobra.Command][]ServerComponent{}
// SetRequiredComponents sets required server components for a command
func SetRequiredComponents(cmd *cobra.Command, components ...ServerComponent) {
for _, c := range components {
cmdRequirements[cmd] = append(cmdRequirements[cmd], c)
if startmod, ok := c.(HasStartFlags); ok {
cmd.Flags().AddFlagSet(startmod.StartCmdFlags())
}
}
}
func RequiresComponent(cmd *cobra.Command, c string) bool {
components, ok := cmdRequirements[cmd]
if !ok {
return false
}
for _, mod := range components {
if mod.Name() == c {
return true
}
}
return false
}
type ComponentsCreator func(ConfigMap, client.Context, log.Logger, func(string) bool) ([]ServerComponent, error)
type ServerAux struct {
components []ServerComponent
}
// AddComponents adds hooks to create, start and stop the components returned from the passed
// constructor, and ensures they are available on the command context.
// It returns a node.Option to additionally configure the CometBFT node.
func (s *ServerAux) AddComponents(cmd *cobra.Command, initComponents ComponentsCreator) {
needsComponent := func(name string) bool {
return RequiresComponent(cmd, name)
}
// manage component lifecycle
cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
serverCtx := server.GetServerContextFromCmd(cmd)
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
s.components, err = initComponents(
serverCtx.Viper.AllSettings(), clientCtx, serverCtx.Logger, needsComponent,
)
// add available context keys
for _, mod := range s.components {
if ckeymod, ok := mod.(HasContextKey); ok {
cmdCtx := cmd.Context()
if cmdCtx == nil {
cmdCtx = context.Background()
}
cmd.SetContext(context.WithValue(cmdCtx, ckeymod.ContextKey(), mod))
}
}
for _, mod := range s.components {
if err := mod.Start(cmd.Context()); err != nil {
return fmt.Errorf("failed to start server %s: %w", mod.Name(), err)
}
}
return nil
}
startFn := cmd.RunE
cmd.RunE = func(cmd *cobra.Command, args []string) error {
// if we do this in PostRunE, it won't be called if RunE errors
defer func() {
var err error
for _, mod := range s.components {
err = errors.Join(err, mod.Stop(cmd.Context()))
}
if err != nil {
cmd.PrintErrln("failed to stop servers:", err)
}
}()
return startFn(cmd, args)
}
}
// AddReactors adds p2p Reactors for all configured components to a CometBFT node.
// When passed to StartCmdOptions this will be run in start(), after components are initialized in PreRun.
func (s *ServerAux) AddReactors(cmt *node.Node) {
for _, mod := range s.components {
if reactormod, ok := mod.(HasP2PReactors); ok {
opt := node.CustomReactors(reactormod.P2PReactors())
opt(cmt)
}
}
}

65
server/config.go Normal file
View File

@ -0,0 +1,65 @@
package server
// The utilities in this file are copied from cosmos-sdk server/v2
import (
"fmt"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
)
type ConfigMap map[string]any
// ReadConfig returns a viper instance of the config file
func ReadConfig(configPath string) (*viper.Viper, error) {
v := viper.New()
v.SetConfigType("toml")
v.SetConfigName("config")
v.AddConfigPath(configPath)
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read config: %s: %w", configPath, err)
}
v.SetConfigName("app")
if err := v.MergeInConfig(); err != nil {
return nil, fmt.Errorf("failed to merge configuration: %w", err)
}
v.WatchConfig()
return v, nil
}
// UnmarshalSubConfig unmarshals the given (sub) config from the main config (given as a map) into the target.
// If subName is empty, the main config is unmarshaled into the target.
func UnmarshalSubConfig(cfg map[string]any, subName string, target any) error {
var sub any
if subName != "" {
if val, ok := cfg[subName]; ok {
sub = val
}
} else {
sub = cfg
}
// Create a new decoder with custom decoding options
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
),
Result: target,
WeaklyTypedInput: true,
})
if err != nil {
return fmt.Errorf("failed to create decoder: %w", err)
}
// Decode the sub-configuration
if err := decoder.Decode(sub); err != nil {
return fmt.Errorf("failed to decode sub-configuration: %w", err)
}
return nil
}

29
server/distsig/config.go Normal file
View File

@ -0,0 +1,29 @@
package distsig
import (
"fmt"
"git.vdb.to/cerc-io/laconicd/server"
)
type Config struct {
Enable bool `mapstructure:"enable" toml:"enable" comment:"Enable distributed Schnorr signatures"`
LongtermKey string `mapstructure:"longterm-key" toml:"longterm-key" comment:"The long-term key used in distributed signatures"`
// TODO: set this in genesis
ThresholdRatio float64 `mapstructure:"threshold-ratio" toml:"threshold-ratio" comment:"The ratio of signers required to sign a message"`
}
func DefaultConfig() *Config {
return &Config{
Enable: false,
ThresholdRatio: 4. / 7,
}
}
func UnmarshalConfig(cfg map[string]any) (*Config, error) {
config := DefaultConfig()
if err := server.UnmarshalSubConfig(cfg, componentName, config); err != nil {
return nil, fmt.Errorf("failed to unmarshal %T: %w", config, err)
}
return config, nil
}

View File

@ -0,0 +1,175 @@
package distsig
import (
"errors"
"math/big"
"testing"
"cosmossdk.io/log"
"git.vdb.to/cerc-io/chain-signatures/ethschnorr"
sdktestutil "github.com/cosmos/cosmos-sdk/testutil"
"github.com/stretchr/testify/require"
"git.vdb.to/cerc-io/laconicd/testutil"
)
func NewTestManager(t *testing.T) (*Manager, Point) {
logger := log.NewTestLogger(t)
kr := testutil.NewKeyring()
accounts := sdktestutil.CreateKeyringAccounts(t, kr, 1)
cfg := DefaultConfig()
cfg.LongtermKey = accounts[0].Name
dsm, err := New(logger, cfg, kr)
require.NoError(t, err)
longterm, err := kr.Key(cfg.LongtermKey)
require.NoError(t, err)
longtermPoint, err := KeyRecordToPoint(longterm)
require.NoError(t, err)
return dsm, longtermPoint
}
type round struct {
name string
verify func(*Manager) error
tweak func([]PeerMessages)
}
var (
dkgRounds = []round{
{
name: "deal",
},
{
name: "response",
verify: func(m *Manager) error {
if !m.currentDkg().Certified() {
return errors.New("not certified")
}
return nil
},
},
{
name: "commit",
verify: func(m *Manager) error {
if !m.currentDkg().Finished() {
return errors.New("run not finished")
}
if m.currentDkg().share == nil {
return errors.New("dist key share is absent")
}
return nil
},
},
}
dssRounds = []round{
{
name: "sign",
verify: func(m *Manager) error {
dss := m.sigs[m.completeSigs[0]]
if dss.sig == nil {
return errors.New("signature is nil")
}
dkg, err := m.getDkg(dss.dkgID)
if err != nil {
return err
}
return ethschnorr.Verify(dkg.share.Public(), dss.Message(), dss.sig)
},
},
}
)
func runRound(t *testing.T, r round, members []*Manager) {
buf := make([]PeerMessages, len(members))
for i := range members {
buf[i] = members[i].FlushMessages()
}
if r.tweak != nil {
r.tweak(buf)
}
for i := range members {
for j := range members {
if i == j {
continue
}
require.NoError(t, members[i].ProcessMessages(&buf[j]),
"round=%s, i=%d, j=%d", r.name, i, j)
}
}
if r.verify != nil {
for i, m := range members {
require.NoError(t, r.verify(m), "round=%s, i=%d", r.name, i)
}
}
}
func TestDkgBasic(t *testing.T) {
numMembers := 2
blockHeight := int64(1)
members := make([]*Manager, numMembers)
pubkeys := make([]Point, numMembers)
for i := range members {
members[i], pubkeys[i] = NewTestManager(t)
}
t.Logf("DKG with %d members", numMembers)
for i, m := range members {
require.NoError(t, m.StartDKG(blockHeight, pubkeys), "i=%d", i)
}
for _, r := range dkgRounds {
runRound(t, r, members)
}
require.False(t, members[0].NeedDKG(pubkeys))
require.Error(t, members[0].StartDKG(blockHeight, pubkeys))
// add participants and refresh DKG
for len(members) < 7 {
blockHeight++
m, pub := NewTestManager(t)
members = append(members, m)
pubkeys = append(pubkeys, pub)
t.Logf("DKG with %d members", len(members))
for i, m := range members {
require.True(t, m.NeedDKG(pubkeys), "i=%d", i)
require.NoError(t, m.StartDKG(blockHeight, pubkeys), "i=%d", i)
}
for _, r := range dkgRounds {
runRound(t, r, members)
}
}
}
func TestSignatureBasic(t *testing.T) {
numMembers := 7
members := make([]*Manager, numMembers)
pubkeys := make([]Point, numMembers)
for i := range members {
members[i], pubkeys[i] = NewTestManager(t)
}
for _, m := range members {
require.NoError(t, m.StartDKG(1, pubkeys))
}
for _, r := range dkgRounds {
runRound(t, r, members)
}
msg := big.NewInt(42)
for _, m := range members {
require.NoError(t, m.StartSignature(m.currentDkgID, msg))
}
runRound(t, dssRounds[0], members)
require.Error(t, members[0].StartSignature(0, msg))
require.NoError(t, members[0].StartDKG(2, pubkeys[:3]))
require.Error(t, members[0].StartSignature(2, msg))
}

19
server/distsig/flags.go Normal file
View File

@ -0,0 +1,19 @@
package distsig
import (
"fmt"
"github.com/spf13/pflag"
)
func prefix(f string) string {
return fmt.Sprintf("%s.%s", componentName, f)
}
var (
FlagLongtermKey = prefix("longterm-key")
)
func AddNitroFlags(flags *pflag.FlagSet) {
flags.String(FlagLongtermKey, "", "The long-term key used in distributed signatures")
}

224
server/distsig/keygen.go Normal file
View File

@ -0,0 +1,224 @@
package distsig
import (
"fmt"
dkg "go.dedis.ch/kyber/v3/share/dkg/rabin"
)
// DkgMessages collects local DKG messages that are pending broadcast.
type DkgMessages struct {
runID DkgRunID
messages []dkgMessage
}
// DKG steps (actions) that are executed by the DKG protocol
type dkgStep int
const (
dkg_deal dkgStep = iota
dkg_certify
dkg_commit
dkg_finish
dkg_done
)
type dkgRun struct {
*dkg.DistKeyGenerator
// runID RunID
msgBuffer []dkgMessage
step dkgStep
share *dkg.DistKeyShare
}
type dkgMessage interface {
send(*dkgRun) (dkgMessage, error)
from() uint32
target() uint32
}
// prepareMessages produces DKG state transitions that don't rely on peer inputs, i.e. the deal or
// commit steps. This is called during ExtendVote by all validators.
func (d *dkgRun) prepareMessages() error {
out, err := d.handleMessage(nil)
if err != nil {
return err
}
if out != nil {
d.msgBuffer = append(d.msgBuffer, out...)
}
return nil
}
func (d *dkgRun) flushMessages() []dkgMessage {
buf := d.msgBuffer
d.msgBuffer = nil
return buf
}
// processMessages processes incoming DKG messages and produces outgoing messages. This is called
// during VerifyVoteExtension by all validators (TODO: verify).
func (d *dkgRun) processMessages(in []dkgMessage) error {
for _, msg := range in {
out, err := d.handleMessage(msg)
if err != nil {
return err
}
if out != nil {
d.msgBuffer = append(d.msgBuffer, out...)
}
}
return nil
}
func (d *dkgRun) handleMessage(in dkgMessage) ([]dkgMessage, error) {
// fmt.Printf("[%d, %7s] handle: %T", d.index, d.step, in)
// if in != nil {
// fmt.Printf(" (from %d to %+v)", in.from(), in.target())
// }
// fmt.Println()
// nil input means we try to execute a step requiring no peer input
if in == nil {
var out []dkgMessage
switch d.step {
case dkg_deal:
deals, err := d.Deals()
if err != nil {
return nil, err
}
for index, deal := range deals {
out = append(out, msgDeal{index: uint32(index), msg: deal})
}
case dkg_commit:
sc, err := d.SecretCommits()
if err != nil {
return nil, err
}
out = []dkgMessage{msgSecretCommits{msg: sc}}
case dkg_done:
dks, err := d.DistKeyShare()
if err != nil {
return nil, err
}
d.share = dks
default:
return nil, fmt.Errorf("unexpected stage: %v", d.step)
}
d.step = nextStep(d.step)
return out, nil
}
var out []dkgMessage
if msg, err := in.send(d); err != nil {
return nil, err
} else if msg != nil {
out = []dkgMessage{msg}
}
if (d.step == dkg_certify && d.Certified()) ||
(d.step == dkg_finish && d.Finished()) {
d.step = nextStep(d.step)
msgs, err := d.handleMessage(nil)
if err != nil {
return nil, err
}
out = append(out, msgs...)
}
return out, nil
}
func nextStep(step dkgStep) dkgStep {
if step == dkg_done {
return dkg_done
}
return step + 1
}
type (
msgDeal struct {
msg *dkg.Deal
index uint32 // recipient index
}
msgResponse struct{ msg *dkg.Response }
msgJustification struct{ msg *dkg.Justification }
msgSecretCommits struct{ msg *dkg.SecretCommits }
msgComplaintCommits struct{ msg *dkg.ComplaintCommits }
msgReconstructCommits struct{ msg *dkg.ReconstructCommits }
)
func (m msgDeal) send(d *dkgRun) (dkgMessage, error) {
if m.index != uint32(d.Index()) {
return nil, nil
}
out, err := d.ProcessDeal(m.msg)
if err != nil {
return nil, err
}
return msgResponse{out}, nil
}
func (m msgResponse) send(d *dkgRun) (dkgMessage, error) {
if out, err := d.ProcessResponse(m.msg); err != nil {
return nil, err
} else if out != nil {
return msgJustification{out}, nil
}
return nil, nil
}
func (m msgJustification) send(d *dkgRun) (dkgMessage, error) {
return nil, d.ProcessJustification(m.msg)
}
func (m msgSecretCommits) send(d *dkgRun) (dkgMessage, error) {
if out, err := d.ProcessSecretCommits(m.msg); err != nil {
return nil, err
} else if out != nil {
return msgComplaintCommits{out}, nil
}
return nil, nil
}
func (m msgComplaintCommits) send(d *dkgRun) (dkgMessage, error) {
out, err := d.ProcessComplaintCommits(m.msg)
if err != nil {
return nil, err
}
return msgReconstructCommits{out}, nil
}
func (m msgReconstructCommits) send(d *dkgRun) (dkgMessage, error) {
return nil, d.ProcessReconstructCommits(m.msg)
}
func (d dkgStep) String() string {
switch d {
case dkg_deal:
return "deal"
case dkg_certify:
return "certify"
case dkg_commit:
return "commit"
case dkg_finish:
return "finish"
case dkg_done:
return "done"
default:
return "unknown"
}
}
// for debugging
func (m msgDeal) from() uint32 { return m.msg.Index }
func (m msgResponse) from() uint32 { return m.msg.Response.Index }
func (m msgJustification) from() uint32 { return m.msg.Justification.Index }
func (m msgSecretCommits) from() uint32 { return m.msg.Index }
func (m msgComplaintCommits) from() uint32 { return m.msg.Index }
func (m msgReconstructCommits) from() uint32 { return m.msg.Index }
func (m msgDeal) target() uint32 { return m.index }
func (m msgResponse) target() uint32 { return m.msg.Index }
func (m msgJustification) target() uint32 { return m.msg.Index }
func (m msgSecretCommits) target() uint32 { return 0 }
func (m msgComplaintCommits) target() uint32 { return m.msg.DealerIndex }
func (m msgReconstructCommits) target() uint32 { return m.msg.DealerIndex }

268
server/distsig/manager.go Normal file
View File

@ -0,0 +1,268 @@
package distsig
import (
"context"
"fmt"
"math"
"math/big"
cmtcrypto "github.com/cometbft/cometbft/crypto"
"github.com/ethereum/go-ethereum/common"
dkg "go.dedis.ch/kyber/v3/share/dkg/rabin"
"cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
clientdss "git.vdb.to/cerc-io/chain-signatures/ethdss"
"git.vdb.to/cerc-io/chain-signatures/ethschnorr"
"git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/utils"
)
type (
DealMap = map[int]*dkg.Deal
DkgRunID int64
SigRunID string
)
const (
componentName = "distsig"
)
type Manager struct {
config *Config
logger log.Logger
keyring keyring.Keyring
longtermKey Scalar // only initialized after Start()
longtermPubKey cmtcrypto.PubKey
dkgs map[DkgRunID]*dkgRun
currentDkgID DkgRunID // zero indicates no current run
sigs map[SigRunID]*sigRun
completeSigs []SigRunID
}
type PeerMessages struct {
dkg *DkgMessages
dss []SigMessages
}
func New(logger log.Logger, globalConfig server.ConfigMap, kr keyring.Keyring) (*Manager, error) {
config, err := UnmarshalConfig(globalConfig)
if err != nil {
return nil, err
}
m := &Manager{
config: config,
keyring: kr,
dkgs: make(map[DkgRunID]*dkgRun),
sigs: make(map[SigRunID]*sigRun),
}
m.logger = logger.With(log.ModuleKey, m.Name())
return m, nil
}
func (*Manager) Name() string { return componentName }
func (m *Manager) Start(ctx context.Context) error {
if !m.config.Enable {
m.logger.Info(fmt.Sprintf("%s server is disabled via config", m.Name()))
return nil
}
if m.config.LongtermKey == "" {
return fmt.Errorf("missing longterm key")
}
longtermPrivKey, err := utils.ExtractPrivateKeyByUid(m.keyring, m.config.LongtermKey)
if err != nil {
return fmt.Errorf("failed to extract longterm key: %w", err)
}
m.longtermKey = suite.Scalar().SetBytes(longtermPrivKey.Bytes())
m.longtermPubKey = longtermPrivKey.PubKey()
return nil
}
func (*Manager) Stop(context.Context) error { return nil }
func (m *Manager) Config() any {
if m.config == nil {
return DefaultConfig()
}
return m.config
}
func (m *Manager) getDkg(runid DkgRunID) (*dkgRun, error) {
if runid == 0 {
// runid = m.currentDkg
return nil, fmt.Errorf("invalid run ID")
}
if run, ok := m.dkgs[runid]; ok {
return run, nil
}
return nil, fmt.Errorf("DKG not initialized for run: %v", runid)
}
func (m *Manager) currentDkg() *dkgRun {
run, err := m.getDkg(m.currentDkgID)
if err != nil {
panic(err)
}
return run
}
func (m *Manager) LongtermPublicKey() cmtcrypto.PubKey {
return m.longtermPubKey
}
func (m *Manager) LongtermEthAddress() common.Address {
pubkey, err := SuitePublicKeyFromBytes(m.LongtermPublicKey().Bytes())
if err != nil {
panic(fmt.Errorf("failed to parse longterm pubkey: %w", err))
}
return pubkey.Address()
}
func thresholdForRatio(m int, tr float64) int {
return int(math.Ceil(float64(m) * tr))
}
func (m *Manager) initDKG(members []Point) (*dkgRun, error) {
t := thresholdForRatio(len(members), m.config.ThresholdRatio)
keygen, err := dkg.NewDistKeyGenerator(suite.(dkg.Suite), m.longtermKey, members, t)
if err != nil {
return nil, err
}
return &dkgRun{DistKeyGenerator: keygen}, nil
}
// NeedDKG returns true if DKG needs to be run for the given participants.
func (m *Manager) NeedDKG(pubkeys []Point) bool {
return m.currentDkgID == 0 || !equalPoints(m.currentDkg().Participants(), pubkeys)
}
// StartDKG begins a new DKG run including the given participants.
func (m *Manager) StartDKG(block int64, pubkeys []Point) error {
// TODO: generate runid from block height and hash of pubkeys?
runid := DkgRunID(block)
if _, ok := m.dkgs[runid]; ok {
return fmt.Errorf("DKG run %v already exists", runid)
}
if len(pubkeys) < 2 {
m.currentDkgID = 0
m.logger.Debug("Too few participants for distributed signature")
return nil
}
run, err := m.initDKG(pubkeys)
if err != nil {
return err
}
m.dkgs[runid] = run
m.currentDkgID = runid
return run.prepareMessages()
}
func (m *Manager) initDSS(dkgid DkgRunID, msg *big.Int) (*sigRun, error) {
run, err := m.getDkg(dkgid)
if err != nil {
return nil, err
}
if !run.Finished() {
return nil, fmt.Errorf("DKG run has not finished: %v", dkgid)
}
random, err := run.DistKeyShare()
if err != nil {
return nil, err
}
dss, err := clientdss.NewDSS(clientdss.DSSArgs{
Secret: m.longtermKey,
Participants: run.Participants(),
Long: run.share,
Random: random,
Msg: msg,
T: run.Threshold(),
// Qualified: run.QUAL(),
})
if err != nil {
return nil, err
}
return &sigRun{DSS: dss, dkgID: dkgid}, nil
}
// StartSignature begins a DSS run for the given message and DKG state.
func (m *Manager) StartSignature(msg *big.Int) error {
if m.currentDkgID == 0 {
return fmt.Errorf("no distributed key prepared")
}
run, err := m.initDSS(m.currentDkgID, msg)
if err != nil {
return err
}
sigid := run.SigRunID()
m.sigs[sigid] = run
return run.prepareMessages()
}
func (m *Manager) CompletedSignatures() map[SigRunID]ethschnorr.Signature {
var ret map[SigRunID]ethschnorr.Signature
for _, id := range m.completeSigs {
run, ok := m.sigs[id]
if !ok {
panic(fmt.Errorf("DSS run not found: %v", id))
}
if run.sig == nil {
panic(fmt.Errorf("DSS signature not completed: %v", id))
}
// Deterministically pick a participant responsible for submitting the signature
session := new(big.Int).SetBytes(run.SessionID()[:8])
submitterIdx := session.Uint64() % uint64(len(run.Participants()))
if run.Index() == int(submitterIdx) {
ret[id] = run.sig
}
}
return ret
}
// FlushMessages flushes the message buffers for any active DKG and DSS runs.
func (m *Manager) FlushMessages() PeerMessages {
var ret PeerMessages
if buf := m.currentDkg().flushMessages(); len(buf) != 0 {
ret.dkg = &DkgMessages{runID: m.currentDkgID, messages: buf}
}
for id, run := range m.sigs {
if buf := run.flushMessages(); buf != nil {
ret.dss = append(ret.dss, SigMessages{id, buf})
}
}
return ret
}
// ProcessMessages processes DKG and DSS peer messages.
func (m *Manager) ProcessMessages(dm *PeerMessages) error {
if dkg := dm.dkg; dkg != nil {
run, err := m.getDkg(dkg.runID)
if err != nil {
return err
}
if err := run.processMessages(dkg.messages); err != nil {
return err
}
}
for _, dss := range dm.dss {
run, ok := m.sigs[dss.runID]
if !ok {
return fmt.Errorf("DSS not initialized for run: %v", dss.runID)
}
if err := run.processMessage(dss.messages); err != nil {
return err
}
if run.sig != nil {
m.completeSigs = append(m.completeSigs, dss.runID)
}
}
return nil
}
func (pm PeerMessages) Empty() bool {
return pm.dkg == nil && len(pm.dss) == 0
}

View File

@ -0,0 +1,61 @@
package distsig
import (
"fmt"
clientdss "git.vdb.to/cerc-io/chain-signatures/ethdss"
"git.vdb.to/cerc-io/chain-signatures/ethschnorr"
)
type SigMessages struct {
runID SigRunID
messages sigMessage
}
type sigStep int
const (
sig_partial sigStep = iota
sig_complete
)
type sigRun struct {
*clientdss.DSS
msgBuffer sigMessage
dkgID DkgRunID
step sigStep
sig ethschnorr.Signature
}
type sigMessage = *clientdss.PartialSig
func (d *sigRun) SigRunID() SigRunID {
return SigRunID(fmt.Sprintf("%020d-%x", d.dkgID, d.SessionID()[:8]))
}
func (d *sigRun) prepareMessages() error {
var err error
d.msgBuffer, err = d.PartialSig()
return err
}
func (d *sigRun) flushMessages() sigMessage {
buf := d.msgBuffer
d.msgBuffer = nil
return buf
}
func (d *sigRun) processMessage(in sigMessage) error {
err := d.ProcessPartialSig(in)
if err != nil {
return err
}
// return d.EnoughPartialSig(), nil
if d.EnoughPartialSig() {
d.sig, err = d.Signature()
return err
}
return nil
}

39
server/distsig/suite.go Normal file
View File

@ -0,0 +1,39 @@
package distsig
import (
"fmt"
"git.vdb.to/cerc-io/chain-signatures/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/suites"
)
type (
Scalar = kyber.Scalar
Point = kyber.Point
PublicKey = secp256k1.PublicKey
)
var (
suite suites.Suite = secp256k1.NewBlakeKeccackSecp256k1()
NewScalar = suite.Scalar
NewPoint = suite.Point
// Note: the compressed encoding of pubkeys used by Cosmos SDK and our library (based on chainlink)
// are the same. See gitlab.com/yawning/secp256k1-voi/secec
SuitePublicKeyFromBytes = secp256k1.NewPublicKeyFromBytes
)
func KeyRecordToPoint(longterm *keyring.Record) (kyber.Point, error) {
pubkey, err := longterm.GetPubKey()
if err != nil {
return nil, fmt.Errorf("failed to access public key: %w", err)
}
suitePubkey, err := SuitePublicKeyFromBytes(pubkey.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to decode public key: %w", err)
}
return suitePubkey.Point()
}

13
server/distsig/utils.go Normal file
View File

@ -0,0 +1,13 @@
package distsig
func equalPoints(a, b []Point) bool {
if len(a) != len(b) {
return false
}
for i, p := range a {
if !p.Equal(b[i]) {
return false
}
}
return true
}

36
server/gas.go Normal file
View File

@ -0,0 +1,36 @@
package server
import (
"context"
"cosmossdk.io/core/gas"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var _ gas.Service = gasService{}
// Placeholder gas service, in anticipation of future use
// https://github.com/cosmos/cosmos-sdk/pull/16310
type gasService struct{}
func NewGasService() gas.Service { return gasService{} }
func (gasService) GetGasMeter(ctx context.Context) gas.Meter {
c := sdk.UnwrapSDKContext(ctx)
return c.GasMeter()
}
func (gasService) WithGasMeter(ctx context.Context, meter gas.Meter) context.Context {
c := sdk.UnwrapSDKContext(ctx)
return c.WithGasMeter(meter)
}
// deprecated https://github.com/cosmos/cosmos-sdk/issues/19793
func (gasService) GetBlockGasMeter(context.Context) gas.Meter {
return nil
}
// deprecated https://github.com/cosmos/cosmos-sdk/issues/19793
func (gasService) WithBlockGasMeter(ctx context.Context, meter gas.Meter) context.Context {
return ctx
}

View File

@ -0,0 +1,230 @@
package nitro
import (
"fmt"
"math/big"
"runtime"
"strings"
"time"
"github.com/cometbft/cometbft/p2p"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"
"github.com/statechannels/go-nitro/protocols"
nitrotypes "github.com/statechannels/go-nitro/types"
"git.vdb.to/cerc-io/laconicd/server/relay"
"git.vdb.to/cerc-io/laconicd/utils"
)
const txTimeoutDuration = 5 * time.Second
func GetServerFromCmd(cmd *cobra.Command) (*Server, error) {
s, err := utils.GetFromContext[Server](cmd.Context(), ServerContextKey)
if err != nil {
return nil, err
}
for !s.Ready() {
runtime.Gosched()
}
return s, nil
}
func delayAfterUpdate() {
// HACK: delay so remote server status is synced before we return
// TODO: correct solution is passing messages with consistency guarantees,
// i.e. using txs to pass nitro messages and integrating state into chain storage
time.Sleep(500 * time.Millisecond)
}
// waitForObjective waits for an objective to complete or fail using select pattern
func waitForObjective[Info any](
failedChan <-chan protocols.ObjectiveId,
objective protocols.ObjectiveId,
updateChan <-chan Info,
statusCheck func(Info) bool,
) error {
for {
select {
case update := <-updateChan:
if statusCheck(update) {
delayAfterUpdate()
return nil
}
case failedObjective := <-failedChan:
if failedObjective == objective {
return fmt.Errorf("objective failed for unspecified reason: %s", objective)
}
}
}
}
func submitNitroTx(
clientCtx client.Context,
cmd *cobra.Command,
msg MsgWrapper,
unordered bool,
) error {
if msg.SignerField != nil {
ac := utils.NewAddressCodec()
var err error
*msg.SignerField, err = ac.BytesToString(clientCtx.FromAddress)
if err != nil {
return err
}
}
factory, err := tx.NewFactoryCLI(clientCtx, cmd.Flags())
if err != nil {
return err
}
if unordered {
deadline := time.Now().Add(txTimeoutDuration)
factory = factory.WithUnordered(true).WithTimeoutTimestamp(deadline)
}
if err := tx.GenerateOrBroadcastTxWithFactory(clientCtx, factory, msg.Msg); err != nil {
return err
}
return nil
}
// setupNitroCommand sets up common infrastructure for nitro commands
func setupNitroCommand(cmd *cobra.Command) (*client.Context, *Server, func() error, error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return nil, nil, nil, err
}
s, err := GetServerFromCmd(cmd)
if err != nil {
return nil, nil, nil, err
}
relayServer, err := relay.GetServerFromCmd(cmd)
if err != nil {
return nil, nil, nil, err
}
sw, err := connectAsPeer(clientCtx, relayServer, s.P2PReactors())
if err != nil {
return nil, nil, nil, err
}
return &clientCtx, s, sw.Stop, nil
}
// setupNitroQueryCommand sets up infrastructure for nitro query commands (no tx context needed)
func setupNitroQueryCommand(cmd *cobra.Command, needp2p bool) (*Server, func() error, error) {
s, err := GetServerFromCmd(cmd)
if err != nil {
return nil, nil, err
}
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return nil, nil, err
}
if !needp2p {
return s, func() error { return nil }, nil
}
relayServer, err := relay.GetServerFromCmd(cmd)
if err != nil {
return nil, nil, err
}
sw, err := connectAsPeer(clientCtx, relayServer, s.P2PReactors())
if err != nil {
return nil, nil, err
}
return s, sw.Stop, nil
}
// resolveTokens resolves amount and asset address to Nitro funds
func resolveTokens(s *Server, amountStr, assetAddr string) (nitrotypes.Funds, error) {
var amount *big.Int
var asset common.Address
if assetAddr != "" {
// Parse asset address
if !common.IsHexAddress(assetAddr) {
return nitrotypes.Funds{}, fmt.Errorf("invalid asset address: %s", assetAddr)
}
asset = common.HexToAddress(assetAddr)
// A token specified by address must have an integer amount
amount = new(big.Int)
if _, ok := amount.SetString(amountStr, 10); !ok {
return nitrotypes.Funds{}, fmt.Errorf("invalid amount for token %s: %s", assetAddr, amountStr)
}
if amount.Sign() <= 0 {
return nitrotypes.Funds{}, fmt.Errorf("amount must be positive for token %s: %s", assetAddr, amountStr)
}
} else {
// Parse as (known) coins and convert
coins, err := sdk.ParseCoinsNormalized(amountStr)
if err != nil {
return nitrotypes.Funds{}, fmt.Errorf("failed to parse coins: %w", err)
}
if len(coins) != 1 {
return nitrotypes.Funds{}, fmt.Errorf("only one asset is supported")
}
// Convert denomination to token address
asset, err = denomToToken(coins[0].Denom)
if err != nil {
return nitrotypes.Funds{}, err
}
amount = coins[0].Amount.BigInt()
}
return nitrotypes.Funds{asset: amount}, nil
}
// map denoms to token addresses
// TODO nitro should control this mapping, and resolution should happen in the module logic
func denomToToken(denom string) (common.Address, error) {
_tokens := map[string]common.Address{
"eth": {},
}
token, ok := _tokens[strings.ToLower(denom)]
if !ok {
return common.Address{}, fmt.Errorf("unknown token %s", denom)
}
return token, nil
}
// connects as a peer to the relay server, initializing the P2P switch
func connectAsPeer(
clientCtx client.Context, relayServer *relay.Server, reactors map[string]p2p.Reactor,
) (*relay.Switch, error) {
// initialize the switch using the target's node info
status, err := clientCtx.Client.Status(clientCtx.CmdContext)
if err != nil {
return nil, err
}
nodeInfo := status.NodeInfo
sw, err := relayServer.GetSwitch(clientCtx.ChainID, nodeInfo.ProtocolVersion.Block)
if err != nil {
return nil, fmt.Errorf("error creating switch: %w", err)
}
for name, reactor := range reactors {
if err := sw.AddReactor(name, reactor); err != nil {
return nil, fmt.Errorf("error adding reactor: %w", err)
}
}
if err := sw.Start(); err != nil {
return nil, fmt.Errorf("error starting switch: %w", err)
}
peerAddr := p2p.IDAddressString(nodeInfo.ID(), nodeInfo.ListenAddr)
// s.logger.Debug("dialing peer", "peer", peerAddr)
if err := sw.Dial(peerAddr); err != nil {
return nil, fmt.Errorf("error dialing peer: %w", err)
}
return sw, nil
}

80
server/nitro/config.go Normal file
View File

@ -0,0 +1,80 @@
package nitro
import (
"errors"
"fmt"
"git.vdb.to/cerc-io/laconicd/server"
)
type Config struct {
Enable bool `mapstructure:"enable" toml:"enable" comment:"Enable Nitro state channel functionality"`
EthKey string `mapstructure:"eth-key" toml:"eth-key" comment:"The private key used when interacting with the Ethereum chain and for our identity as a participant in the Nitro protocol."`
// UseDistsig bool `mapstructure:"use-distsig" toml:"use-distsig" comment:"Whether to use distributed signatures to authenticate Nitro actions."`
// EthChainID string
EthURL string `mapstructure:"eth-url" toml:"eth-url" comment:"The URL of the Ethereum node to connect to."`
EthAuthToken string `mapstructure:"eth-auth-token" toml:"eth-auth-token" comment:"The bearer token used for auth in requests to the Ethereum chain's RPC endpoint."`
EthStartBlock uint64 `mapstructure:"eth-start-block" toml:"eth-start-block" comment:"Ethereum block number to start listening for Nitro Adjudicator events."`
// TODO: move to module params?
EthNaAddress string `mapstructure:"eth-na-address" toml:"eth-na-address" comment:"Ethereum address of the Nitro Adjudicator contract."`
EthVpaAddress string `mapstructure:"eth-vpa-address" toml:"eth-vpa-address" comment:"Ethereum address of the Virtual Payment App contract."`
EthCaAddress string `mapstructure:"eth-ca-address" toml:"eth-ca-address" comment:"Ethereum address of the Consensus App contract."`
// P2P Rate Limiting
P2PRateLimitEnable bool `mapstructure:"p2p-rate-limit-enable" toml:"p2p-rate-limit-enable" comment:"Enable send-side rate limiting for P2P messages."`
P2PRateLimitRate float64 `mapstructure:"p2p-rate-limit-rate" toml:"p2p-rate-limit-rate" comment:"Maximum number of P2P messages per second (e.g., 10.0 for 10 messages/second)."`
P2PRateLimitBurst int `mapstructure:"p2p-rate-limit-burst" toml:"p2p-rate-limit-burst" comment:"Maximum burst size for P2P rate limiter."`
}
func DefaultConfig() *Config {
return &Config{
Enable: true,
// UseDistsig: false,
// EthURL: "ws://127.0.0.1:8545",
// Default rate limiting: 10 messages/second with burst of 20
P2PRateLimitEnable: true,
P2PRateLimitRate: 10.0,
P2PRateLimitBurst: 20,
}
}
func (c Config) Validate() error {
if !c.Enable {
return nil
}
if c.EthKey == "" {
return errors.New("nitro.eth-key must be set")
}
// TODO With distsig enabled, eth-key will be a mutually exclusive setting, and authentication
// for signing groups will be done using longterm key
//
// if c.UseDistsig && c.EthKey != "" {
// return errors.New("nitro.eth-key should not be set when distsig is enabled")
// }
// Validate rate limiting config
if c.P2PRateLimitEnable {
if c.P2PRateLimitRate <= 0 {
return errors.New("nitro.p2p-rate-limit-rate must be positive when rate limiting is enabled")
}
if c.P2PRateLimitBurst <= 0 {
return errors.New("nitro.p2p-rate-limit-burst must be positive when rate limiting is enabled")
}
}
return nil
}
func UnmarshalConfig(cfg map[string]any) (*Config, error) {
config := DefaultConfig()
if len(cfg) > 0 {
if err := server.UnmarshalSubConfig(cfg, serverName, config); err != nil {
return nil, fmt.Errorf("failed to unmarshal %T: %w", config, err)
}
}
return config, nil
}

View File

@ -0,0 +1,71 @@
package nitro
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestConfigValidation(t *testing.T) {
tests := []struct {
name string
config Config
expectErr bool
}{
{
name: "valid config with rate limiting",
config: Config{
Enable: true,
EthKey: "test-eth-key",
P2PRateLimitEnable: true,
P2PRateLimitRate: 10.0,
P2PRateLimitBurst: 20,
},
expectErr: false,
},
{
name: "invalid rate - zero",
config: Config{
Enable: true,
EthKey: "test-eth-key",
P2PRateLimitEnable: true,
P2PRateLimitRate: 0,
P2PRateLimitBurst: 20,
},
expectErr: true,
},
{
name: "invalid burst - zero",
config: Config{
Enable: true,
EthKey: "test-eth-key",
P2PRateLimitEnable: true,
P2PRateLimitRate: 10.0,
P2PRateLimitBurst: 0,
},
expectErr: true,
},
{
name: "rate limiting disabled - no validation needed",
config: Config{
Enable: true,
EthKey: "test-eth-key",
P2PRateLimitEnable: false,
P2PRateLimitRate: 0, // Invalid but should be ignored
P2PRateLimitBurst: 0, // Invalid but should be ignored
},
expectErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.config.Validate()
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

30
server/nitro/flags.go Normal file
View File

@ -0,0 +1,30 @@
package nitro
import (
"fmt"
"github.com/spf13/pflag"
)
// start flags are prefixed with the server name
func prefix(f string) string {
return fmt.Sprintf("%s.%s", serverName, f)
}
var (
FlagEthKey = prefix("eth-key")
FlagEthURL = prefix("eth-url")
FlagEthStartBlock = prefix("eth-start-block")
FlagEthAuthToken = prefix("eth-auth-token")
// FlagEthNaAddress = prefix("eth-na-address")
// FlagEthVpaAddress = prefix("eth-vpa-address")
// FlagEthCaAddress = prefix("eth-ca-address")
)
func AddFlags(f *pflag.FlagSet) {
f.String(FlagEthKey, "", "name of private key to use when interacting with the Ethereum chain")
f.String(FlagEthURL, "ws://127.0.0.1:8545", "URL of the Ethereum node to connect to")
f.String(FlagEthAuthToken, "", "bearer token used for auth in requests to the Ethereum chain's RPC endpoint")
f.Uint64(FlagEthStartBlock, 0, "Ethereum block number to start listening for Nitro Adjudicator events")
}

286
server/nitro/p2p.go Normal file
View File

@ -0,0 +1,286 @@
package nitro
import (
"context"
"errors"
"fmt"
"sync"
"time"
"cosmossdk.io/log"
"github.com/cometbft/cometbft/p2p"
"github.com/cosmos/gogoproto/proto"
nitrop2p "github.com/statechannels/go-nitro/node/engine/messageservice"
"github.com/statechannels/go-nitro/protocols"
nitrotypes "github.com/statechannels/go-nitro/types"
"golang.org/x/time/rate"
)
const (
P2PMessageChannel = byte(0x80)
// SigRequestChannel = byte(0x81)
maxMsgSize = 1024 * 1024
msgBufSize = 1000
limiterTimeout = 5 * time.Second
)
var (
_ p2p.Reactor = (*p2pReactor)(nil)
_ nitrop2p.MessageService = (*p2pMsgService)(nil)
)
// p2pReactor handles Nitro P2P messages via CometBFT
type p2pReactor struct {
p2p.BaseReactor
incoming chan nitrop2p.Message
outgoing map[p2p.ID]chan nitrop2p.Message
sendQueue chan nitrop2p.Message
rateLimiter *rate.Limiter
config *Config
logger log.Logger
mtx sync.RWMutex
wg sync.WaitGroup
}
type p2pMsgService struct {
id string
logger log.Logger
reactor *p2pReactor // one reactor per msg service
}
type streamDesc struct {
id byte
msgType proto.Message
}
func (sd streamDesc) StreamID() byte { return sd.id }
// TODO: convert Message to protobuf
func (sd streamDesc) MessageType() proto.Message { return sd.msgType }
// Reactor
func newReactor(config *Config, logger log.Logger) *p2pReactor {
var rateLimiter *rate.Limiter
if config.P2PRateLimitEnable {
rateLimiter = rate.NewLimiter(rate.Limit(config.P2PRateLimitRate), config.P2PRateLimitBurst)
}
ret := &p2pReactor{
incoming: make(chan nitrop2p.Message, msgBufSize),
outgoing: make(map[p2p.ID]chan nitrop2p.Message),
sendQueue: make(chan nitrop2p.Message, msgBufSize),
rateLimiter: rateLimiter,
config: config,
logger: logger,
}
ret.BaseReactor = *p2p.NewBaseReactor("nitro-reactor", ret)
return ret
}
// StreamDescriptors returns the stream descriptor for Nitro messages.
func (r *p2pReactor) GetChannels() []*p2p.ChannelDescriptor {
return []*p2p.ChannelDescriptor{
{
ID: P2PMessageChannel,
Priority: 5,
SendQueueCapacity: 100,
RecvBufferCapacity: 1000,
RecvMessageCapacity: maxMsgSize,
MessageType: &P2PMessage{},
},
// TODO Nitro peer info channel
}
}
// AddPeer begins sending messages to a peer.
func (r *p2pReactor) AddPeer(peer p2p.Peer) {
if !r.IsRunning() {
return
}
peerCh := make(chan nitrop2p.Message, msgBufSize)
r.mtx.Lock()
r.outgoing[peer.ID()] = peerCh
r.mtx.Unlock()
// Start goroutine to handle sending to this specific peer
go r.handlePeer(peer, peerCh)
}
func (r *p2pReactor) handlePeer(peer p2p.Peer, peerCh chan nitrop2p.Message) {
logger := r.logger.With("peer", peer)
defer func() {
r.mtx.Lock()
defer r.mtx.Unlock()
if _, ok := r.outgoing[peer.ID()]; ok {
close(peerCh)
delete(r.outgoing, peer.ID())
}
}()
for {
if !peer.IsRunning() || !r.IsRunning() {
return
}
inner_loop:
for {
select {
case msg := <-peerCh:
encoded, err := msg.Serialize()
if err != nil {
logger.Error("Failed to serialize message", "msg", msg, "err", err)
continue inner_loop
}
sent := peer.Send(p2p.Envelope{
Message: &P2PMessage{Content: encoded},
ChannelID: P2PMessageChannel,
})
if !sent {
logger.Error("Failed to send message", "msg", msg)
return
}
default:
break inner_loop
}
}
}
}
// Receive handles an envelope received from any connected peer on any registered channel.
func (r *p2pReactor) Receive(e p2p.Envelope) {
if !r.IsRunning() {
r.Logger.Debug("Receive", "src", e.Src, "chId", e.ChannelID)
return
}
switch e.ChannelID {
case P2PMessageChannel:
switch msg := e.Message.(type) {
case *P2PMessage:
nitroMsg, err := protocols.DeserializeMessage(msg.Content)
if err != nil {
r.Logger.Error("Failed to deserialize message", "err", err)
r.Switch.StopPeerForError(e.Src, err)
return
}
r.incoming <- nitroMsg
default:
r.Logger.Error(fmt.Sprintf("Unknown message type: %T", e.Message))
}
default:
r.Logger.Error(fmt.Sprintf("Unknown channel ID: %X", e.ChannelID))
}
}
func (r *p2pReactor) OnStart() error {
// Start the single sending goroutine
r.wg.Add(1)
go r.sendLoop()
return nil
}
func (r *p2pReactor) OnStop() { r.close() }
func (r *p2pReactor) close() {
close(r.incoming)
close(r.sendQueue)
r.wg.Wait() // Wait for sending goroutine to finish
r.mtx.Lock()
defer r.mtx.Unlock()
for id, ch := range r.outgoing {
close(ch)
delete(r.outgoing, id)
}
}
// sendLoop runs in a single goroutine and handles all outgoing messages with rate limiting
func (r *p2pReactor) sendLoop() {
defer r.wg.Done()
for msg := range r.sendQueue {
// Apply rate limiting if enabled
if r.rateLimiter != nil {
ctx, cancel := context.WithTimeout(context.Background(), limiterTimeout)
if err := r.rateLimiter.Wait(ctx); err != nil {
r.logger.Error("Rate limit exceeded, dropping message", "error", err)
cancel()
continue
}
cancel()
}
// Send to all connected peers
r.mtx.RLock()
for peerID, ch := range r.outgoing {
select {
case ch <- msg:
// Message sent successfully
default:
// Channel is full, log warning but don't block
r.logger.Warn("Peer channel full, dropping message", "peer", peerID)
}
}
r.mtx.RUnlock()
}
}
func (r *p2pReactor) send(msg nitrop2p.Message) {
select {
case r.sendQueue <- msg:
// Message queued successfully
default:
// Send queue is full, log error
r.logger.Error("Send queue full, dropping message")
}
}
// GetRateLimitStats returns current rate limiting statistics
func (r *p2pReactor) GetRateLimitStats() (enabled bool, limit float64, burst int, tokens float64) {
if r.rateLimiter == nil {
return false, 0, 0, 0
}
return true, float64(r.rateLimiter.Limit()), r.rateLimiter.Burst(), r.rateLimiter.Tokens()
}
// UpdateRateLimit updates the rate limiting parameters at runtime
func (r *p2pReactor) UpdateRateLimit(newRate float64, newBurst int) {
if r.rateLimiter != nil {
r.rateLimiter.SetLimit(rate.Limit(newRate))
r.rateLimiter.SetBurst(newBurst)
}
}
// MessageService
func newMessageService(id string, r *p2pReactor /* , logger log.Logger */) *p2pMsgService {
return &p2pMsgService{
id: id,
reactor: r,
// logger: logger,
}
}
func (ms *p2pMsgService) P2PMessages() <-chan nitrop2p.Message {
return ms.reactor.incoming
}
func (ms *p2pMsgService) Send(p nitrotypes.Participant, m nitrop2p.Message) error {
if !ms.reactor.IsRunning() {
return errors.New("reactor not running")
}
ms.reactor.send(m)
return nil
}
func (ms *p2pMsgService) Close() error {
// the lifecycle of the reactor is managed by cometbft
return nil
}
func (ms *p2pMsgService) Id() string {
return ms.id
}

317
server/nitro/p2p.pb.go Normal file
View File

@ -0,0 +1,317 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: cerc/nitro/v1/p2p.proto
package nitro
import (
fmt "fmt"
proto "github.com/cosmos/gogoproto/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// P2PMessage wraps a json-encoded Nitro protocols.Message
// TODO: proper proto message?
type P2PMessage struct {
Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
}
func (m *P2PMessage) Reset() { *m = P2PMessage{} }
func (m *P2PMessage) String() string { return proto.CompactTextString(m) }
func (*P2PMessage) ProtoMessage() {}
func (*P2PMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_97350824263b9206, []int{0}
}
func (m *P2PMessage) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *P2PMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_P2PMessage.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *P2PMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_P2PMessage.Merge(m, src)
}
func (m *P2PMessage) XXX_Size() int {
return m.Size()
}
func (m *P2PMessage) XXX_DiscardUnknown() {
xxx_messageInfo_P2PMessage.DiscardUnknown(m)
}
var xxx_messageInfo_P2PMessage proto.InternalMessageInfo
func (m *P2PMessage) GetContent() string {
if m != nil {
return m.Content
}
return ""
}
func init() {
proto.RegisterType((*P2PMessage)(nil), "cerc.nitro.v1.P2PMessage")
}
func init() { proto.RegisterFile("cerc/nitro/v1/p2p.proto", fileDescriptor_97350824263b9206) }
var fileDescriptor_97350824263b9206 = []byte{
// 162 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0x4e, 0x2d, 0x4a,
0xd6, 0xcf, 0xcb, 0x2c, 0x29, 0xca, 0xd7, 0x2f, 0x33, 0xd4, 0x2f, 0x30, 0x2a, 0xd0, 0x2b, 0x28,
0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x05, 0x49, 0xe8, 0x81, 0x25, 0xf4, 0xca, 0x0c, 0x95, 0xd4, 0xb8,
0xb8, 0x02, 0x8c, 0x02, 0x7c, 0x53, 0x8b, 0x8b, 0x13, 0xd3, 0x53, 0x85, 0x24, 0xb8, 0xd8, 0x93,
0xf3, 0xf3, 0x4a, 0x52, 0xf3, 0x4a, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x60, 0x5c, 0x27,
0xa7, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63,
0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xd2, 0x48, 0xcf, 0x2c, 0xd1,
0x2b, 0x4b, 0x49, 0xd2, 0x2b, 0xc9, 0xd7, 0x07, 0x99, 0xad, 0x9b, 0x99, 0xaf, 0x9f, 0x93, 0x98,
0x9c, 0x9f, 0x97, 0x99, 0x9c, 0xa2, 0x5f, 0x9c, 0x5a, 0x54, 0x96, 0x5a, 0x04, 0x71, 0x47, 0x12,
0x1b, 0xd8, 0x05, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x79, 0x5d, 0x6d, 0x7c, 0x9c, 0x00,
0x00, 0x00,
}
func (m *P2PMessage) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *P2PMessage) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *P2PMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Content) > 0 {
i -= len(m.Content)
copy(dAtA[i:], m.Content)
i = encodeVarintP2P(dAtA, i, uint64(len(m.Content)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintP2P(dAtA []byte, offset int, v uint64) int {
offset -= sovP2P(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *P2PMessage) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Content)
if l > 0 {
n += 1 + l + sovP2P(uint64(l))
}
return n
}
func sovP2P(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozP2P(x uint64) (n int) {
return sovP2P(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *P2PMessage) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowP2P
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: P2PMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: P2PMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowP2P
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthP2P
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthP2P
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Content = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipP2P(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthP2P
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipP2P(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowP2P
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowP2P
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowP2P
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthP2P
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupP2P
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthP2P
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthP2P = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowP2P = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupP2P = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -0,0 +1,74 @@
package nitro
import (
"testing"
"cosmossdk.io/log"
nitrop2p "github.com/statechannels/go-nitro/node/engine/messageservice"
nitrotypes "github.com/statechannels/go-nitro/types"
"github.com/stretchr/testify/assert"
)
func TestP2PRateLimit(t *testing.T) {
// Test with rate limiting enabled
config := &Config{
P2PRateLimitEnable: true,
P2PRateLimitRate: 2.0, // 2 messages per second
P2PRateLimitBurst: 2, // burst of 2
}
reactor := newReactor(config, log.NewNopLogger())
// Check rate limit stats
enabled, limit, burst, tokens := reactor.GetRateLimitStats()
assert.True(t, enabled, "Rate limiting should be enabled")
assert.Equal(t, 2.0, limit, "Rate limit should be 2.0")
assert.Equal(t, 2, burst, "Burst should be 2")
assert.Equal(t, float64(2), tokens, "Initial tokens should equal burst size")
// Test that UpdateRateLimit works
reactor.UpdateRateLimit(5.0, 10)
enabled, limit, burst, _ = reactor.GetRateLimitStats()
assert.True(t, enabled)
assert.Equal(t, 5.0, limit)
assert.Equal(t, 10, burst)
}
func TestP2PRateLimitDisabled(t *testing.T) {
// Test with rate limiting disabled
config := &Config{
P2PRateLimitEnable: false,
}
reactor := newReactor(config, log.NewNopLogger())
// Check rate limit stats
enabled, limit, burst, tokens := reactor.GetRateLimitStats()
assert.False(t, enabled, "Rate limiting should be disabled")
assert.Equal(t, 0.0, limit)
assert.Equal(t, 0, burst)
assert.Equal(t, 0.0, tokens)
}
func TestP2PRateLimitSendBehavior(t *testing.T) {
// Test actual send behavior with rate limiting
config := &Config{
P2PRateLimitEnable: true,
P2PRateLimitRate: 1000.0, // High rate for testing
P2PRateLimitBurst: 1, // Small burst
}
reactor := newReactor(config, log.NewNopLogger())
msgService := newMessageService("test", reactor)
// Create a dummy participant and message
participant, _ := nitrotypes.ParseParticipant("0x0000000000000000000000000000000000000001")
message := nitrop2p.Message{}
// This should return an error because reactor is not running
err := msgService.Send(participant, message)
// We expect an error because reactor is not running
assert.Error(t, err)
assert.Contains(t, err.Error(), "reactor not running")
}

166
server/nitro/protocol.go Normal file
View File

@ -0,0 +1,166 @@
package nitro
import (
"errors"
"fmt"
"math/big"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/statechannels/go-nitro/channel/state/outcome"
"github.com/statechannels/go-nitro/protocols"
nitrotypes "github.com/statechannels/go-nitro/types"
"git.vdb.to/cerc-io/laconicd/x/nitro/types/v1"
)
type MsgWrapper struct {
Msg sdk.Msg
Objective protocols.ObjectiveId
SignerField *string
}
func (s *Server) OpenLedgerChannel(funds nitrotypes.Funds, counterparty nitrotypes.Participant) (*MsgWrapper, error) {
// TODO: handle multiple assets
if len(funds) != 1 {
return nil, errors.New("only one asset is supported")
}
fund, exit := makeOutcome(s.ethAddress, counterparty, funds)
r, err := s.node.CreateLedgerChannel(counterparty, 0, exit)
if err != nil {
return nil, err
}
s.logger.Info("Initiated objective", "id", r.Id)
// Convert nitrotypes.Funds back to sdk.Coins for the message
// Use "token" as a default denomination for now
// TODO: nitro will store a mapping of denoms to token addresses
// an entry will be added for new denominations upon confirmation of ledger deposit
coins := sdk.Coins{sdk.NewCoin("token", math.NewIntFromBigInt(fund.Amount))}
msg := &v1.MsgOpenChannel{
Funds: coins,
NitroAddress: s.ethAddress.String(),
Counterparty: counterparty.String(),
ChannelId: r.ChannelId.String(),
}
return &MsgWrapper{msg, r.Id, &msg.Signer}, nil
}
func (s *Server) CloseLedgerChannel(channelId nitrotypes.ChannelID, isChallenge bool) (*MsgWrapper, error) {
objective, err := s.node.CloseLedgerChannel(channelId, isChallenge)
if err != nil {
return nil, err
}
s.logger.Info("Initiated objective", "id", objective)
msg := &v1.MsgCloseChannel{
ChannelId: channelId.String(),
IsChallenge: isChallenge,
}
return &MsgWrapper{msg, objective, &msg.Signer}, nil
}
func (s *Server) CreatePaymentChannel(intermediaries []string, counterpartyString string, challengeDuration uint32, funds nitrotypes.Funds) (*MsgWrapper, error) {
// Parse intermediaries
var intermediaryParticipants []nitrotypes.Participant
for _, addr := range intermediaries {
participant, err := nitrotypes.ParseParticipant(addr)
if err != nil {
return nil, fmt.Errorf("failed to parse intermediary %s: %w", addr, err)
}
intermediaryParticipants = append(intermediaryParticipants, participant)
}
// Parse counterparty
counterparty, err := nitrotypes.ParseParticipant(counterpartyString)
if err != nil {
return nil, fmt.Errorf("failed to parse counterparty: %w", err)
}
// TODO: handle multiple assets
if len(funds) != 1 {
return nil, errors.New("only one asset is supported")
}
if len(funds) != 1 {
return nil, errors.New("only one asset is supported")
}
fund, exit := makeOutcome(s.ethAddress, counterparty, funds)
// Create payment channel
r, err := s.node.CreatePaymentChannel(intermediaryParticipants, counterparty, challengeDuration, exit)
if err != nil {
return nil, err
}
s.logger.Info("Initiated payment channel objective", "id", r.Id)
// Convert nitrotypes.Funds back to sdk.Coins for the message
coins := sdk.Coins{sdk.NewCoin("token", math.NewIntFromBigInt(fund.Amount))}
msg := &v1.MsgCreatePaymentChannel{
NitroAddress: s.ethAddress.String(),
Counterparty: counterpartyString,
Intermediaries: intermediaries,
ChallengeDuration: challengeDuration,
Funds: coins,
ChannelId: r.ChannelId.String(),
}
return &MsgWrapper{msg, r.Id, &msg.Signer}, nil
}
func (s *Server) ClosePaymentChannel(channelId nitrotypes.ChannelID) (*MsgWrapper, error) {
objective, err := s.node.ClosePaymentChannel(channelId)
if err != nil {
return nil, err
}
s.logger.Info("Initiated payment channel close objective", "id", objective)
msg := &v1.MsgClosePaymentChannel{
ChannelId: channelId.String(),
}
return &MsgWrapper{msg, objective, &msg.Signer}, nil
}
func (s *Server) Pay(channelId nitrotypes.ChannelID, amount sdk.Coin) (*MsgWrapper, error) {
err := s.node.Pay(channelId, amount.Amount.BigInt())
if err != nil {
return nil, err
}
nodeAddrStr, err := s.addressCodec.BytesToString(s.nodeAddress())
if err != nil {
return nil, err
}
msg := &v1.MsgPay{
ChannelId: channelId.String(),
Amount: amount,
Signer: nodeAddrStr,
}
return &MsgWrapper{msg, "", &msg.Signer}, nil
}
func makeOutcome(ethAddress, counterparty nitrotypes.Participant, funds nitrotypes.Funds) (nitrotypes.Fund, outcome.Exit) {
var fund nitrotypes.Fund
for asset, amount := range funds {
fund = nitrotypes.Fund{asset, amount}
}
exit := outcome.Exit{
outcome.SingleAssetExit{
Asset: fund.Asset,
Allocations: []outcome.Allocation{
{
Destination: ethAddress.ToDestination(),
Amount: fund.Amount,
},
{
Destination: counterparty.ToDestination(),
Amount: big.NewInt(0),
},
},
},
}
return fund, exit
}

194
server/nitro/query.go Normal file
View File

@ -0,0 +1,194 @@
package nitro
import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/spf13/cobra"
nitrotypes "github.com/statechannels/go-nitro/types"
"git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/server/relay"
)
func GetChannelCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "get-channel <channel-id>",
Short: "Get information about a channel",
Args: cobra.ExactArgs(1),
Long: "Retrieves detailed information about a specific ledger or payment channel.",
RunE: func(cmd *cobra.Command, args []string) error {
channelId, err := nitrotypes.ParseChannelID(args[0])
if err != nil {
return fmt.Errorf("failed to parse channel ID: %w", err)
}
ledger, _ := cmd.Flags().GetBool("ledger")
payment, _ := cmd.Flags().GetBool("payment")
local, _ := cmd.Flags().GetBool("local")
if !ledger && !payment {
ledger = true
}
if ledger && payment {
return fmt.Errorf("cannot specify both --ledger and --payment flags")
}
s, done, err := setupNitroQueryCommand(cmd, !local)
if err != nil {
return err
}
defer done()
var output []byte
if ledger {
info, err := s.node.GetLedgerChannel(channelId)
if err != nil {
return fmt.Errorf("failed to get ledger channel: %w", err)
}
output, err = json.MarshalIndent(info, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal response: %w", err)
}
} else {
info, err := s.node.GetPaymentChannel(channelId)
if err != nil {
return fmt.Errorf("failed to get payment channel: %w", err)
}
output, err = json.MarshalIndent(info, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal response: %w", err)
}
}
fmt.Println(string(output))
return nil
},
}
cmd.Flags().Bool("ledger", false, "Get ledger channel information")
cmd.Flags().Bool("payment", false, "Get payment channel information")
cmd.Flags().Bool("local", false, "Query local data only (no P2P connection)")
flags.AddQueryFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func ListChannelsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list-channels",
Short: "List all ledger channels",
Args: cobra.NoArgs,
Long: "Retrieves information about all ledger channels.",
RunE: func(cmd *cobra.Command, args []string) error {
local, _ := cmd.Flags().GetBool("local")
s, done, err := setupNitroQueryCommand(cmd, !local)
if err != nil {
return err
}
defer done()
channels, err := s.node.GetAllLedgerChannels()
if err != nil {
return fmt.Errorf("failed to get all ledger channels: %w", err)
}
output, err := json.MarshalIndent(channels, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal response: %w", err)
}
fmt.Println(string(output))
return nil
},
}
cmd.Flags().Bool("local", false, "Query local data only (no P2P connection)")
flags.AddQueryFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func ListPaymentChannelsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list-payment-channels <ledger-channel-id>",
Short: "List all payment channels for a ledger channel",
Args: cobra.ExactArgs(1),
Long: "Retrieves all active payment channels associated with a specific ledger channel.",
RunE: func(cmd *cobra.Command, args []string) error {
ledgerChannelId, err := nitrotypes.ParseChannelID(args[0])
if err != nil {
return fmt.Errorf("failed to parse ledger channel ID: %w", err)
}
local, _ := cmd.Flags().GetBool("local")
s, done, err := setupNitroQueryCommand(cmd, !local)
if err != nil {
return err
}
defer done()
channels, err := s.node.GetPaymentChannelsByLedger(ledgerChannelId)
if err != nil {
return fmt.Errorf("failed to get payment channels: %w", err)
}
output, err := json.MarshalIndent(channels, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal response: %w", err)
}
fmt.Println(string(output))
return nil
},
}
cmd.Flags().Bool("local", false, "Query local data only (no P2P connection)")
flags.AddQueryFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func IdentityCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "identity",
Short: "Get the node identity",
Args: cobra.NoArgs,
Long: "Retrieves the node's identity. Use --participant for principal ID (default) or --network for network address.",
RunE: func(cmd *cobra.Command, args []string) error {
participant, _ := cmd.Flags().GetBool("participant")
network, _ := cmd.Flags().GetBool("network")
if !participant && !network {
participant = true
}
if participant && network {
return fmt.Errorf("cannot specify both --participant and --network flags")
}
s, done, err := setupNitroQueryCommand(cmd, false)
if err != nil {
return err
}
defer done()
if participant {
identity := s.node.PrincipalID()
fmt.Println(identity.String())
} else {
identity := s.node.NetworkID()
fmt.Println(identity.String())
}
return nil
},
}
cmd.Flags().Bool("participant", false, "Get the principal ID (default)")
cmd.Flags().Bool("network", false, "Get the network address")
flags.AddQueryFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{})
return cmd
}

210
server/nitro/server.go Normal file
View File

@ -0,0 +1,210 @@
package nitro
import (
"context"
"encoding/hex"
"fmt"
"log/slog"
"path/filepath"
"sync/atomic"
cmtcrypto "github.com/cometbft/cometbft/crypto"
"github.com/cometbft/cometbft/p2p"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/spf13/pflag"
nitrocrypto "github.com/statechannels/go-nitro/crypto"
"github.com/statechannels/go-nitro/node"
"github.com/statechannels/go-nitro/node/engine"
"github.com/statechannels/go-nitro/node/engine/chainservice"
chainutils "github.com/statechannels/go-nitro/node/engine/chainservice/utils"
"github.com/statechannels/go-nitro/node/engine/store"
nitrotypes "github.com/statechannels/go-nitro/types"
"cosmossdk.io/core/address"
"cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/utils"
)
const (
serverName = "nitro"
ServerContextKey = "server." + serverName
)
// TODO
// 1. implement participation as a signing group member:
// 1.1. initialize group delegate credentials, with distsig over ABCI
// 1.2. bind custodian contract for chain/adjudicator actions
// 2. operation to update DKG group members
// 3. integrate with onboarding module
// Server wraps and configures a go-nitro Node.
type Server struct {
logger log.Logger
config *Config
node *node.Node
storeDir string // path to Nitro store directory
reactor *p2pReactor
keyring keyring.Keyring
addressCodec address.Codec
ethAddress nitrotypes.Address
nodeKey cmtcrypto.PrivKey
// participantID nitrotypes.Participant
ready atomic.Bool
}
func New(globalConfig server.ConfigMap, logger log.Logger, kr keyring.Keyring) (*Server, error) {
home, _ := globalConfig[flags.FlagHome].(string)
cfg, err := UnmarshalConfig(globalConfig)
if err != nil {
return nil, err
}
if err := cfg.Validate(); err != nil {
return nil, err
}
s := &Server{
config: cfg,
storeDir: filepath.Join(home, "nitro"),
reactor: newReactor(cfg, logger.With(log.ModuleKey, serverName, "component", "p2p-reactor")),
keyring: kr,
logger: logger.With(log.ModuleKey, serverName),
addressCodec: utils.NewAddressCodec(),
}
return s, nil
}
func (*Server) Name() string {
return serverName
}
func (s *Server) Start(ctx context.Context) error {
if !s.config.Enable {
s.logger.Info(fmt.Sprintf("%s server is disabled via config", s.Name()))
return nil
}
// TODO: pass keyring through context?
if err := s.init(s.keyring); err != nil {
return err
}
s.logger.Info("starting Nitro server")
s.ready.Store(true)
return nil
}
func (s *Server) Stop(context.Context) error {
s.logger.Info("stopping Nitro server")
s.ready.Store(false)
if s.node != nil {
return s.node.Close()
}
return nil
}
func (s *Server) init(kr keyring.Keyring) error {
var (
ethkey cmtcrypto.PrivKey
err error
c = s.config
)
if c.EthKey != "" {
ethkey, err = utils.ExtractPrivateKeyByUid(kr, c.EthKey)
if err != nil {
return err
}
} else {
return fmt.Errorf("eth-key is not set") // should already be caught by Config.Validate
}
s.nodeKey = ethkey
storeOpts := store.StoreOpts{
UseDurableStore: true,
DurableStoreDir: s.storeDir,
}
chainOpts := chainservice.ChainOpts{
ChainUrl: c.EthURL,
StartBlockNum: c.EthStartBlock,
ChainAuthToken: c.EthAuthToken,
ChainPk: hex.EncodeToString(ethkey.Bytes()),
}
// inject SDK logger into slog, which Nitro uses
loggerImpl, ok := s.logger.Impl().(*slog.Logger)
if ok {
slog.SetDefault(loggerImpl)
} else {
s.logger.Warn("Logger backend is not slog, cannot set as Nitro logger")
}
store, err := store.NewStore(storeOpts)
if err != nil {
return err
}
// Compare chainOpts.ChainStartBlock to lastBlockNum seen in store. The larger of the two
// gets passed as an argument when creating NewEthChainService
storeBlockNum, err := store.GetLastBlockNumSeen()
if err != nil {
return err
}
chainOpts.StartBlockNum = max(storeBlockNum, chainOpts.StartBlockNum)
contractAddresses := node.ContractAddresses{
NaAddress: common.HexToAddress(c.EthNaAddress),
VpaAddress: common.HexToAddress(c.EthVpaAddress),
CaAddress: common.HexToAddress(c.EthCaAddress),
}
chainOpts.CreateAdjudicator = chainutils.AdjudicatorCreator(contractAddresses.NaAddress)
chain, err := chainservice.NewL1ChainService(chainOpts)
if err != nil {
return err
}
// note: utils.EthAddressFromPubKey expects uncompressed keys
ethPubkey, err := crypto.DecompressPubkey(ethkey.PubKey().Bytes())
if err != nil {
return err
}
s.ethAddress = nitrotypes.Address(crypto.PubkeyToAddress(*ethPubkey))
s.node = node.New(
newMessageService("laconicd-p2p-"+s.ethAddress.String(), s.reactor),
chain,
store,
&engine.PermissivePolicy{},
nitrocrypto.NewSimpleCredential(ethkey.Bytes()),
contractAddresses,
)
return nil
}
func (s *Server) Config() any {
if s.config == nil {
return DefaultConfig()
}
return s.config
}
func (s *Server) StartCmdFlags() *pflag.FlagSet {
flags := pflag.NewFlagSet(s.Name(), pflag.ExitOnError)
AddFlags(flags)
return flags
}
func (s *Server) Ready() bool {
return s.ready.Load()
}
func (s *Server) ContextKey() string {
return ServerContextKey
}
func (s *Server) P2PReactors() map[string]p2p.Reactor {
return map[string]p2p.Reactor{"NITRO": s.reactor}
}
func (s *Server) nodeAddress() sdk.AccAddress {
return sdk.AccAddress(s.nodeKey.PubKey().Address())
}

326
server/nitro/tx.go Normal file
View File

@ -0,0 +1,326 @@
package nitro
import (
"encoding/json"
"fmt"
"math/big"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"github.com/statechannels/go-nitro/node/query"
nitrotypes "github.com/statechannels/go-nitro/types"
"git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/server/relay"
)
func OpenChannelCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "open-channel <counterparty> <amount>",
Short: "Open a ledger channel with the given counterparty",
Args: cobra.ExactArgs(2),
Long: "Creates a ledger channel with the specified counterparty and funding amount.",
RunE: func(cmd *cobra.Command, args []string) error {
counterparty, err := nitrotypes.ParseParticipant(args[0])
if err != nil {
return fmt.Errorf("failed to parse counterparty: %w", err)
}
clientCtx, s, done, err := setupNitroCommand(cmd)
if err != nil {
return err
}
defer done()
assetAddr, _ := cmd.Flags().GetString("asset")
funds, err := resolveTokens(s, args[1], assetAddr)
if err != nil {
return err
}
m, err := s.OpenLedgerChannel(funds, counterparty)
if err != nil {
return err
}
if err = waitForObjective(
s.node.FailedObjectives(),
m.Objective,
s.node.LedgerUpdatedChan(m.Objective.ChannelID()),
func(info query.LedgerChannelInfo) bool { return info.Status == query.Open },
); err != nil {
return err
}
return submitNitroTx(*clientCtx, cmd, *m, false)
},
}
cmd.Flags().String("asset", "", "Ethereum token address (if specified, amount is parsed as decimal without denomination)")
AddFlags(cmd.Flags())
flags.AddTxFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func CloseChannelCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "close-channel <channel-id>",
Short: "Close a ledger channel",
Args: cobra.ExactArgs(1),
Long: "Close a ledger channel. Use --challenge flag for dispute resolution when cooperation fails.",
RunE: func(cmd *cobra.Command, args []string) error {
channelId, err := nitrotypes.ParseChannelID(args[0])
if err != nil {
return fmt.Errorf("failed to parse channel ID: %w", err)
}
isChallenge, err := cmd.Flags().GetBool("challenge")
if err != nil {
return err
}
clientCtx, s, done, err := setupNitroCommand(cmd)
if err != nil {
return err
}
defer done()
m, err := s.CloseLedgerChannel(channelId, isChallenge)
if err != nil {
return err
}
if err = waitForObjective(
s.node.FailedObjectives(),
m.Objective,
s.node.LedgerUpdatedChan(channelId),
func(info query.LedgerChannelInfo) bool { return info.Status == query.Complete },
); err != nil {
return err
}
return submitNitroTx(*clientCtx, cmd, *m, false)
},
}
cmd.Flags().Bool("challenge", false, "Close via challenge (dispute resolution) instead of cooperative closure")
AddFlags(cmd.Flags())
flags.AddTxFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func OpenPaymentChannelCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "open-payment-channel <counterparty> <amount> [intermediaries...]",
Short: "Open a virtual payment channel",
Args: cobra.MinimumNArgs(2),
Long: "Creates a virtual payment channel with the specified counterparty and optional intermediaries.",
RunE: func(cmd *cobra.Command, args []string) error {
counterparty := args[0]
challengeDuration, err := cmd.Flags().GetUint32("challenge-duration")
if err != nil {
return err
}
var intermediaries []string
if len(args) > 2 {
intermediaries = args[2:]
}
clientCtx, s, done, err := setupNitroCommand(cmd)
if err != nil {
return err
}
defer done()
assetAddr, _ := cmd.Flags().GetString("asset")
funds, err := resolveTokens(s, args[1], assetAddr)
if err != nil {
return err
}
m, err := s.CreatePaymentChannel(intermediaries, counterparty, challengeDuration, funds)
if err != nil {
return err
}
if err = waitForObjective(
s.node.FailedObjectives(),
m.Objective,
s.node.PaymentChannelUpdatedChan(m.Objective.ChannelID()),
func(info query.PaymentChannelInfo) bool { return info.Status == query.Open },
); err != nil {
return err
}
return submitNitroTx(*clientCtx, cmd, *m, false)
},
}
cmd.Flags().String("asset", "", "Ethereum token address (if specified, amount is parsed as decimal without denomination)")
cmd.Flags().Uint32("challenge-duration", 0, "Challenge duration for the channel")
AddFlags(cmd.Flags())
flags.AddTxFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func ClosePaymentChannelCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "close-payment-channel <channel-id>",
Short: "Close a virtual payment channel",
Args: cobra.ExactArgs(1),
Long: "Close a virtual payment channel with the given channel ID.",
RunE: func(cmd *cobra.Command, args []string) error {
channelId, err := nitrotypes.ParseChannelID(args[0])
if err != nil {
return fmt.Errorf("failed to parse channel ID: %w", err)
}
clientCtx, s, done, err := setupNitroCommand(cmd)
if err != nil {
return err
}
defer done()
m, err := s.ClosePaymentChannel(channelId)
if err != nil {
return err
}
if err = waitForObjective(
s.node.FailedObjectives(),
m.Objective,
s.node.PaymentChannelUpdatedChan(channelId),
func(info query.PaymentChannelInfo) bool { return info.Status == query.Complete },
); err != nil {
return err
}
return submitNitroTx(*clientCtx, cmd, *m, false)
},
}
AddFlags(cmd.Flags())
flags.AddTxFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func PayCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "pay <channel-id> <amount>",
Short: "Send a payment through a payment channel",
Args: cobra.ExactArgs(2),
Long: "Creates and sends a payment voucher through the specified payment channel.",
RunE: func(cmd *cobra.Command, args []string) error {
channelId, err := nitrotypes.ParseChannelID(args[0])
if err != nil {
return fmt.Errorf("failed to parse channel ID: %w", err)
}
coins, err := sdk.ParseCoinsNormalized(args[1])
if err != nil {
return fmt.Errorf("failed to parse amount: %w", err)
}
if len(coins) != 1 {
return fmt.Errorf("exactly one coin denomination required")
}
clientCtx, s, done, err := setupNitroCommand(cmd)
if err != nil {
return err
}
defer done()
info, err := s.node.GetPaymentChannel(channelId)
if err != nil {
return err
}
paidSoFar := info.Balance.PaidSoFar.ToInt()
expectedAmount := new(big.Int).Add(paidSoFar, coins[0].Amount.BigInt())
updateChan := s.node.PaymentChannelUpdatedChan(channelId)
m, err := s.Pay(channelId, coins[0])
if err != nil {
return fmt.Errorf("failed to send payment: %w", err)
}
// TODO: unordered tx for payments: this could break if tx is sent before opening the
// channel; think through wrt. state integration
if err = submitNitroTx(*clientCtx, cmd, *m, true); err != nil {
return err
}
for info := range updateChan {
if expectedAmount.Cmp(info.Balance.PaidSoFar.ToInt()) <= 0 {
break
}
}
delayAfterUpdate()
return nil
},
}
AddFlags(cmd.Flags())
flags.AddTxFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}
func CreateVoucherCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create-voucher <channel-id> <amount>",
Short: "Create a payment voucher for a payment channel",
Args: cobra.ExactArgs(2),
Long: "Creates a signed payment voucher that can be used to redeem payments from the specified channel.",
RunE: func(cmd *cobra.Command, args []string) error {
channelId, err := nitrotypes.ParseChannelID(args[0])
if err != nil {
return fmt.Errorf("failed to parse channel ID: %w", err)
}
coins, err := sdk.ParseCoinsNormalized(args[1])
if err != nil {
return fmt.Errorf("failed to parse amount: %w", err)
}
if len(coins) != 1 {
return fmt.Errorf("exactly one coin denomination required")
}
amount := coins[0].Amount.BigInt()
s, done, err := setupNitroQueryCommand(cmd, false)
if err != nil {
return err
}
defer done()
voucher, err := s.node.CreateVoucher(channelId, amount)
if err != nil {
return fmt.Errorf("failed to create voucher: %w", err)
}
output, err := json.MarshalIndent(voucher, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal voucher: %w", err)
}
fmt.Println(string(output))
return nil
},
}
flags.AddQueryFlagsToCmd(cmd)
server.SetRequiredComponents(cmd, &Server{}, &relay.Server{})
return cmd
}

23
server/relay/config.go Normal file
View File

@ -0,0 +1,23 @@
package relay
import (
"fmt"
"git.vdb.to/cerc-io/laconicd/server"
)
type Config struct {
// Target string `mapstructure:"target" toml:"target" comment:"The target peer to relay messages to"`
}
func DefaultConfig() *Config {
return &Config{}
}
func UnmarshalConfig(cfg map[string]any) (*Config, error) {
config := DefaultConfig()
if err := server.UnmarshalSubConfig(cfg, serverName, config); err != nil {
return nil, fmt.Errorf("failed to unmarshal %T: %w", config, err)
}
return config, nil
}

199
server/relay/server.go Normal file
View File

@ -0,0 +1,199 @@
package relay
import (
"context"
"errors"
"fmt"
"strings"
"sync/atomic"
"cosmossdk.io/log"
cmtcfg "github.com/cometbft/cometbft/config"
"github.com/cometbft/cometbft/p2p"
"github.com/cometbft/cometbft/p2p/pex"
"github.com/cometbft/cometbft/version"
cmtversion "github.com/cometbft/cometbft/version"
"git.vdb.to/cerc-io/laconicd/server"
)
const (
serverName = "relay"
ServerContextKey = "server." + serverName
)
// Server is a server component with CometBFT p2p capabilities
type Server struct {
logger log.Logger
config *Config
cometConfig *cmtcfg.Config
nodeKey *p2p.NodeKey
ready atomic.Bool
}
type Switch struct {
*p2p.Switch
transport *p2p.MultiplexTransport
}
func New(cfg server.ConfigMap, logger log.Logger) (*Server, error) {
c, err := UnmarshalConfig(cfg)
if err != nil {
return nil, err
}
cometConfig := cmtcfg.DefaultConfig()
if err := server.UnmarshalSubConfig(cfg, "", &cometConfig); err != nil {
return nil, err
}
return &Server{
config: c,
cometConfig: cometConfig,
logger: logger.With(log.ModuleKey, serverName),
}, nil
}
func (*Server) Name() string {
return serverName
}
func (s *Server) Start(ctx context.Context) error {
var err error
s.nodeKey, err = p2p.LoadOrGenNodeKey(s.cometConfig.NodeKeyFile())
if err != nil {
return err
}
s.ready.Store(true)
return nil
}
func (s *Server) Stop(ctx context.Context) error {
s.ready.Store(false)
return nil
}
func (s *Server) Config() any {
if s.config == nil {
return DefaultConfig()
}
return s.config
}
func (s *Server) Ready() bool {
return s.ready.Load()
}
func (s *Server) ContextKey() string {
return ServerContextKey
}
func (s *Server) GetSwitch(chainID string, blockVersion uint64) (*Switch, error) {
nodeInfo, err := makeNodeInfo(s.cometConfig, s.nodeKey, chainID, blockVersion)
if err != nil {
return nil, err
}
transport := createTransport(s.cometConfig, nodeInfo, s.nodeKey)
sw := p2p.NewSwitch(s.cometConfig.P2P, transport, p2p.SwitchPeerFilters())
sw.SetNodeInfo(nodeInfo)
sw.SetNodeKey(s.nodeKey)
return &Switch{sw, transport}, nil
}
func (s *Switch) AddReactor(name string, reactor p2p.Reactor) error {
s.Switch.AddReactor(name, reactor)
// register the new channels to the nodeInfo
ni, ok := s.NodeInfo().(p2p.DefaultNodeInfo)
if !ok {
return errors.New("Node info is not of type DefaultNodeInfo")
}
for _, chDesc := range reactor.GetChannels() {
if !ni.HasChannel(chDesc.ID) {
ni.Channels = append(ni.Channels, chDesc.ID)
s.transport.AddChannel(chDesc.ID)
}
}
s.SetNodeInfo(ni)
return nil
}
func (s *Switch) Dial(peerAddr string) error {
addr, err := p2p.NewNetAddressString(peerAddr)
if err != nil {
return fmt.Errorf("error resolving net address: %w", err)
}
err = s.AddPersistentPeers([]string{addr.String()})
if err != nil {
return fmt.Errorf("error adding persistent peer: %w", err)
}
return s.DialPeerWithAddress(addr)
}
func makeNodeInfo(
config *cmtcfg.Config,
nodeKey *p2p.NodeKey,
chainID string,
blockVersion uint64,
) (p2p.DefaultNodeInfo, error) {
nodeInfo := p2p.DefaultNodeInfo{
ProtocolVersion: p2p.NewProtocolVersion(
cmtversion.P2PProtocol,
blockVersion,
0,
),
DefaultNodeID: nodeKey.ID(),
Network: chainID,
Version: version.TMCoreSemVer,
Channels: nil, // updated in AddReactor
Moniker: config.Moniker,
Other: p2p.DefaultNodeInfoOther{
RPCAddress: config.RPC.ListenAddress,
},
}
if config.P2P.PexReactor {
nodeInfo.Channels = append(nodeInfo.Channels, pex.PexChannel)
}
lAddr := config.P2P.ExternalAddress
if lAddr == "" {
lAddr = config.P2P.ListenAddress
}
nodeInfo.ListenAddr = lAddr
// err := nodeInfo.Validate()
return nodeInfo, nil
}
func createTransport(
config *cmtcfg.Config,
nodeInfo p2p.NodeInfo,
nodeKey *p2p.NodeKey,
) *p2p.MultiplexTransport {
var (
mConnConfig = p2p.MConnConfig(config.P2P)
transport = p2p.NewMultiplexTransport(nodeInfo, *nodeKey, mConnConfig)
connFilters = []p2p.ConnFilterFunc{}
)
if !config.P2P.AllowDuplicateIP {
connFilters = append(connFilters, p2p.ConnDuplicateIPFilter())
}
p2p.MultiplexTransportConnFilters(connFilters...)(transport)
// Limit the number of incoming connections.
max := config.P2P.MaxNumInboundPeers
p2p.MultiplexTransportMaxIncomingConnections(max)(transport)
return transport
}
func removeProtocolIfDefined(addr string) string {
if strings.Contains(addr, "://") {
return strings.Split(addr, "://")[1]
}
return addr
}

20
server/relay/util.go Normal file
View File

@ -0,0 +1,20 @@
package relay
import (
"runtime"
"git.vdb.to/cerc-io/laconicd/utils"
"github.com/spf13/cobra"
)
func GetServerFromCmd(cmd *cobra.Command) (*Server, error) {
s, err := utils.GetFromContext[Server](cmd.Context(), ServerContextKey)
if err != nil {
return nil, err
}
for !s.Ready() {
s.logger.Debug("waiting for relay server")
runtime.Gosched()
}
return s, nil
}

29
server/server.go Normal file
View File

@ -0,0 +1,29 @@
package server
// The utilities in this file are copied from cosmos-sdk server/v2
import (
"context"
"github.com/spf13/pflag"
)
// ServerComponent is a server component that can be started and stopped.
type ServerComponent interface {
// Name returns the name of the server component.
Name() string
// Start starts the server component.
Start(context.Context) error
// Stop stops the server component.
// Once Stop has been called on a server component, it may not be reused.
Stop(context.Context) error
}
// HasStartFlags is a server component that has start flags.
type HasStartFlags interface {
// StartCmdFlags returns server start flags.
// Those flags should be prefixed with the server name.
// They are then merged with the server config in one viper instance.
StartCmdFlags() *pflag.FlagSet
}

View File

@ -3,7 +3,6 @@ package keeper_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
integrationTest "git.vdb.to/cerc-io/laconicd/tests/integration"
@ -19,7 +18,7 @@ type KeeperTestSuite struct {
func (kts *KeeperTestSuite) SetupTest() {
err := kts.TestFixture.Setup()
assert.Nil(kts.T(), err)
kts.Require().NoError(err)
qr := kts.App.QueryHelper()
kts.queryClient = types.NewQueryClient(qr)

View File

@ -30,7 +30,7 @@ func (kts *KeeperTestSuite) TestGrpcQueryParams() {
for _, test := range testCases {
kts.Run(fmt.Sprintf("Case %s", test.msg), func() {
resp, err := kts.queryClient.Params(context.Background(), test.req)
kts.Require().Nil(err)
kts.Require().NoError(err)
kts.Require().Equal(*(resp.Params), types.DefaultParams())
})
}
@ -59,14 +59,14 @@ func (kts *KeeperTestSuite) TestGrpcGetAuction() {
var expectedAuction types.Auction
if test.createAuction {
auction, _, err := kts.createAuctionAndCommitBid(false)
kts.Require().Nil(err)
kts.Require().NoError(err)
test.req.Id = auction.Id
expectedAuction = *auction
}
resp, err := kts.queryClient.GetAuction(context.Background(), test.req)
if test.createAuction {
kts.Require().Nil(err)
kts.Require().NoError(err)
kts.Require().NotNil(resp.GetAuction())
kts.Require().EqualExportedValues(expectedAuction, *(resp.GetAuction()))
} else {
@ -103,7 +103,7 @@ func (kts *KeeperTestSuite) TestGrpcGetAllAuctions() {
kts.Run(fmt.Sprintf("Case %s", test.msg), func() {
if test.createAuctions {
_, _, err := kts.createAuctionAndCommitBid(false)
kts.Require().Nil(err)
kts.Require().NoError(err)
}
resp, _ := kts.queryClient.Auctions(context.Background(), test.req)
@ -153,7 +153,7 @@ func (kts *KeeperTestSuite) TestGrpcGetBids() {
resp, err := kts.queryClient.GetBids(context.Background(), test.req)
if test.createAuction {
kts.Require().Nil(err)
kts.Require().NoError(err)
kts.Require().Equal(test.bidCount, len(resp.GetBids()))
} else {
kts.Require().NotNil(err)

View File

@ -3,7 +3,6 @@ package keeper_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
integrationTest "git.vdb.to/cerc-io/laconicd/tests/integration"
@ -19,7 +18,7 @@ type KeeperTestSuite struct {
func (kts *KeeperTestSuite) SetupTest() {
err := kts.TestFixture.Setup()
assert.Nil(kts.T(), err)
kts.Require().NoError(err)
qr := kts.App.QueryHelper()
kts.queryClient = types.NewQueryClient(qr)

View File

@ -26,7 +26,7 @@ func (kts *KeeperTestSuite) TestGrpcQueryParams() {
for _, test := range testCases {
kts.Run(fmt.Sprintf("Case %s", test.msg), func() {
resp, err := kts.queryClient.Params(context.Background(), test.req)
kts.Require().Nil(err)
kts.Require().NoError(err)
kts.Require().Equal(*(resp.Params), types.DefaultParams())
})
}
@ -101,7 +101,7 @@ func (kts *KeeperTestSuite) TestGrpcQueryBondByBondId() {
}
resp, err := kts.queryClient.GetBondById(context.Background(), test.req)
if !test.errResponse {
kts.Require().Nil(err)
kts.Require().NoError(err)
kts.Require().NotNil(resp.GetBond())
kts.Require().Equal(test.req.Id, resp.GetBond().GetId())
} else {
@ -148,7 +148,7 @@ func (kts *KeeperTestSuite) TestGrpcGetBondsByOwner() {
}
resp, err := kts.queryClient.GetBondsByOwner(context.Background(), test.req)
if !test.errResponse {
kts.Require().Nil(err)
kts.Require().NoError(err)
kts.Require().NotNil(resp.GetBonds())
kts.Require().Equal(test.noOfBonds, len(resp.GetBonds()))
} else {
@ -184,7 +184,7 @@ func (kts *KeeperTestSuite) TestGrpcGetModuleBalance() {
}
resp, err := kts.queryClient.GetBondModuleBalance(context.Background(), test.req)
if !test.errResponse {
kts.Require().Nil(err)
kts.Require().NoError(err)
kts.Require().NotNil(resp.GetBalance())
kts.Require().Equal(resp.GetBalance(), sdk.NewCoins(sdk.NewCoin(params.CoinUnit, math.NewInt(10))))
} else {

View File

@ -3,17 +3,20 @@ package integration_test
import (
"context"
runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1"
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1"
bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1"
"cosmossdk.io/core/appconfig"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
cmtprototypes "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil/integration"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/auth"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation"
@ -21,16 +24,22 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
auctionmodulev1 "git.vdb.to/cerc-io/laconicd/api/cerc/auction/module/v1"
bondmodulev1 "git.vdb.to/cerc-io/laconicd/api/cerc/bond/module/v1"
registrymodulev1 "git.vdb.to/cerc-io/laconicd/api/cerc/registry/module/v1"
"git.vdb.to/cerc-io/laconicd/app/params"
auctionTypes "git.vdb.to/cerc-io/laconicd/x/auction"
"git.vdb.to/cerc-io/laconicd/server"
"git.vdb.to/cerc-io/laconicd/utils"
auctiontypes "git.vdb.to/cerc-io/laconicd/x/auction"
auctionkeeper "git.vdb.to/cerc-io/laconicd/x/auction/keeper"
auctionmodule "git.vdb.to/cerc-io/laconicd/x/auction/module"
bondTypes "git.vdb.to/cerc-io/laconicd/x/bond"
bondtypes "git.vdb.to/cerc-io/laconicd/x/bond"
bondkeeper "git.vdb.to/cerc-io/laconicd/x/bond/keeper"
bondmodule "git.vdb.to/cerc-io/laconicd/x/bond/module"
registryTypes "git.vdb.to/cerc-io/laconicd/x/registry"
registrytypes "git.vdb.to/cerc-io/laconicd/x/registry"
registrykeeper "git.vdb.to/cerc-io/laconicd/x/registry/keeper"
registrymodule "git.vdb.to/cerc-io/laconicd/x/registry/module"
)
@ -51,108 +60,136 @@ type TestFixture struct {
}
func (tf *TestFixture) Setup() error {
keys := storetypes.NewKVStoreKeys(
authtypes.StoreKey, banktypes.StoreKey, auctionTypes.StoreKey, bondTypes.StoreKey, registryTypes.StoreKey,
logger := log.NewNopLogger()
authority := authtypes.NewModuleAddress(govtypes.ModuleName)
moduleAccPerms := []*authmodulev1.ModuleAccountPermission{
{Account: minttypes.ModuleName, Permissions: []string{authtypes.Minter}},
{Account: auctiontypes.ModuleName},
{Account: auctiontypes.AuctionBurnModuleAccountName},
{Account: bondtypes.ModuleName},
{Account: registrytypes.ModuleName},
{Account: registrytypes.RecordRentModuleAccountName},
{Account: registrytypes.AuthorityRentModuleAccountName},
}
moduleConfigs := []*appv1alpha1.ModuleConfig{
{
Name: runtime.ModuleName,
Config: appconfig.WrapAny(&runtimev1alpha1.Module{
AppName: "TestApp",
BeginBlockers: []string{
authtypes.ModuleName,
banktypes.ModuleName,
auctiontypes.ModuleName,
bondtypes.ModuleName,
registrytypes.ModuleName,
},
EndBlockers: []string{
auctiontypes.ModuleName,
bondtypes.ModuleName,
registrytypes.ModuleName,
banktypes.ModuleName,
authtypes.ModuleName,
},
InitGenesis: []string{
authtypes.ModuleName,
banktypes.ModuleName,
auctiontypes.ModuleName,
bondtypes.ModuleName,
registrytypes.ModuleName,
},
}),
},
{
Name: authtypes.ModuleName,
Config: appconfig.WrapAny(&authmodulev1.Module{
Bech32Prefix: params.Bech32PrefixAccAddr,
ModuleAccountPermissions: moduleAccPerms,
EnableUnorderedTransactions: true,
}),
},
{
Name: banktypes.ModuleName,
Config: appconfig.WrapAny(&bankmodulev1.Module{}),
},
{
Name: auctiontypes.ModuleName,
Config: appconfig.WrapAny(&auctionmodulev1.Module{}),
},
{
Name: bondtypes.ModuleName,
Config: appconfig.WrapAny(&bondmodulev1.Module{}),
},
{
Name: registrytypes.ModuleName,
Config: appconfig.WrapAny(&registrymodulev1.Module{}),
},
}
storeKeys := StoreKeyExtractor{}
appConfig := depinject.Configs(
appconfig.Compose(&appv1alpha1.Config{
Modules: moduleConfigs,
}),
depinject.Supply(
logger,
authority,
utils.NewAddressCodec(),
),
depinject.Provide(
server.NewGasService,
),
storeKeys.config(),
)
cdc := moduletestutil.MakeTestEncodingConfig(
auth.AppModuleBasic{},
auctionmodule.AppModule{},
bondmodule.AppModule{},
registrymodule.AppModule{},
).Codec
logger := log.NewNopLogger() // Use log.NewTestLogger(kts.T()) for help with debugging
cms := integration.CreateMultiStore(keys, logger)
var (
appBuilder *runtime.AppBuilder
cdc codec.Codec
accountKeeper authkeeper.AccountKeeper
bankKeeper bankkeeper.Keeper
auctionKeeper *auctionkeeper.Keeper
bondKeeper *bondkeeper.Keeper
registryKeeper registrykeeper.Keeper
)
if err := depinject.Inject(
appConfig,
&appBuilder,
&cdc,
&accountKeeper,
&bankKeeper,
&auctionKeeper,
&bondKeeper,
&registryKeeper,
); err != nil {
return err
}
cms := integration.CreateMultiStore(storeKeys, logger)
newCtx := sdk.NewContext(cms, cmtprototypes.Header{}, true, logger)
authority := authtypes.NewModuleAddress("gov")
maccPerms := map[string][]string{
minttypes.ModuleName: {authtypes.Minter},
auctionTypes.ModuleName: {},
auctionTypes.AuctionBurnModuleAccountName: {},
bondTypes.ModuleName: {},
registryTypes.ModuleName: {},
registryTypes.RecordRentModuleAccountName: {},
registryTypes.AuthorityRentModuleAccountName: {},
modules := map[string]appmodule.AppModule{
authtypes.ModuleName: auth.NewAppModule(cdc, accountKeeper, authsims.RandomGenesisAccounts, nil),
banktypes.ModuleName: bank.NewAppModule(cdc, bankKeeper, accountKeeper, nil),
auctiontypes.ModuleName: auctionmodule.NewAppModule(cdc, auctionKeeper),
bondtypes.ModuleName: bondmodule.NewAppModule(cdc, bondKeeper),
registrytypes.ModuleName: registrymodule.NewAppModule(cdc, registryKeeper),
}
accountKeeper := authkeeper.NewAccountKeeper(
cdc,
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
authtypes.ProtoBaseAccount,
maccPerms,
addresscodec.NewBech32Codec(params.Bech32PrefixAccAddr),
params.Bech32PrefixAccAddr,
authority.String(),
)
blockedAddresses := map[string]bool{
accountKeeper.GetAuthority(): false,
}
bankKeeper := bankkeeper.NewBaseKeeper(
cdc,
runtime.NewKVStoreService(keys[banktypes.StoreKey]),
accountKeeper,
blockedAddresses,
authority.String(),
log.NewNopLogger(),
)
auctionKeeper := auctionkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[auctionTypes.StoreKey]), accountKeeper, bankKeeper, authority.String())
bondKeeper := bondkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[bondTypes.StoreKey]), accountKeeper, bankKeeper, authority.String())
registryKeeper := registrykeeper.NewKeeper(
cdc,
runtime.NewKVStoreService(keys[registryTypes.StoreKey]),
accountKeeper,
bankKeeper,
bondKeeper,
auctionKeeper,
authority.String(),
)
authModule := auth.NewAppModule(cdc, accountKeeper, authsims.RandomGenesisAccounts, nil)
bankModule := bank.NewAppModule(cdc, bankKeeper, accountKeeper, nil)
auctionModule := auctionmodule.NewAppModule(cdc, auctionKeeper)
bondModule := bondmodule.NewAppModule(cdc, bondKeeper)
registryModule := registrymodule.NewAppModule(cdc, registryKeeper)
integrationApp := integration.NewIntegrationApp(newCtx, logger, keys, cdc, map[string]appmodule.AppModule{
authtypes.ModuleName: authModule,
banktypes.ModuleName: bankModule,
auctionTypes.ModuleName: auctionModule,
bondTypes.ModuleName: bondModule,
registryTypes.ModuleName: registryModule,
})
integrationApp := integration.NewIntegrationApp(newCtx, logger, storeKeys, cdc, modules)
sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context())
// Register MsgServer and QueryServer
auctionTypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), auctionkeeper.NewMsgServerImpl(auctionKeeper))
auctionTypes.RegisterQueryServer(integrationApp.QueryHelper(), auctionkeeper.NewQueryServerImpl(auctionKeeper))
auctiontypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), auctionkeeper.NewMsgServerImpl(auctionKeeper))
auctiontypes.RegisterQueryServer(integrationApp.QueryHelper(), auctionkeeper.NewQueryServerImpl(auctionKeeper))
bondtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), bondkeeper.NewMsgServerImpl(bondKeeper))
bondtypes.RegisterQueryServer(integrationApp.QueryHelper(), bondkeeper.NewQueryServerImpl(bondKeeper))
registrytypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), registrykeeper.NewMsgServerImpl(registryKeeper))
registrytypes.RegisterQueryServer(integrationApp.QueryHelper(), registrykeeper.NewQueryServerImpl(registryKeeper))
bondTypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), bondkeeper.NewMsgServerImpl(bondKeeper))
bondTypes.RegisterQueryServer(integrationApp.QueryHelper(), bondkeeper.NewQueryServerImpl(bondKeeper))
auctionKeeper.Params.Set(sdkCtx, auctiontypes.DefaultParams())
bondKeeper.Params.Set(sdkCtx, bondtypes.DefaultParams())
registryKeeper.Params.Set(sdkCtx, registrytypes.DefaultParams())
registryTypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), registrykeeper.NewMsgServerImpl(registryKeeper))
registryTypes.RegisterQueryServer(integrationApp.QueryHelper(), registrykeeper.NewQueryServerImpl(registryKeeper))
// set default params
if err := auctionKeeper.Params.Set(sdkCtx, auctionTypes.DefaultParams()); err != nil {
return err
}
if err := bondKeeper.Params.Set(sdkCtx, bondTypes.DefaultParams()); err != nil {
return err
}
if err := registryKeeper.Params.Set(sdkCtx, registryTypes.DefaultParams()); err != nil {
return err
}
tf.App = integrationApp
tf.SdkCtx, tf.cdc, tf.keys = sdkCtx, cdc, keys
tf.App, tf.SdkCtx, tf.cdc, tf.keys = integrationApp, sdkCtx, cdc, storeKeys
tf.AccountKeeper, tf.BankKeeper = accountKeeper, bankKeeper
tf.AuctionKeeper, tf.BondKeeper, tf.RegistryKeeper = auctionKeeper, bondKeeper, registryKeeper
@ -164,3 +201,22 @@ type BondDenomProvider struct{}
func (bdp BondDenomProvider) BondDenom(ctx context.Context) (string, error) {
return params.CoinUnit, nil
}
// store keys are uniquely constructed by ProvideKVStoreKey when building the app, so we must
// extract the keys like this rather than create new ones.
type StoreKeyExtractor map[string]*storetypes.KVStoreKey
func (ske *StoreKeyExtractor) ExtractKey(mkey depinject.ModuleKey, storeKey *storetypes.KVStoreKey) {
(*ske)[mkey.Name()] = storeKey
}
func (ske *StoreKeyExtractor) config() depinject.Config {
return depinject.Configs(
depinject.Supply(ske),
depinject.InvokeInModule(banktypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
depinject.InvokeInModule(authtypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
depinject.InvokeInModule(bondtypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
depinject.InvokeInModule(auctiontypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
depinject.InvokeInModule(registrytypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
)
}

View File

@ -6,7 +6,6 @@ import (
"cosmossdk.io/math"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"git.vdb.to/cerc-io/laconicd/app/params"
@ -27,18 +26,18 @@ type KeeperTestSuite struct {
func (kts *KeeperTestSuite) SetupTest() {
err := kts.TestFixture.Setup()
assert.Nil(kts.T(), err)
kts.Require().NoError(err)
// set default params
err = kts.RegistryKeeper.Params.Set(kts.SdkCtx, types.DefaultParams())
assert.Nil(kts.T(), err)
kts.Require().NoError(err)
qr := kts.App.QueryHelper()
kts.queryClient = types.NewQueryClient(qr)
// Create a bond
bond, err := kts.createBond()
assert.Nil(kts.T(), err)
kts.Require().NoError(err)
kts.bond = *bond
}

View File

@ -0,0 +1,260 @@
package system
import (
"fmt"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/tidwall/gjson"
"git.vdb.to/cerc-io/laconicd/x/auction"
)
const (
randomAuctionId = "randomAuctionId"
randomBidderAddress = "randomBidderAddress"
randomOwnerAddress = "randomOwnerAddress"
)
func (s *auctionSuite) TestGRPCQueryParams() {
reqURL := s.endpointURL("auction/v1/params")
s.Run("valid request to get auction params", func() {
require := s.Require()
resp, err := testutil.GetRequest(reqURL)
require.NoError(err)
var params auction.QueryParamsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &params), string(resp))
require.Equal(auction.DefaultParams(), *params.GetParams())
})
}
func (s *auctionSuite) TestGRPCGetAllAuctions() {
reqURL := s.endpointURL("auction/v1/auctions")
s.createAuction()
testCases := []struct {
name string
url string
errmsg string
}{
{
"invalid request",
reqURL + "-asdasd",
"Not Implemented",
},
{
"valid request",
reqURL,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var auctions auction.QueryAuctionsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &auctions), string(resp))
require.NotEmpty(auctions.Auctions.Auctions)
}
})
}
}
func (s *auctionSuite) TestGRPCGetAuction() {
reqURL := s.endpointURL("auction/v1/auctions/%s")
auctionId := s.createAuction()
s.createBid(auctionId)
testCases := []struct {
name string
url string
exists bool
}{
{
"nonexistent auction",
fmt.Sprintf(reqURL, randomAuctionId),
false,
},
{
"valid request",
fmt.Sprintf(reqURL, auctionId),
true,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.exists {
var auction auction.QueryGetAuctionResponse
require.NoError(s.codec.UnmarshalJSON(resp, &auction), string(resp))
require.Equal(auctionId, auction.Auction.Id)
} else {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, "not found", tc.url)
}
})
}
}
func (s *auctionSuite) TestGRPCGetBids() {
reqURL := s.endpointURL("auction/v1/bids/%s")
auctionId := s.createAuction()
s.createBid(auctionId)
testCases := []struct {
name string
url string
exists bool
}{
{
"nonexistent auction",
fmt.Sprintf(reqURL, randomAuctionId),
false,
},
{
"valid request",
fmt.Sprintf(reqURL, auctionId),
true,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
var bids auction.QueryGetBidsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &bids), string(resp))
if tc.exists {
require.Equal(auctionId, bids.Bids[0].AuctionId)
} else {
require.Empty(bids.Bids)
}
})
}
}
func (s *auctionSuite) TestGRPCGetBid() {
reqURL := s.endpointURL("auction/v1/bids/%s/%s")
bidderAddress := s.cli().GetKeyAddr(s.bidderAccount)
auctionId := s.createAuction()
s.createBid(auctionId)
testCases := []struct {
name string
url string
exists bool
}{
{
"nonexistent auction",
fmt.Sprintf(reqURL, randomAuctionId, bidderAddress),
false,
},
{
"nonexistent bidder",
fmt.Sprintf(reqURL, auctionId, randomBidderAddress),
false,
},
{
"valid request",
fmt.Sprintf(reqURL, auctionId, bidderAddress),
true,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.exists {
var bid auction.QueryGetBidResponse
require.NoError(s.codec.UnmarshalJSON(resp, &bid), string(resp))
require.Equal(auctionId, bid.Bid.AuctionId)
} else {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, "not found", tc.url)
}
})
}
}
func (s *auctionSuite) TestGRPCGetAuctionsByOwner() {
reqURL := s.endpointURL("auction/v1/by-owner/%s")
ownerAddress := s.cli().GetKeyAddr(s.ownerAccount)
auctionId := s.createAuction()
s.createBid(auctionId)
testCases := []struct {
name string
url string
exists bool
}{
{
"nonexistent owner",
fmt.Sprintf(reqURL, randomOwnerAddress),
false,
},
{
"valid request",
fmt.Sprintf(reqURL, ownerAddress),
true,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
var auctions auction.QueryAuctionsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &auctions), string(resp))
if tc.exists {
require.Equal(auctionId, auctions.Auctions.Auctions[0].Id)
} else {
require.Empty(auctions.Auctions.Auctions)
}
})
}
}
func (s *auctionSuite) TestGRPCQueryBalance() {
reqURL := s.endpointURL("auction/v1/balance")
s.Run("valid request", func() {
require := s.Require()
auctionId := s.createAuction()
s.createBid(auctionId)
resp, err := testutil.GetRequest(reqURL)
require.NoError(err)
var response auction.QueryGetAuctionModuleBalanceResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
require.NotEmpty(response.GetBalance())
})
}

View File

@ -0,0 +1,162 @@
package system
import (
"fmt"
"os"
"path/filepath"
"testing"
"git.vdb.to/cerc-io/laconicd/x/auction"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/stretchr/testify/suite"
)
const (
sampleCommitTime = "90s"
sampleRevealTime = "5s"
placeholderAuctionId = "placeholder_auction_id"
)
type auctionSuite struct {
testSuite
ownerAccount string
bidderAccount string
defaultAuctionId string
}
func TestAuction(t *testing.T) {
suite.Run(t, new(auctionSuite))
}
func (s *auctionSuite) SetupTest() {
s.resetChain()
s.ownerAccount = s.account(0)
s.bidderAccount = s.account(1)
Sut.StartChain(s.T())
}
func (s *auctionSuite) TearDownTest() {
s.cleanupBidFiles()
}
func (s *auctionSuite) TestQueryList() {
sr := s.Require()
testCases := []struct {
name string
createAuction bool
}{
{
"when no auctions exist",
false,
},
{
"after creating an auction",
true,
},
}
for _, test := range testCases {
s.Run(test.name, func() {
if test.createAuction {
s.createAuction()
}
out := s.cli().CustomQuery("q", "auction", "list")
var response auction.QueryAuctionsResponse
sr.NoError(s.codec.UnmarshalJSON([]byte(out), &response), out)
if test.createAuction {
sr.NotEmpty(response.Auctions.Auctions)
}
})
}
}
func (s *auctionSuite) TestTxCommitBid() {
auctionId := s.createAuction()
testCases := []struct {
name string
args []string
err bool
}{
{
"with missing args",
[]string{auctionId},
true,
},
{
"with zero bid",
[]string{auctionId, ("0" + s.bondDenom)},
true,
},
{
"with invalid auction",
[]string{"fake", ("200" + s.bondDenom)},
true,
},
{
"with valid args",
[]string{auctionId, ("200" + s.bondDenom)},
false,
},
}
for _, test := range testCases {
s.Run(test.name, func() {
cmd := []string{
"tx", "auction", "commit-bid",
}
cmd = append(cmd, test.args...)
s.runTx(cmd, test.err)
})
}
}
func (s *auctionSuite) createAuction() string {
require := s.Require()
cmd := []string{
"tx", "auction", "create",
sampleCommitTime,
sampleRevealTime,
"10" + s.bondDenom,
"10" + s.bondDenom,
"--kind", auction.AuctionKindVickrey,
"--minimum-bid", "100" + s.bondDenom,
"--max-price", "0" + s.bondDenom,
"--num-providers", "0",
}
s.runTx(cmd, false)
out := s.cli().CustomQuery("q", "auction", "list")
var response auction.QueryAuctionsResponse
require.NoError(s.codec.UnmarshalJSON([]byte(out), &response), out)
return response.Auctions.Auctions[0].Id
}
func (s *auctionSuite) createBid(auctionId string) {
cmd := []string{
"tx", "auction", "commit-bid",
auctionId,
"200" + s.bondDenom,
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.bidderAccount),
}
s.runTx(cmd, false)
}
func (s *auctionSuite) cleanupBidFiles() {
matches, err := filepath.Glob(fmt.Sprintf("%s-*.json", s.bidderAccount))
if err != nil {
s.T().Errorf("Error matching bidder files: %v\n", err)
}
for _, match := range matches {
err := os.Remove(match)
if err != nil {
s.T().Errorf("Error removing bidder file: %v\n", err)
}
}
}

View File

@ -0,0 +1,168 @@
package system
import (
"fmt"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
"github.com/cosmos/cosmos-sdk/testutil"
"git.vdb.to/cerc-io/laconicd/x/bond"
)
func (s *bondSuite) TestGRPCGetParams() {
reqURL := s.endpointURL("bond/v1/params")
var params bond.QueryParamsResponse
resp, err := testutil.GetRequest(reqURL)
require.NoError(s.T(), err)
require.NoError(s.T(), s.codec.UnmarshalJSON(resp, &params))
require.Equal(s.T(), bond.DefaultParams().MaxBondAmount, params.GetParams().MaxBondAmount)
}
func (s *bondSuite) TestGRPCGetBond() {
reqURL := s.endpointURL("bond/v1/bonds")
testcases := []struct {
name string
url string
errmsg string
prerun func()
}{
{
"invalid request with headers",
reqURL + "asdasdas",
"Not Implemented",
func() {},
},
{
"valid request",
reqURL,
"",
func() { s.createBond() },
},
}
for _, tc := range testcases {
s.Run(tc.name, func() {
require := s.Require()
tc.prerun()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, string(resp))
} else {
var bonds bond.QueryBondsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &bonds), string(resp))
require.NotEmpty(bonds.GetBonds())
}
})
}
}
func (s *bondSuite) TestGRPCGetBondsByOwner() {
reqURL := s.endpointURL("bond/v1/by-owner/%s")
accountAddress := s.cli().GetKeyAddr(s.account(0))
testcases := []struct {
name string
url string
err bool
prerun func()
}{
{
"empty list",
fmt.Sprintf(reqURL, "asdasd"),
true,
func() {},
},
{
"valid request",
fmt.Sprintf(reqURL, accountAddress),
false,
func() { s.createBond() },
},
}
for _, tc := range testcases {
s.Run(tc.name, func() {
require := s.Require()
tc.prerun()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
var bonds bond.QueryGetBondsByOwnerResponse
require.NoError(s.codec.UnmarshalJSON(resp, &bonds))
if tc.err {
require.Empty(bonds.GetBonds())
} else {
bondsList := bonds.GetBonds()
require.NotEmpty(bondsList)
require.Equal(accountAddress, bondsList[0].GetOwner())
}
})
}
}
func (s *bondSuite) TestGRPCGetBondById() {
reqURL := s.endpointURL("bond/v1/bonds/%s")
testcases := []struct {
name string
url string
err bool
prerun func() string
}{
{
"invalid request",
reqURL,
true,
func() string { return "asdadad" },
},
{
"valid request",
reqURL,
false,
func() string { return s.createBond() },
},
}
for _, tc := range testcases {
s.Run(tc.name, func() {
require := s.Require()
bondId := tc.prerun()
tc.url = fmt.Sprintf(reqURL, bondId)
var bond bond.QueryGetBondByIdResponse
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.err {
require.Error(s.codec.UnmarshalJSON(resp, &bond))
} else {
require.NoError(s.codec.UnmarshalJSON(resp, &bond))
require.Equal(bondId, bond.GetBond().GetId())
}
})
}
}
func (s *bondSuite) TestGRPCGetBondModuleBalance() {
reqURL := s.endpointURL("bond/v1/balance")
s.createBond()
require := s.Require()
resp, err := testutil.GetRequest(reqURL)
require.NoError(err)
var response bond.QueryGetBondModuleBalanceResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response))
require.False(response.GetBalance().IsZero())
}

96
tests/system/bond_test.go Normal file
View File

@ -0,0 +1,96 @@
package system
import (
"encoding/json"
"testing"
"git.vdb.to/cerc-io/laconicd/x/bond"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
)
type bondSuite struct {
testSuite
}
func TestBonds(t *testing.T) {
suite.Run(t, new(bondSuite))
}
func (s *bondSuite) SetupTest() {
s.resetChain()
Sut.StartChain(s.T())
}
func (s *bondSuite) TestQueryList() {
testCases := []struct {
name string
args []string
prerun func()
}{
{
"create and get bond lists",
nil,
func() { s.createBond() },
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
tc.prerun()
out := s.cli().CustomQuery("q", "bond", "list")
s.Require().NotEmpty(gjson.Get(out, "bonds").Array(), out)
})
}
}
func (s *bondSuite) TestTxCreateBond() {
testCases := []struct {
name string
args []string
err bool
}{
{
"without deposit",
[]string{},
true,
},
{
"create bond",
[]string{
"10" + s.bondDenom,
},
false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
cmd := append([]string{"tx", "bond", "create"}, tc.args...)
s.runTx(cmd, tc.err)
})
}
}
func (s *bondSuite) createBond() string {
return createBond(&s.testSuite)
}
// free function for use in other suites
func createBond(s *testSuite) string {
require := s.Require()
cli := s.cli()
cmd := []string{
"tx", "bond", "create", ("1000000" + s.bondDenom),
}
s.runTx(cmd, false)
raw := cli.CustomQuery("q", "bond", "list")
var queryResponse bond.QueryBondsResponse
require.NoError(json.Unmarshal([]byte(raw), &queryResponse))
bonds := queryResponse.GetBonds()
require.NotEmpty(bonds)
return bonds[0].GetId()
}

55
tests/system/main_test.go Normal file
View File

@ -0,0 +1,55 @@
package system
import (
"flag"
"os"
"testing"
"time"
"cosmossdk.io/systemtests"
)
var (
Sut *systemtests.SystemUnderTest
verbose bool
nodesCount = flag.Int("nodes", 4, "number of nodes in the cluster")
blockTime = 3 * time.Second // expected time between blocks
// txTimeout = 10 * time.Second // timeout for transactions to be included in a block
txTimeout = 10 * time.Minute // timeout for transactions to be included in a block
workDir string
)
func TestMain(m *testing.M) {
// sdk systemtests expects this to be set for v2 server/runtime
os.Setenv("COSMOS_BUILD_OPTIONS", "v2")
flag.BoolVar(&verbose, "verbose", false, "verbose output")
flag.Parse()
dir, err := os.Getwd()
if err != nil {
panic(err)
}
workDir = dir
if verbose {
println("Work dir: ", workDir)
}
Sut = systemtests.NewSystemUnderTest("laconicd", verbose, *nodesCount, blockTime)
// Sut.SetTestnetInitializer(initer)
Sut.SetupChain() // setup chain and keyring
// run tests
exitCode := m.Run()
// postprocess
Sut.StopChain()
if verbose || exitCode != 0 {
Sut.PrintBuffer()
}
os.Exit(exitCode)
}

452
tests/system/nitro_test.go Normal file
View File

@ -0,0 +1,452 @@
package system
import (
"encoding/json"
"math/big"
"os"
"os/signal"
"syscall"
"testing"
nitrocrypto "github.com/statechannels/go-nitro/crypto"
chainutils "github.com/statechannels/go-nitro/node/chain"
"github.com/statechannels/go-nitro/node/query"
"github.com/statechannels/go-nitro/node_test"
nitrotypes "github.com/statechannels/go-nitro/types"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
systest "cosmossdk.io/systemtests"
)
var (
ethChainPort = "8546"
ethChainUrl = "ws://127.0.0.1:8546"
)
type nitroSuite struct {
testSuite
tokenDenom string
ethChainOpts *node_test.LocalChainOptions
alice, bob nitrocrypto.Credential
intermediaries []nitrocrypto.Credential
}
func TestNitro(t *testing.T) {
suite.Run(t, new(nitroSuite))
}
func (s *nitroSuite) SetupSuite() {
s.testSuite.SetupSuite()
s.tokenDenom = "eth"
// Set up Ethereum test chain
// TODO: use anvil for better self-containment
ethChainOpts := node_test.ExternalHardhatChainOptions(s.T())
ethChainOpts.ChainUrl = ethChainUrl
aliceKey := ethChainOpts.ChainPks[0]
bobKey := ethChainOpts.ChainPks[1]
s.ethChainOpts = ethChainOpts
ethChain, err := node_test.ConnectLocalChain(ethChainOpts, nitrotypes.BytesFromHex(aliceKey))
s.Require().NoError(err)
s.T().Cleanup(func() { s.NoError(ethChain.Close()) })
s.alice = nitrocrypto.NewSimpleCredential(nitrotypes.BytesFromHex(aliceKey))
s.bob = nitrocrypto.NewSimpleCredential(nitrotypes.BytesFromHex(bobKey))
for _, pk := range ethChainOpts.ChainPks {
creds := nitrocrypto.NewSimpleCredential(nitrotypes.BytesFromHex(pk))
s.intermediaries = append(s.intermediaries, creds)
}
s.configureEthChain()
// TODO when using intermediaries, set more validator node pks here
s.setValidatorNitroKeys(s.ethChainOpts.ChainPks[1])
}
func (s *nitroSuite) SetupTest() {
s.resetChain()
s.setClientKeys(s.ethChainOpts.ChainPks[0])
Sut.StartChain(s.T())
}
func (s *nitroSuite) SetupSubTest() { s.SetupTest() }
func (s *nitroSuite) configureEthChain() {
cas, err := chainutils.LoadEnvContractAddresses()
s.Require().NoError(err)
cmds := [][]string{
{"config", "set", "app", "nitro.eth-na-address", "'" + cas.NaAddress.String() + "'"},
{"config", "set", "app", "nitro.eth-vpa-address", "'" + cas.VpaAddress.String() + "'"},
{"config", "set", "app", "nitro.eth-ca-address", "'" + cas.CaAddress.String() + "'"},
{"config", "set", "app", "nitro.eth-url", ethChainUrl},
}
_ = Sut.ForEachNodeExecAndWait(s.T(), cmds...)
for _, cmd := range cmds {
cmd = append(cmd, "--home", s.clientHome())
_ = systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), cmd...)
}
}
func (s *nitroSuite) TestTxOpenChannel() {
testCases := []struct {
name string
args []string
err bool
}{
{
"missing args",
[]string{},
true,
},
{
"missing amount",
[]string{s.bob.AsParticipant().String()},
true,
},
{
"valid request",
[]string{s.bob.AsParticipant().String(), "10" + s.tokenDenom},
false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
cmd := append([]string{"tx", "nitro", "open-channel"}, tc.args...)
s.runTx(cmd, tc.err)
})
}
}
func (s *nitroSuite) TestTxCloseChannel() {
testCases := []struct {
name string
args []string
err bool
setup func() []string
}{
{
"missing args",
[]string{},
true,
nil,
},
{
"invalid channel",
[]string{"invalid-channel-id"},
true,
nil,
},
{
"valid channel",
nil,
false,
func() []string { return s.setupLedgerChannel("10") },
},
// {
// "valid channel with challenge",
// []string{"--challenge"},
// false,
// func() []string { return s.setupLedgerChannel("10") },
// },
}
for _, tc := range testCases {
s.Run(tc.name, func() {
cmd := []string{"tx", "nitro", "close-channel"}
if tc.setup != nil {
cmd = append(cmd, tc.setup()...)
}
cmd = append(cmd, tc.args...)
s.runTx(cmd, tc.err)
})
}
}
func (s *nitroSuite) TestTxOpenPaymentChannel() {
testCases := []struct {
name string
args []string
err bool
setup func() []string
}{
{
"missing args",
[]string{},
true,
nil,
},
{
"missing amount",
[]string{s.bob.AsParticipant().String()},
true,
nil,
},
{
"zero hops",
[]string{s.bob.AsParticipant().String(), "10" + s.tokenDenom},
false,
func() []string { s.setupLedgerChannel("20"); return nil },
},
// { // TODO open n-hop ledger channels
// "one hop",
// []string{"10" + s.tokenDenom, s.bob.AsParticipant().String(), s.intermediaries[0].String()},
// false,
// setupLedgerChannelForPaymentChannel,
// },
// { // TODO use a different counterparty, cannot have duplicate channels
// "direct channel with challenge duration",
// []string{"10" + s.tokenDenom, s.bob.AsParticipant().String(), "--challenge-duration", "100"},
// false,
// setupLedgerChannelForPaymentChannel,
// },
}
for _, tc := range testCases {
s.Run(tc.name, func() {
cmd := []string{"tx", "nitro", "open-payment-channel"}
if tc.setup != nil {
tc.setup()
}
cmd = append(cmd, tc.args...)
s.runTx(cmd, tc.err)
})
}
}
func (s *nitroSuite) TestTxClosePaymentChannel() {
testCases := []struct {
name string
args []string
err bool
setup func() []string
}{
{
"missing args",
[]string{},
true,
nil,
},
// { // TODO use different counterparty
// "invalid channel ID",
// []string{"invalid-channel-id"},
// true,
// setupLedgerChannel,
// },
{
"zero hops",
nil,
false,
func() []string {
s.setupLedgerChannel("20")
return s.setupPaymentChannel("10")
},
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
cmd := []string{"tx", "nitro", "close-payment-channel"}
if tc.setup != nil {
cmd = append(cmd, tc.setup()...)
}
cmd = append(cmd, tc.args...)
s.runTx(cmd, tc.err)
})
}
}
func (s *nitroSuite) TestTxPay() {
testCases := []struct {
name string
args []string
error bool
setup func() []string
paid *big.Int
}{
{
name: "missing args",
error: true,
},
{
name: "invalid channel ID",
args: []string{"invalid-channel-id", "10" + s.tokenDenom},
error: true,
},
{
name: "invalid amount format",
args: []string{"0x0102030000000000000000000000000000000000000000000000000000000000", "invalid-amount"},
error: true,
},
{
name: "nonexistent channel",
args: []string{"0x0102030000000000000000000000000000000000000000000000000000000000", "5" + s.tokenDenom},
error: true,
},
{
name: "valid payment",
args: []string{"10" + s.tokenDenom},
setup: func() []string {
s.setupLedgerChannel("100")
return s.setupPaymentChannel("50")
},
paid: big.NewInt(10),
},
// {
// "insufficient funds",
// []string{"10" + s.tokenDenom},
// false,
// func() []string {
// s.setupLedgerChannel("100")
// return s.setupPaymentChannel("50")
// },
// },
}
for _, tc := range testCases {
s.Run(tc.name, func() {
var channelId string
cmd := []string{"tx", "nitro", "pay"}
if tc.setup != nil {
setupArgs := tc.setup()
cmd = append(cmd, setupArgs...)
if !tc.error && len(setupArgs) > 0 {
channelId = setupArgs[0]
}
}
cmd = append(cmd, tc.args...)
s.runTx(cmd, tc.error)
// Check balance change for successful payments
if channelId != "" {
balance := s.getChannelBalance(channelId)
s.Require().Equal(tc.paid, balance.PaidSoFar.ToInt())
}
})
}
}
func (s *nitroSuite) TestFullPaymentFlow() {
s.Run("complete payment lifecycle", func() {
// Open ledger and payment channels
ledgerChannel := s.setupLedgerChannel("100")[0]
paymentChannel := s.setupPaymentChannel("50")[0]
// Verify initial balance
s.Require().Equal("0", s.getChannelBalance(paymentChannel).PaidSoFar.ToInt().String())
// Make payments: 5 + 10 + 15 = 30
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "5" + s.tokenDenom}, false)
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "10" + s.tokenDenom}, false)
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "15" + s.tokenDenom}, false)
// Verify final balance
finalBalance := s.getChannelBalance(paymentChannel)
s.Require().Equal("30", finalBalance.PaidSoFar.ToInt().String())
s.Require().Equal("20", finalBalance.RemainingFunds.ToInt().String())
// Close channels in correct order
s.runTx([]string{"tx", "nitro", "close-payment-channel", paymentChannel}, false)
s.runTx([]string{"tx", "nitro", "close-channel", ledgerChannel}, false)
})
s.Run("insufficient funds scenario", func() {
s.setupLedgerChannel("20")
paymentChannel := s.setupPaymentChannel("10")[0]
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "5" + s.tokenDenom}, false)
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "10" + s.tokenDenom}, true) // insufficient funds
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "5" + s.tokenDenom}, false)
// Verify all funds used
balance := s.getChannelBalance(paymentChannel)
s.Require().Equal("10", balance.PaidSoFar.ToInt().String())
s.Require().Equal("0", balance.RemainingFunds.ToInt().String())
})
s.Run("invalid closure order", func() {
// Open both channels
ledgerChannel := s.setupLedgerChannel("50")[0]
paymentChannel := s.setupPaymentChannel("30")[0]
// Make a payment
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "10" + s.tokenDenom}, false)
// Try to close ledger channel before payment channel - should fail
s.runTx([]string{"tx", "nitro", "close-channel", ledgerChannel}, true)
// Correct order should work
s.runTx([]string{"tx", "nitro", "close-payment-channel", paymentChannel}, false)
s.runTx([]string{"tx", "nitro", "close-channel", ledgerChannel}, false)
})
}
// setupLedgerChannel creates a ledger channel and returns the channel ID as args
func (s *nitroSuite) setupLedgerChannel(amount string) []string {
openCmd := []string{"tx", "nitro", "open-channel", s.bob.AsParticipant().String(), amount + s.tokenDenom}
txhash := s.runTx(openCmd, false)
return []string{s.getChannelIdFromTx(txhash)}
}
// setupPaymentChannel creates a ledger channel and payment channel, returns payment channel ID as args
func (s *nitroSuite) setupPaymentChannel(amount string) []string {
openPcCmd := []string{"tx", "nitro", "open-payment-channel", s.bob.AsParticipant().String(), amount + s.tokenDenom}
txhash := s.runTx(openPcCmd, false)
return []string{s.getChannelIdFromTx(txhash)}
}
// getChannelIdFromTx opens a channel and extracts the channel ID from the transaction result
func (s *nitroSuite) getChannelIdFromTx(txHash string) string {
txDetails := s.cli().CustomQuery("query", "tx", txHash)
// find channel_id in any event's attributes
channelId := gjson.Get(txDetails, "events.#.attributes.#(key==\"channel_id\").value|0")
s.Require().True(channelId.Exists(), "channel_id not found in transaction events")
return channelId.String()
}
func (s *nitroSuite) getChannelBalance(channelId string) query.PaymentChannelBalance {
channelInfo := s.cli().CustomQuery(
s.withNitroQueryFlags("q", "nitro", "get-channel", "--payment", channelId)...,
)
var info query.PaymentChannelInfo
err := json.Unmarshal([]byte(channelInfo), &info)
s.Require().NoError(err, "could not parse PaymentChannelInfo: %s", channelInfo)
return info.Balance
}
func (s *nitroSuite) withNitroQueryFlags(cmd ...string) []string {
return append(cmd, "--home", s.clientHome())
}
func (s *nitroSuite) Test_Hang() {
hang := os.Getenv("HANG")
if hang == "" {
s.T().Skip("Placeholder to run the validator nodes and hang for manual testing")
}
if hang == "ledger" || hang == "payment" {
s.setupLedgerChannel("20")
}
if hang == "payment" {
s.setupPaymentChannel("10")
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
}

View File

@ -0,0 +1,389 @@
package system
import (
"fmt"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/tidwall/gjson"
"git.vdb.to/cerc-io/laconicd/x/registry"
)
const badPath = "/asdasd"
func (s *registrySuite) TestGRPCQueryParams() {
reqURL := s.endpointURL("registry/v1/params")
testCases := []struct {
name string
url string
errmsg string
}{
{
"invalid request",
reqURL + badPath,
"Not Implemented",
},
{
"valid request",
reqURL,
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryParamsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
params := registry.DefaultParams()
s.updateParams(&params)
require.Equal(params.String(), response.GetParams().String())
}
})
}
}
func (s *registrySuite) TestGRPCQueryWhoIs() {
reqURL := s.endpointURL("registry/v1/whois/%s")
authorityName := "QueryWhoIS"
testCases := []struct {
name string
url string
errmsg string
prerun func(string)
}{
{
"invalid url",
reqURL + badPath,
"Not Implemented",
func(string) {},
},
{
"valid request",
reqURL,
"",
func(name string) { s.reserveName(name) },
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
tc.prerun(authorityName)
url := fmt.Sprintf(tc.url, authorityName)
resp, err := testutil.GetRequest(url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryWhoisResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
require.Equal(registry.AuthorityActive, response.GetNameAuthority().Status)
}
})
}
}
func (s *registrySuite) TestGRPCQueryLookup() {
reqURL := s.endpointURL("registry/v1/lookup")
authorityName := "QueryLookUp"
s.createNameRecord(authorityName)
testCases := []struct {
name string
url string
errmsg string
}{
{
"invalid url",
reqURL + badPath,
"Not Implemented",
},
{
"nonexistent LRN",
fmt.Sprintf(reqURL+"?lrn=lrn://%s/", "nonexistent"),
"not found",
},
{
"valid request",
fmt.Sprintf(reqURL+"?lrn=lrn://%s/", authorityName),
"",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryLookupLrnResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
require.NotEmpty(response.Name.Latest.Id)
}
})
}
}
func (s *registrySuite) TestGRPCQueryListRecords() {
reqURL := s.endpointURL("registry/v1/records")
bondId := s.bondId
testCases := []struct {
name string
url string
errmsg string
prerun func(string)
}{
{
"invalid url",
reqURL + badPath,
"not found",
func(string) {},
},
{
"valid request",
reqURL,
"",
func(id string) { s.createRecord(id) },
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
tc.prerun(bondId)
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryRecordsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
require.NotEmpty(response.GetRecords())
require.Equal(bondId, response.GetRecords()[0].GetBondId())
}
})
}
}
func (s *registrySuite) TestGRPCQueryGetRecordById() {
reqURL := s.endpointURL("registry/v1/records/%s")
bondId := s.bondId
testCases := []struct {
name string
url string
errmsg string
prerun func(string) string
}{
{
"invalid url",
reqURL + badPath,
"not found",
func(string) string { return "" },
},
{
"valid request",
reqURL,
"",
func(id string) string {
require := s.Require()
// create a record and get the id
s.createRecord(id)
out := s.cli().CustomQuery("q", "registry", "list")
var response registry.QueryRecordsResponse
require.NoError(s.codec.UnmarshalJSON([]byte(out), &response), out)
require.NotEmpty(response.GetRecords())
return response.GetRecords()[0].Id
},
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
recordId := tc.prerun(bondId)
url := fmt.Sprintf(tc.url, recordId)
resp, err := testutil.GetRequest(url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryGetRecordResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
record := response.GetRecord()
require.NotEmpty(record.GetId())
require.Equal(recordId, record.GetId())
}
})
}
}
func (s *registrySuite) TestGRPCQueryGetRecordByBondId() {
reqURL := s.endpointURL("registry/v1/records-by-bond-id/%s")
bondId := s.bondId
testCases := []struct {
name string
url string
errmsg string
prerun func(string)
}{
{
"invalid url",
reqURL + badPath,
"Not Implemented",
func(string) {},
},
{
"valid request",
reqURL,
"",
func(id string) { s.createRecord(id) },
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
tc.prerun(bondId)
url := fmt.Sprintf(tc.url, bondId)
resp, err := testutil.GetRequest(url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryGetRecordsByBondIdResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
records := response.GetRecords()
require.NotEmpty(records)
require.Equal(bondId, records[0].GetBondId())
}
})
}
}
func (s *registrySuite) TestGRPCQueryGetRegistryModuleBalance() {
reqURL := s.endpointURL("registry/v1/balance")
bondId := s.bondId
testCases := []struct {
name string
url string
errmsg string
prerun func(string)
}{
{
"invalid url",
reqURL + badPath,
"Not Implemented",
func(string) {},
},
{
"valid request",
reqURL,
"",
func(id string) { s.createRecord(id) },
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
tc.prerun(bondId)
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryGetRegistryModuleBalanceResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
require.NotEmpty(response.GetBalances())
}
})
}
}
func (s *registrySuite) TestGRPCQueryNamesList() {
reqURL := s.endpointURL("registry/v1/names")
testCases := []struct {
name string
url string
errmsg string
prerun func(string)
}{
{
"invalid url",
reqURL + badPath,
"Not Implemented",
func(string) {},
},
{
"valid request",
reqURL,
"",
func(name string) { s.createNameRecord(name) },
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
require := s.Require()
tc.prerun("ListNameRecords")
resp, err := testutil.GetRequest(tc.url)
require.NoError(err)
if tc.errmsg != "" {
message := gjson.Get(string(resp), "message").String()
require.Contains(message, tc.errmsg, tc.url)
} else {
var response registry.QueryNameRecordsResponse
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
require.NotEmpty(response.GetNames())
}
})
}
}

View File

@ -0,0 +1,157 @@
package system
import (
"fmt"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
// accounts "github.com/cosmos/cosmos-sdk/x/accounts/v1"
"cosmossdk.io/math"
systest "cosmossdk.io/systemtests"
sdk "github.com/cosmos/cosmos-sdk/types"
// nitro "git.vdb.to/cerc-io/laconicd/x/nitro/v1"
"git.vdb.to/cerc-io/laconicd/x/registry"
)
var recordFilePath = "../data/examples/service_provider_example.yml"
func init() {
var err error
if recordFilePath, err = filepath.Abs(recordFilePath); err != nil {
panic(err)
}
if _, err = os.Stat(recordFilePath); err != nil {
panic(err)
}
}
type registrySuite struct {
testSuite
accountAddress string
bondId string
}
func TestRegistry(t *testing.T) {
suite.Run(t, new(registrySuite))
}
func (s *registrySuite) SetupTest() {
s.resetChain()
Sut.ModifyGenesisJSON(s.T(), UpdateGenesisRegistry(s))
s.accountAddress = s.cli().GetKeyAddr(s.account(0))
Sut.StartChain(s.T())
s.bondId = createBond(&s.testSuite)
}
func (s *registrySuite) TestTxSetRecord() {
testCases := []struct {
name string
args []string
err bool
}{
{
"request with invalid payload file arg",
[]string{"bad-file", s.bondId},
true,
},
{
"success",
[]string{recordFilePath, s.bondId},
false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
cmd := []string{"tx", "registry", "set"}
cmd = append(cmd, tc.args...)
s.runTx(cmd, tc.err)
})
}
}
func (s *testSuite) updateParams(params *registry.Params) {
params.RecordRent = sdk.NewCoin(s.bondDenom, math.NewInt(1000))
params.RecordRentDuration = 10 * time.Second
params.AuthorityRent = sdk.NewCoin(s.bondDenom, math.NewInt(1000))
params.AuthorityGracePeriod = 10 * time.Second
params.AuthorityAuctionCommitFee = sdk.NewCoin(s.bondDenom, math.NewInt(100))
params.AuthorityAuctionRevealFee = sdk.NewCoin(s.bondDenom, math.NewInt(100))
params.AuthorityAuctionMinimumBid = sdk.NewCoin(s.bondDenom, math.NewInt(500))
}
func UpdateGenesisRegistry(s *registrySuite) systest.GenesisMutator {
require := s.Require()
return func(genesis []byte) []byte {
var regState registry.GenesisState
raw := gjson.Get(string(genesis), "app_state.registry").String()
require.NoError(s.codec.UnmarshalJSON([]byte(raw), &regState))
s.updateParams(&regState.Params)
regStateBz, err := s.codec.MarshalJSON(&regState)
require.NoError(err)
genesis, err = sjson.SetRawBytes(genesis, "app_state.registry", regStateBz)
require.NoError(err)
// // add consensus-controlled module account(s)
// bondinitmsg, err := codectypes.NewAnyWithValue(&nitro.MsgInitAccount{})
// require.NoError(s.T(), err)
// var accountsState accounts.GenesisState
// raw = gjson.Get(string(genesis), "app_state.accounts").String()
// require.NoError(s.T(), cdc.UnmarshalJSON([]byte(raw), &accountsState))
// accountsState.InitAccountMsgs = []*accounts.MsgInit{
// {
// Sender: accountAddress,
// AccountType: "nitro",
// Message: bondinitmsg,
// },
// }
// accountsStateBz, err := cdc.MarshalJSON(&accountsState)
// require.NoError(s.T(), err)
// genesis, err = sjson.SetRawBytes(genesis, "app_state.accounts", accountsStateBz)
// require.NoError(s.T(), err)
return genesis
}
}
func (s *registrySuite) reserveName(authorityName string) {
cmd := []string{
"tx", "registry", "reserve-authority", authorityName, s.accountAddress,
}
s.runTx(cmd, false)
}
func (s *registrySuite) createNameRecord(authorityName string) {
for _, cmd := range [][]string{
// reserve name authority
{"tx", "registry", "reserve-authority", authorityName, s.accountAddress},
// add bond-id to name authority
{"tx", "registry", "authority-bond", authorityName, s.bondId},
// create actual name record
{"tx", "registry", "set-name", fmt.Sprintf("lrn://%s/", authorityName), "test_hello_cid"},
} {
s.runTx(cmd, false)
}
}
func (s *registrySuite) createRecord(bondId string) {
s.T().Helper()
cmd := []string{
"tx", "registry", "set", recordFilePath, bondId,
}
s.runTx(cmd, false)
}

170
tests/system/suite_test.go Normal file
View File

@ -0,0 +1,170 @@
package system
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
systest "cosmossdk.io/systemtests"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"git.vdb.to/cerc-io/laconicd/app/params"
)
type testSuite struct {
suite.Suite
codec codec.Codec
accountNamePrefix string
fees string
bondDenom string
clientEthKey string
validatorEthKey string
}
func (s *testSuite) SetupSuite() {
s.codec = codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
s.accountNamePrefix = "node" // from testnet (--node-dir-prefix)
s.fees = "1" + params.CoinUnit
s.bondDenom = sdk.DefaultBondDenom
s.validatorEthKey = "validator-eth-key"
s.clientEthKey = "client-eth-key"
// configure the validators and client
commonInitCommands := [][]string{
{"config", "set", "client", "chain-id", "testing"},
{"config", "set", "client", "keyring-backend", "test"},
}
// disable nitro on validators by default
Sut.ForEachNodeExecAndWait(s.T(), append(commonInitCommands,
[]string{"config", "set", "app", "nitro.enable", "false"},
)...)
clientInitCommands := append(commonInitCommands,
[]string{"config", "set", "app", "nitro.eth-key", s.clientEthKey},
)
for _, cmd := range clientInitCommands {
cmd = append(cmd, "--home", s.clientHome())
_ = systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), cmd...)
}
}
func (s *testSuite) setValidatorNitroKeys(keys ...string) {
Sut.WithEachNodeHome(func(i int, home string) {
var cmds [][]string
// TODO: complete and enable distsig
// []string{"config", "set", "app", "distsig.enable", "true"},
// []string{"config", "set", "app", "distsig.longterm-key", s.validatorNitroKey},
// []string{"config", "set", "app", "nitro.use-distsig", "true"},
if i < len(keys) {
// configure validator as a payment channel (individual) counterparty
cmds = [][]string{
{"config", "set", "app", "nitro.enable", "true"},
{"keys", "import-hex", s.validatorEthKey, keys[i]},
{"config", "set", "app", "nitro.eth-key", s.validatorEthKey},
}
}
for _, cmd := range cmds {
systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), append(cmd, "--home", home)...)
}
})
}
// note: keys have to be created after ResetChain, which clears the keyring
func (s *testSuite) setClientKeys(ethKey string) {
commands := [][]string{
{"keys", "import-hex", s.clientEthKey, ethKey},
}
for _, cmd := range commands {
cmd = append(cmd, "--home", s.clientHome())
_ = systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), cmd...)
}
}
func (s *testSuite) resetChain() {
Sut.ResetChain(s.T())
// clear the nitro data dirs
Sut.WithEachNodeHome(func(i int, home string) {
_ = os.RemoveAll(filepath.Join(home, "nitro"))
})
_ = os.RemoveAll(filepath.Join(s.clientHome(), "nitro"))
}
func (s *testSuite) account(i int) string {
return fmt.Sprintf("%s%d", s.accountNamePrefix, i)
}
// the home dir systemtests uses to run client commands
func (s *testSuite) clientHome() string {
return filepath.Join(workDir, "testnet")
}
func (s *testSuite) endpointURL(endpoint string) string {
return fmt.Sprintf("%s/cerc/%s", Sut.APIAddress(), endpoint)
}
func (s *testSuite) cli() *systest.CLIWrapper {
cli := systest.NewCLIWrapper(s.T(), Sut, verbose).
WithRunStderr(os.Stderr)
return &cli
}
func (s *testSuite) assertWithStderr(asserter systest.RunErrorAssert) systest.RunErrorAssert {
return func(t assert.TestingT, err error, msgAndArgs ...any) bool {
if asserter(t, err, msgAndArgs...) {
return true
}
var exitError *exec.ExitError
if errors.As(err, &exitError) {
if stderr := exitError.Stderr; len(stderr) > 0 {
defer t.Errorf("stderr: %s", stderr)
}
}
return false
}
}
func (s *testSuite) runTx(cmd []string, err bool) string {
s.T().Helper()
cli := s.cli()
asserter := assert.NoError
if err {
asserter = assert.Error
}
// don't use Run(), it will clobber our custom --home
out := cli.
WithRunErrorMatcher(s.assertWithStderr(asserter)).
RunCommandWithArgs(s.withTxFlags(cmd)...)
if err {
return ""
}
txResult, committed := cli.AwaitTxCommitted(out, txTimeout)
s.Require().True(committed)
systest.RequireTxSuccess(s.T(), txResult)
txHash := gjson.Get(out, "txhash")
s.Require().True(txHash.Exists(), "txhash not found in output: %s", out)
return txHash.String()
}
func (s *testSuite) withTxFlags(cmd []string) []string {
cmd = s.cli().WithTXFlags(cmd...)
return append(cmd, []string{
"--from", s.account(0),
"--fees", s.fees,
}...)
}

11
testutil/keyring.go Normal file
View File

@ -0,0 +1,11 @@
package testutil
import (
"github.com/cosmos/cosmos-sdk/crypto/keyring"
modtestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
)
func NewKeyring() keyring.Keyring {
encCfg := modtestutil.MakeTestEncodingConfig()
return keyring.NewInMemory(encCfg.Codec)
}

54
utils/address.go Normal file
View File

@ -0,0 +1,54 @@
package utils
import (
"cosmossdk.io/core/address"
errorsmod "cosmossdk.io/errors"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/runtime"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"git.vdb.to/cerc-io/laconicd/app/params"
)
type addressCodec struct {
address.Codec
}
func (ac addressCodec) StringToBytes(text string) ([]byte, error) {
bz, err := ac.Codec.StringToBytes(text)
if err != nil {
return nil, err
}
if len(bz) != 20 && len(bz) != 32 {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownAddress,
"address length must be 20 or 32 bz, got %d", len(bz))
}
return bz, nil
}
func NewAddressCodec() address.Codec {
return addressCodec{
Codec: addresscodec.NewBech32Codec(params.Bech32PrefixAccAddr),
}
}
func NewValidatorAddressCodec() runtime.ValidatorAddressCodec {
return addressCodec{
Codec: addresscodec.NewBech32Codec(params.Bech32PrefixValAddr),
}
}
func NewConsensusAddressCodec() runtime.ConsensusAddressCodec {
return addressCodec{
Codec: addresscodec.NewBech32Codec(params.Bech32PrefixConsAddr),
}
}
func MustBytesToString(ac address.Codec, bz []byte) string {
str, err := ac.BytesToString(bz)
if err != nil {
panic(err)
}
return str
}

18
utils/cmd.go Normal file
View File

@ -0,0 +1,18 @@
package utils
import (
"context"
"errors"
"fmt"
)
func GetFromContext[T any](ctx context.Context, key string) (*T, error) {
if v := ctx.Value(key); v != nil {
val, ok := v.(*T)
if !ok {
return nil, fmt.Errorf("context value of wrong type; expected %T, got %T", new(T), v)
}
return val, nil
}
return nil, errors.New("key not found in context: " + key)
}

View File

@ -1,29 +1,39 @@
package utils
import (
"context"
"fmt"
"cosmossdk.io/core/gas"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func CtxWithCustomKVGasConfig(ctx *sdk.Context) *sdk.Context {
updatedCtx := ctx.WithKVGasConfig(storetypes.GasConfig{
HasCost: 0,
DeleteCost: 0,
ReadCostFlat: 0,
ReadCostPerByte: 0,
WriteCostFlat: 0,
WriteCostPerByte: 0,
IterNextCostFlat: 0,
})
const RefundModuleGasDescriptor = "laconic module gas refund"
return &updatedCtx
// WithCustomGasConfig returns a context with a zero-cost gas config, and a function to log all consumed gas.
//
// Note: as planned in server/v2, the gas meter will not be available on the STF-provided context
// during tx execution, and must be accessed via gas.Service instead. This arg is a placeholder
// to ensure modules using this include the gas service in anticipation of that change.
// https://github.com/cosmos/cosmos-sdk/pull/16310
func WithCustomGasConfig(ctx context.Context, _ gas.Service) (context.Context, func(log.Logger, string)) {
c := sdk.UnwrapSDKContext(ctx)
meter := c.GasMeter()
startGas := meter.GasConsumed()
logGas := func(logger log.Logger, method string) {
endGas := meter.GasConsumed()
meter.RefundGas(endGas-startGas, RefundModuleGasDescriptor)
LogTxGasConsumed(meter, logger, method)
}
// Set a zero-cost gas config on the returned context
return c.WithKVGasConfig(storetypes.GasConfig{}), logGas
}
func LogTxGasConsumed(ctx sdk.Context, logger log.Logger, tx string) {
gasConsumed := ctx.GasMeter().GasConsumed()
logger.Info("tx executed", "method", tx, "gas_consumed", fmt.Sprintf("%d", gasConsumed))
func LogTxGasConsumed(gm gas.Meter, logger log.Logger, method string) {
gasConsumed := gm.GasConsumed()
logger.Info("tx executed", "method", method, "gas_consumed", fmt.Sprintf("%d", gasConsumed))
}

79
utils/keys.go Normal file
View File

@ -0,0 +1,79 @@
package utils
import (
"cosmossdk.io/core/address"
sdkerrors "cosmossdk.io/errors"
cmtcrypto "github.com/cometbft/cometbft/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdkcrypto "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// cosmos-sdk crypto/keyring/record.go:138
func extractPrivKeyFromLocal(rl *keyring.Record_Local) (cmtcrypto.PrivKey, error) {
if rl.PrivKey == nil {
return nil, keyring.ErrPrivKeyNotAvailable
}
priv, ok := rl.PrivKey.GetCachedValue().(sdkcrypto.PrivKey)
if !ok {
return nil, sdkerrors.Wrap(keyring.ErrCastAny, "PrivKey")
}
return &PrivKey{priv}, nil
}
func ExtractPrivateKey(r *keyring.Record) (cmtcrypto.PrivKey, error) {
local := r.GetLocal()
if local == nil {
return nil, keyring.ErrPrivKeyExtr
}
return extractPrivKeyFromLocal(local)
}
func ExtractPrivateKeyByUid(kr keyring.Keyring, uid string) (cmtcrypto.PrivKey, error) {
r, err := kr.Key(uid)
if err != nil {
return nil, err
}
return ExtractPrivateKey(r)
}
// GetKeyRecord tries to get a key by either address or uid
func GetKeyRecord(kr keyring.Keyring, from string, ac address.Codec) (*keyring.Record, error) {
var k *keyring.Record
addr, err := ac.StringToBytes(from)
if err == nil {
k, err = kr.KeyByAddress(sdk.AccAddress(addr))
if err != nil {
return nil, err
}
} else {
k, err = kr.Key(from)
if err != nil {
return nil, err
}
}
return k, nil
}
// PrivKey wraps the SDK PrivKey type to satisfy cometbft/crypto.PrivKey
type PrivKey struct{ sdkcrypto.PrivKey }
func (k PrivKey) PubKey() cmtcrypto.PubKey { return PubKey{k.PrivKey.PubKey()} }
func (k PrivKey) Equals(other cmtcrypto.PrivKey) bool {
if otherPk, ok := other.(*PrivKey); ok {
return k.PrivKey.Equals(otherPk.PrivKey)
}
return false
}
// PubKey wraps the SDK PubKey type to satisfy cometbft/crypto.PubKey
type PubKey struct{ sdkcrypto.PubKey }
func (k PubKey) Equals(other cmtcrypto.PubKey) bool {
if otherPk, ok := other.(*PubKey); ok {
return k.PubKey.Equals(otherPk.PubKey)
}
return false
}

Some files were not shown because too many files have changed in this diff Show More