Documentation ¶
Overview ¶
Package grpc provides a GRPC-based router for smart contract method invocation. This package uses protocol buffers (protobuf) and GRPC service descriptions to dynamically route and invoke smart contract methods based on predefined service and method definitions.
GRPC Routing ¶
The core functionality of this package revolves around the Router type, which implements the github.com/anoideaopen/foundation/core/routing.Router interface. The router dynamically routes incoming method calls based on GRPC service definitions and protobuf method options.
Supported GRPC Method Types:
- Transaction: Methods that modify the blockchain state. These are identified by the custom `method_type` option set to `METHOD_TYPE_TRANSACTION` in the protobuf definition.
- Invoke: Methods that are executed directly as Hyperledger Fabric Invoke transactions, bypassing the batching mechanism.
- Query: Read-only methods that retrieve data without altering the blockchain state.
The router also supports custom method authorization settings, defined using the `method_auth` option in the protobuf file. These settings allow developers to specify whether a method requires authentication.
Protobuf Example ¶
Here is an example of a protobuf definition that includes custom extensions for method types and authorization:
syntax = "proto3"; package foundationtoken; option go_package = "github.com/anoideaopen/foundation/test/chaincode/fiat/service"; import "google/protobuf/empty.proto"; import "validate/validate.proto"; import "method_options.proto"; // Import custom options. message Address { string base58check = 1 [(validate.rules).string = {pattern: "^[1-9A-HJ-NP-Za-km-z]+$"}]; } message BigInt { string value = 1 [(validate.rules).string = {pattern: "^[0-9]+$"}]; } message BalanceAdjustmentRequest { Address address = 1 [(validate.rules).message.required = true]; BigInt amount = 2 [(validate.rules).message.required = true]; string reason = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; } service FiatService { rpc AddBalanceByAdmin(BalanceAdjustmentRequest) returns (google.protobuf.Empty) { option (foundation.method_type) = METHOD_TYPE_TRANSACTION; } }
Usage Example ¶
To use the GRPC router in your chaincode, you can define a service in your Go code that implements the methods defined in your protobuf file, and then register this service with the GRPC router:
package main import ( "context" "errors" mbig "math/big" "github.com/anoideaopen/foundation/core/routing/grpc" "github.com/anoideaopen/foundation/test/chaincode/fiat/service" "google.golang.org/protobuf/types/known/emptypb" ) // FiatToken represents a custom smart contract type FiatToken struct { service.UnimplementedFiatServiceServer } // AddBalanceByAdmin implements a method to add balance by an admin func (ft *FiatToken) AddBalanceByAdmin(ctx context.Context, req *service.BalanceAdjustmentRequest) (*emptypb.Empty, error) { if grpc.SenderFromContext(ctx) == "" { return nil, errors.New("unauthorized") } if grpc.StubFromContext(ctx) == nil { return nil, errors.New("stub is nil") } value, _ := mbig.NewInt(0).SetString(req.GetAmount().GetValue(), 10) return &emptypb.Empty{}, balance.Add( grpc.StubFromContext(ctx), balance.BalanceTypeToken, req.GetAddress().GetBase58Check(), "", value, ) } func main() { contract := &FiatToken{} grpcRouter := grpc.NewRouter() // Register the service with the GRPC router service.RegisterFiatServiceServer(grpcRouter, contract) // Initialize chaincode with the GRPC router cc, err := core.NewCC( contract, core.WithRouters(grpcRouter), ) if err != nil { log.Fatal(err) } // Start chaincode if err = cc.Start(); err != nil { log.Fatal(err) } }
Method Invocation and Authorization ¶
When a method is invoked via the GRPC router, the router first checks the method options defined in the protobuf file to determine the type of method (transaction, invoke, or query) and whether authentication is required. If authentication is required, the router will validate the sender before invoking the method.
Below is an example of initializing a GRPC router alongside a reflection-based router:
Index ¶
- Variables
- func ContextWithSender(parent context.Context, sender string) context.Context
- func ContextWithStub(parent context.Context, stub shim.ChaincodeStubInterface) context.Context
- func FindServiceDescriptor(serviceName string) protoreflect.ServiceDescriptor
- func FullNameToURL(fullMethodName string) string
- func SenderFromContext(parent context.Context) string
- func StubFromContext(parent context.Context) shim.ChaincodeStubInterface
- func URLToServiceAndMethod(url string) (string, string)
- type Router
- func (r *Router) ArgCount(method string) int
- func (r *Router) AuthRequired(method string) bool
- func (r *Router) Check(stub shim.ChaincodeStubInterface, method string, args ...string) error
- func (r *Router) Function(method string) (function string)
- func (r *Router) Handlers() map[string]string
- func (r *Router) Invoke(stub shim.ChaincodeStubInterface, method string, args ...string) ([]byte, error)
- func (r *Router) IsInvoke(method string) bool
- func (r *Router) IsQuery(method string) bool
- func (r *Router) IsTransaction(method string) bool
- func (r *Router) Method(function string) (method string)
- func (r *Router) RegisterService(desc *grpc.ServiceDesc, impl any)
Constants ¶
This section is empty.
Variables ¶
var ( ErrUnsupportedMethod = errors.New("unsupported method") ErrInvalidNumberOfArguments = errors.New("invalid number of arguments") ErrInterfaceNotProtoMessage = errors.New("interface is not a proto.Message") )
Routing errors.
Functions ¶
func ContextWithSender ¶
ContextWithSender adds a sender to the context.
func ContextWithStub ¶
ContextWithStub adds a stub to the context.
func FindServiceDescriptor ¶
func FindServiceDescriptor(serviceName string) protoreflect.ServiceDescriptor
FindServiceDescriptor finds the service descriptor by the given service name.
func FullNameToURL ¶
FullNameToURL transforms a method name from "package.Service.Method" to "/package.Service/Method"
func SenderFromContext ¶
SenderFromContext retrieves a sender from the context.
func StubFromContext ¶
func StubFromContext(parent context.Context) shim.ChaincodeStubInterface
StubFromContext retrieves a stub from the context.
func URLToServiceAndMethod ¶ added in v0.0.4
URLToServiceAndMethod extracts the service name and method name from a URL.
Types ¶
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router routes method calls to contract methods based on gRPC service description.
func (*Router) ArgCount ¶ added in v0.0.4
ArgCount returns the number of arguments the method takes (excluding the receiver).
func (*Router) AuthRequired ¶ added in v0.0.4
AuthRequired indicates if the method requires authentication.
func (*Router) Function ¶ added in v0.0.4
Function returns the name of the chaincode function by the specified method.
func (*Router) Handlers ¶ added in v0.0.4
Handlers returns a map of method names to chaincode functions.
func (*Router) Invoke ¶
func (r *Router) Invoke(stub shim.ChaincodeStubInterface, method string, args ...string) ([]byte, error)
Invoke calls the specified method with the provided arguments.
func (*Router) IsTransaction ¶ added in v0.0.4
IsTransaction checks if the method is a transaction type.
func (*Router) Method ¶ added in v0.0.4
Method retrieves the method associated with the specified chaincode function.
func (*Router) RegisterService ¶
func (r *Router) RegisterService(desc *grpc.ServiceDesc, impl any)
RegisterService registers a service and its implementation to the concrete type implementing this interface. It may not be called once the server has started serving. desc describes the service and its methods and handlers. impl is the service implementation which is passed to the method handlers.