diff --git a/asset-transfer-basic/application-gateway-go/assetTransfer.go b/asset-transfer-basic/application-gateway-go/assetTransfer.go old mode 100755 new mode 100644 index 348d2341..e830ab7f --- a/asset-transfer-basic/application-gateway-go/assetTransfer.go +++ b/asset-transfer-basic/application-gateway-go/assetTransfer.go @@ -7,290 +7,14 @@ SPDX-License-Identifier: Apache-2.0 package main import ( - "bytes" - "context" - "crypto/x509" - "encoding/json" - "errors" + "assetTransfer/cmd" "fmt" "os" - "path" - "time" - - "github.com/hyperledger/fabric-gateway/pkg/client" - "github.com/hyperledger/fabric-gateway/pkg/hash" - "github.com/hyperledger/fabric-gateway/pkg/identity" - "github.com/hyperledger/fabric-protos-go-apiv2/gateway" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/status" ) -const ( - mspID = "Org1MSP" - cryptoPath = "../../test-network/organizations/peerOrganizations/org1.example.com" - certPath = cryptoPath + "/users/User1@org1.example.com/msp/signcerts" - keyPath = cryptoPath + "/users/User1@org1.example.com/msp/keystore" - tlsCertPath = cryptoPath + "/peers/peer0.org1.example.com/tls/ca.crt" - peerEndpoint = "dns:///localhost:7051" - gatewayPeer = "peer0.org1.example.com" -) - -var now = time.Now() -var assetId = fmt.Sprintf("asset%d", now.Unix()*1e3+int64(now.Nanosecond())/1e6) - func main() { - // The gRPC client connection should be shared by all Gateway connections to this endpoint - clientConnection := newGrpcConnection() - defer clientConnection.Close() - - id := newIdentity() - sign := newSign() - - // Create a Gateway connection for a specific client identity - gw, err := client.Connect( - id, - client.WithSign(sign), - client.WithHash(hash.SHA256), - client.WithClientConnection(clientConnection), - // Default timeouts for different gRPC calls - client.WithEvaluateTimeout(5*time.Second), - client.WithEndorseTimeout(15*time.Second), - client.WithSubmitTimeout(5*time.Second), - client.WithCommitStatusTimeout(1*time.Minute), - ) - if err != nil { - panic(err) - } - defer gw.Close() - - // Override default values for chaincode and channel name as they may differ in testing contexts. - chaincodeName := "basic" - if ccname := os.Getenv("CHAINCODE_NAME"); ccname != "" { - chaincodeName = ccname - } - - channelName := "mychannel" - if cname := os.Getenv("CHANNEL_NAME"); cname != "" { - channelName = cname - } - - network := gw.GetNetwork(channelName) - contract := network.GetContract(chaincodeName) - - initLedger(contract) - getAllAssets(contract) - createAsset(contract) - readAssetByID(contract) - transferAssetAsync(contract) - exampleErrorHandling(contract) -} - -// newGrpcConnection creates a gRPC connection to the Gateway server. -func newGrpcConnection() *grpc.ClientConn { - certificatePEM, err := os.ReadFile(tlsCertPath) - if err != nil { - panic(fmt.Errorf("failed to read TLS certifcate file: %w", err)) - } - - certificate, err := identity.CertificateFromPEM(certificatePEM) - if err != nil { - panic(err) - } - - certPool := x509.NewCertPool() - certPool.AddCert(certificate) - transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer) - - connection, err := grpc.NewClient(peerEndpoint, grpc.WithTransportCredentials(transportCredentials)) - if err != nil { - panic(fmt.Errorf("failed to create gRPC connection: %w", err)) - } - - return connection -} - -// newIdentity creates a client identity for this Gateway connection using an X.509 certificate. -func newIdentity() *identity.X509Identity { - certificatePEM, err := readFirstFile(certPath) - if err != nil { - panic(fmt.Errorf("failed to read certificate file: %w", err)) - } - - certificate, err := identity.CertificateFromPEM(certificatePEM) - if err != nil { - panic(err) - } - - id, err := identity.NewX509Identity(mspID, certificate) - if err != nil { - panic(err) - } - - return id -} - -// newSign creates a function that generates a digital signature from a message digest using a private key. -func newSign() identity.Sign { - privateKeyPEM, err := readFirstFile(keyPath) - if err != nil { - panic(fmt.Errorf("failed to read private key file: %w", err)) - } - - privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM) - if err != nil { - panic(err) - } - - sign, err := identity.NewPrivateKeySign(privateKey) - if err != nil { - panic(err) - } - - return sign -} - -func readFirstFile(dirPath string) ([]byte, error) { - dir, err := os.Open(dirPath) - if err != nil { - return nil, err - } - - fileNames, err := dir.Readdirnames(1) - if err != nil { - return nil, err - } - - return os.ReadFile(path.Join(dirPath, fileNames[0])) -} - -// This type of transaction would typically only be run once by an application the first time it was started after its -// initial deployment. A new version of the chaincode deployed later would likely not need to run an "init" function. -func initLedger(contract *client.Contract) { - fmt.Printf("\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger \n") - - _, err := contract.SubmitTransaction("InitLedger") - if err != nil { - panic(fmt.Errorf("failed to submit transaction: %w", err)) - } - - fmt.Printf("*** Transaction committed successfully\n") -} - -// Evaluate a transaction to query ledger state. -func getAllAssets(contract *client.Contract) { - fmt.Println("\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger") - - evaluateResult, err := contract.EvaluateTransaction("GetAllAssets") - if err != nil { - panic(fmt.Errorf("failed to evaluate transaction: %w", err)) - } - result := formatJSON(evaluateResult) - - fmt.Printf("*** Result:%s\n", result) -} - -// Submit a transaction synchronously, blocking until it has been committed to the ledger. -func createAsset(contract *client.Contract) { - fmt.Printf("\n--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments \n") - - _, err := contract.SubmitTransaction("CreateAsset", assetId, "yellow", "5", "Tom", "1300") - if err != nil { - panic(fmt.Errorf("failed to submit transaction: %w", err)) - } - - fmt.Printf("*** Transaction committed successfully\n") -} - -// Evaluate a transaction by assetID to query ledger state. -func readAssetByID(contract *client.Contract) { - fmt.Printf("\n--> Evaluate Transaction: ReadAsset, function returns asset attributes\n") - - evaluateResult, err := contract.EvaluateTransaction("ReadAsset", assetId) - if err != nil { - panic(fmt.Errorf("failed to evaluate transaction: %w", err)) - } - result := formatJSON(evaluateResult) - - fmt.Printf("*** Result:%s\n", result) -} - -// Submit transaction asynchronously, blocking until the transaction has been sent to the orderer, and allowing -// this thread to process the chaincode response (e.g. update a UI) without waiting for the commit notification -func transferAssetAsync(contract *client.Contract) { - fmt.Printf("\n--> Async Submit Transaction: TransferAsset, updates existing asset owner") - - submitResult, commit, err := contract.SubmitAsync("TransferAsset", client.WithArguments(assetId, "Mark")) - if err != nil { - panic(fmt.Errorf("failed to submit transaction asynchronously: %w", err)) - } - - fmt.Printf("\n*** Successfully submitted transaction to transfer ownership from %s to Mark. \n", string(submitResult)) - fmt.Println("*** Waiting for transaction commit.") - - if commitStatus, err := commit.Status(); err != nil { - panic(fmt.Errorf("failed to get commit status: %w", err)) - } else if !commitStatus.Successful { - panic(fmt.Errorf("transaction %s failed to commit with status: %d", commitStatus.TransactionID, int32(commitStatus.Code))) - } - - fmt.Printf("*** Transaction committed successfully\n") -} - -// Submit transaction, passing in the wrong number of arguments ,expected to throw an error containing details of any error responses from the smart contract. -func exampleErrorHandling(contract *client.Contract) { - fmt.Println("\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error") - - _, err := contract.SubmitTransaction("UpdateAsset", "asset70", "blue", "5", "Tomoko", "300") - if err == nil { - panic("******** FAILED to return an error") - } - - fmt.Println("*** Successfully caught the error:") - - var endorseErr *client.EndorseError - var submitErr *client.SubmitError - var commitStatusErr *client.CommitStatusError - var commitErr *client.CommitError - - if errors.As(err, &endorseErr) { - fmt.Printf("Endorse error for transaction %s with gRPC status %v: %s\n", endorseErr.TransactionID, status.Code(endorseErr), endorseErr) - } else if errors.As(err, &submitErr) { - fmt.Printf("Submit error for transaction %s with gRPC status %v: %s\n", submitErr.TransactionID, status.Code(submitErr), submitErr) - } else if errors.As(err, &commitStatusErr) { - if errors.Is(err, context.DeadlineExceeded) { - fmt.Printf("Timeout waiting for transaction %s commit status: %s", commitStatusErr.TransactionID, commitStatusErr) - } else { - fmt.Printf("Error obtaining commit status for transaction %s with gRPC status %v: %s\n", commitStatusErr.TransactionID, status.Code(commitStatusErr), commitStatusErr) - } - } else if errors.As(err, &commitErr) { - fmt.Printf("Transaction %s failed to commit with status %d: %s\n", commitErr.TransactionID, int32(commitErr.Code), err) - } else { - panic(fmt.Errorf("unexpected error type %T: %w", err, err)) - } - - // Any error that originates from a peer or orderer node external to the gateway will have its details - // embedded within the gRPC status error. The following code shows how to extract that. - statusErr := status.Convert(err) - - details := statusErr.Details() - if len(details) > 0 { - fmt.Println("Error Details:") - - for _, detail := range details { - switch detail := detail.(type) { - case *gateway.ErrorDetail: - fmt.Printf("- address: %s; mspId: %s; message: %s\n", detail.Address, detail.MspId, detail.Message) - } - } + if err := cmd.Execute(); err != nil { + fmt.Println("Error:", err) + os.Exit(1) } } - -// Format JSON data -func formatJSON(data []byte) string { - var prettyJSON bytes.Buffer - if err := json.Indent(&prettyJSON, data, "", " "); err != nil { - panic(fmt.Errorf("failed to parse JSON: %w", err)) - } - return prettyJSON.String() -} diff --git a/asset-transfer-basic/application-gateway-go/cmd/root.go b/asset-transfer-basic/application-gateway-go/cmd/root.go new file mode 100644 index 00000000..49af8961 --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/cmd/root.go @@ -0,0 +1,62 @@ +package cmd + +import ( + "assetTransfer/internal/conf" + "assetTransfer/internal/grpc" + "assetTransfer/internal/log" + "assetTransfer/internal/router" + "fmt" + "github.com/gin-gonic/gin" + "github.com/spf13/cobra" +) + +var ( + port string + configPath string +) + +var rootCmd = &cobra.Command{ + Use: "ledger-gw", + RunE: run, +} + +func init() { + rootCmd.PersistentFlags().StringVarP(&port, "port", "p", "8080", "Port to run the server on") + rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "./config.yaml", "Path of the configuration file") + + if err := rootCmd.MarkPersistentFlagRequired("port"); err != nil { + fmt.Println(err) + } + if err := rootCmd.MarkPersistentFlagRequired("config"); err != nil { + fmt.Println(err) + } +} + +func run(_ *cobra.Command, _ []string) error { + if err := config.InitConfig(configPath); err != nil { + return err + } + if err := log.InitLog(config.GetLogLevel(), config.GetLogPath()); err != nil { + return err + } + + // 初始化网关连接 + grpc.InitGWConnect() + defer grpc.CloseGWConnect() + + gin.SetMode(config.GetServerMode()) + r := gin.Default() + router.SetupRoutes(r) + if err := r.Run(":" + port); err != nil { + fmt.Println("Failed to start server:", err) + return err + } + return nil +} + +func Execute() error { + if err := rootCmd.Execute(); err != nil { + return err + } + return nil +} diff --git a/asset-transfer-basic/application-gateway-go/config.yaml b/asset-transfer-basic/application-gateway-go/config.yaml new file mode 100644 index 00000000..1edf9c27 --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/config.yaml @@ -0,0 +1,6 @@ +server: + mode: debug + +log: + level: debug + path: ./logs/culture_platform.log diff --git a/asset-transfer-basic/application-gateway-go/go.mod b/asset-transfer-basic/application-gateway-go/go.mod index d59cfe48..e411be0d 100644 --- a/asset-transfer-basic/application-gateway-go/go.mod +++ b/asset-transfer-basic/application-gateway-go/go.mod @@ -3,13 +3,40 @@ module assetTransfer go 1.23.0 require ( + github.com/gin-gonic/gin v1.10.0 github.com/hyperledger/fabric-gateway v1.7.0 github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4 + github.com/spf13/cobra v1.9.1 + go.uber.org/zap v1.27.0 google.golang.org/grpc v1.67.1 + gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.28.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.26.0 // indirect diff --git a/asset-transfer-basic/application-gateway-go/go.sum b/asset-transfer-basic/application-gateway-go/go.sum index 01eab6a8..fe50bfd3 100644 --- a/asset-transfer-basic/application-gateway-go/go.sum +++ b/asset-transfer-basic/application-gateway-go/go.sum @@ -1,23 +1,98 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/hyperledger/fabric-gateway v1.7.0 h1:bd1quU8qYPYqYO69m1tPIDSjB+D+u/rBJfE1eWFcpjY= github.com/hyperledger/fabric-gateway v1.7.0/go.mod h1:TItDGnq71eJcgz5TW+m5Sq3kWGp0AEI1HPCNxj0Eu7k= github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4 h1:YJrd+gMaeY0/vsN0aS0QkEKTivGoUnSRIXxGJ7KI+Pc= github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4/go.mod h1:bau/6AJhvEcu9GKKYHlDXAxXKzYNfhP6xu2GXuxEcFk= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= @@ -28,5 +103,10 @@ google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/asset-transfer-basic/application-gateway-go/internal/api/api.go b/asset-transfer-basic/application-gateway-go/internal/api/api.go new file mode 100644 index 00000000..623c6fb4 --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/internal/api/api.go @@ -0,0 +1,61 @@ +package api + +import ( + "fmt" + + "github.com/gin-gonic/gin" + + ierror "assetTransfer/internal/error" + "assetTransfer/internal/grpc" +) + +func SubmitTransaction(c *gin.Context) { + funcName, args, err := getCommonParams(c) + if err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + result, err := grpc.Contract.SubmitTransaction(funcName, args...) + if err != nil { + c.JSON(500, gin.H{"error": ierror.ErrorHandling(err)}) + return + } + c.JSON(200, gin.H{"result": result}) +} + +func EvaluateTransaction(c *gin.Context) { + funcName, args, err := getCommonParams(c) + if err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + result, err := grpc.Contract.EvaluateTransaction(funcName, args...) + if err != nil { + c.JSON(500, gin.H{"error": ierror.ErrorHandling(err)}) + return + } + c.JSON(200, gin.H{"result": result}) +} + +func getCommonParams(c *gin.Context) (string, []string, error) { + type RequestBody struct { + FuncName string `json:"func"` + Args []string `json:"args"` + } + + var body RequestBody + if err := c.ShouldBindJSON(&body); err != nil { + return "", nil, fmt.Errorf("invalid request body: %v", err) + } + + if body.FuncName == "" { + return "", nil, fmt.Errorf("func is required") + } + if len(body.Args) == 0 { + return "", nil, fmt.Errorf("args is required") + } + + return body.FuncName, body.Args, nil +} diff --git a/asset-transfer-basic/application-gateway-go/internal/conf/conf.go b/asset-transfer-basic/application-gateway-go/internal/conf/conf.go new file mode 100644 index 00000000..4529b094 --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/internal/conf/conf.go @@ -0,0 +1,44 @@ +package config + +import ( + "gopkg.in/yaml.v3" + "io/ioutil" +) + +type ( + Config struct { + Server Server `yaml:"server"` + Log Log `yaml:"log"` + } + + Server struct { + Mode string `yaml:"mode"` + } + + Log struct { + Level string `yaml:"level"` + Path string `yaml:"path"` + } +) + +var config Config + +func InitConfig(configPath string) error { + // 读取配置文件 + yamlFile, err := ioutil.ReadFile(configPath) + if err != nil { + return err + } + + // 解析YAML文件到config结构体 + err = yaml.Unmarshal(yamlFile, &config) + if err != nil { + return err + } + return nil +} + +func GetServerMode() string { return config.Server.Mode } + +func GetLogLevel() string { return config.Log.Level } +func GetLogPath() string { return config.Log.Path } diff --git a/asset-transfer-basic/application-gateway-go/internal/error/error.go b/asset-transfer-basic/application-gateway-go/internal/error/error.go new file mode 100644 index 00000000..377de974 --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/internal/error/error.go @@ -0,0 +1,49 @@ +package error + +import ( + "context" + "errors" + "fmt" + "github.com/hyperledger/fabric-protos-go-apiv2/gateway" + + "github.com/hyperledger/fabric-gateway/pkg/client" + "google.golang.org/grpc/status" +) + +func ErrorHandling(err error) string { + var endorseErr *client.EndorseError + var submitErr *client.SubmitError + var commitStatusErr *client.CommitStatusError + var commitErr *client.CommitError + var res string + + if errors.As(err, &endorseErr) { + res = fmt.Sprintf("Endorse error for transaction %s with gRPC status %v: %s\n", endorseErr.TransactionID, status.Code(endorseErr), endorseErr) + } else if errors.As(err, &submitErr) { + res = fmt.Sprintf("Submit error for transaction %s with gRPC status %v: %s\n", submitErr.TransactionID, status.Code(submitErr), submitErr) + } else if errors.As(err, &commitStatusErr) { + if errors.Is(err, context.DeadlineExceeded) { + res = fmt.Sprintf("Timeout waiting for transaction %s commit status: %s", commitStatusErr.TransactionID, commitStatusErr) + } else { + res = fmt.Sprintf("Error obtaining commit status for transaction %s with gRPC status %v: %s\n", commitStatusErr.TransactionID, status.Code(commitStatusErr), commitStatusErr) + } + } else if errors.As(err, &commitErr) { + res = fmt.Sprintf("Transaction %s failed to commit with status %d: %s\n", commitErr.TransactionID, int32(commitErr.Code), err) + } else { + panic(fmt.Errorf("unexpected error type %T: %w", err, err)) + } + + statusErr := status.Convert(err) + details := statusErr.Details() + if len(details) > 0 { + fmt.Println("Error Details:") + + for _, detail := range details { + switch detail := detail.(type) { + case *gateway.ErrorDetail: + res = fmt.Sprintf("- address: %s; mspId: %s; message: %s\n", detail.Address, detail.MspId, detail.Message) + } + } + } + return res +} diff --git a/asset-transfer-basic/application-gateway-go/internal/grpc/connect.go b/asset-transfer-basic/application-gateway-go/internal/grpc/connect.go new file mode 100644 index 00000000..6fada8ec --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/internal/grpc/connect.go @@ -0,0 +1,154 @@ +package grpc + +import ( + "crypto/x509" + "fmt" + "os" + "path" + "time" + + "github.com/hyperledger/fabric-gateway/pkg/client" + "github.com/hyperledger/fabric-gateway/pkg/hash" + "github.com/hyperledger/fabric-gateway/pkg/identity" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +const ( + rootPath = "/root/go/src/github.com/BennielAllan/fabric-samples" + mspID = "Org1MSP" + cryptoPath = rootPath + "/test-network/organizations/peerOrganizations/org1.example.com" + certPath = cryptoPath + "/users/User1@org1.example.com/msp/signcerts" + keyPath = cryptoPath + "/users/User1@org1.example.com/msp/keystore" + tlsCertPath = cryptoPath + "/peers/peer0.org1.example.com/tls/ca.crt" + peerEndpoint = "dns:///localhost:7051" + gatewayPeer = "peer0.org1.example.com" +) + +var ( + ClientConnection *grpc.ClientConn + GateWay *client.Gateway + Contract *client.Contract +) + +func InitGWConnect() { + // The gRPC client connection should be shared by all Gateway connections to this endpoint + ClientConnection = newGrpcConnection() + + id := newIdentity() + sign := newSign() + + // Create a Gateway connection for a specific client identity + var err error + GateWay, err = client.Connect( + id, + client.WithSign(sign), + client.WithHash(hash.SHA256), + client.WithClientConnection(ClientConnection), + // Default timeouts for different gRPC calls + client.WithEvaluateTimeout(5*time.Second), + client.WithEndorseTimeout(15*time.Second), + client.WithSubmitTimeout(5*time.Second), + client.WithCommitStatusTimeout(1*time.Minute), + ) + if err != nil { + panic(err) + } + // ./network.sh deployCC -ccn ledger -ccp /root/go/src/github.com/BennielAllan/fabric-samples/asset-transfer-ledger-queries/chaincode-go/ -ccl go + // Override default values for chaincode and channel name as they may differ in testing contexts. + chaincodeName := "ledger" + if ccname := os.Getenv("CHAINCODE_NAME"); ccname != "" { + chaincodeName = ccname + } + + channelName := "mychannel" + if cname := os.Getenv("CHANNEL_NAME"); cname != "" { + channelName = cname + } + + network := GateWay.GetNetwork(channelName) + Contract = network.GetContract(chaincodeName) +} + +func CloseGWConnect() { + GateWay.Close() + ClientConnection.Close() +} + +// newGrpcConnection creates a gRPC connection to the Gateway server. +func newGrpcConnection() *grpc.ClientConn { + certificatePEM, err := os.ReadFile(tlsCertPath) + if err != nil { + panic(fmt.Errorf("failed to read TLS certifcate file: %w", err)) + } + + certificate, err := identity.CertificateFromPEM(certificatePEM) + if err != nil { + panic(err) + } + + certPool := x509.NewCertPool() + certPool.AddCert(certificate) + transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer) + + connection, err := grpc.NewClient(peerEndpoint, grpc.WithTransportCredentials(transportCredentials)) + if err != nil { + panic(fmt.Errorf("failed to create gRPC connection: %w", err)) + } + + return connection +} + +// newIdentity creates a client identity for this Gateway connection using an X.509 certificate. +func newIdentity() *identity.X509Identity { + certificatePEM, err := readFirstFile(certPath) + if err != nil { + panic(fmt.Errorf("failed to read certificate file: %w", err)) + } + + certificate, err := identity.CertificateFromPEM(certificatePEM) + if err != nil { + panic(err) + } + + id, err := identity.NewX509Identity(mspID, certificate) + if err != nil { + panic(err) + } + + return id +} + +// newSign creates a function that generates a digital signature from a message digest using a private key. +func newSign() identity.Sign { + privateKeyPEM, err := readFirstFile(keyPath) + if err != nil { + panic(fmt.Errorf("failed to read private key file: %w", err)) + } + + privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM) + if err != nil { + panic(err) + } + + sign, err := identity.NewPrivateKeySign(privateKey) + if err != nil { + panic(err) + } + + return sign +} + +func readFirstFile(dirPath string) ([]byte, error) { + dir, err := os.Open(dirPath) + if err != nil { + return nil, err + } + + fileNames, err := dir.Readdirnames(1) + if err != nil { + return nil, err + } + + return os.ReadFile(path.Join(dirPath, fileNames[0])) +} diff --git a/asset-transfer-basic/application-gateway-go/internal/log/log.go b/asset-transfer-basic/application-gateway-go/internal/log/log.go new file mode 100644 index 00000000..f5641117 --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/internal/log/log.go @@ -0,0 +1,58 @@ +package log + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" + "path/filepath" +) + +var logger *zap.Logger + +func InitLog(level, path string) error { + // 配置日志级别 + var logLevel zapcore.Level + switch level { + case "debug": + logLevel = zap.DebugLevel + case "info": + logLevel = zap.InfoLevel + case "warn": + logLevel = zap.WarnLevel + case "error": + logLevel = zap.ErrorLevel + case "dpanic": + logLevel = zap.DPanicLevel + case "panic": + logLevel = zap.PanicLevel + case "fatal": + logLevel = zap.FatalLevel + default: + logLevel = zap.InfoLevel + } + + // 配置日志输出到文件 + fileEncoderConfig := zap.NewProductionEncoderConfig() + fileEncoderConfig.TimeKey = "timestamp" + fileEncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + fileEncoder := zapcore.NewJSONEncoder(fileEncoderConfig) + + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return err + } + file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + return err + } + + fileWriter := zapcore.AddSync(file) + fileCore := zapcore.NewCore(fileEncoder, fileWriter, logLevel) + + // 创建 logger + logger = zap.New(fileCore, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)) + return nil +} + +func GetLogger() *zap.Logger { + return logger +} diff --git a/asset-transfer-basic/application-gateway-go/internal/router/router.go b/asset-transfer-basic/application-gateway-go/internal/router/router.go new file mode 100644 index 00000000..5bd3271a --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/internal/router/router.go @@ -0,0 +1,13 @@ +package router + +import ( + "assetTransfer/internal/api" + "github.com/gin-gonic/gin" +) + +// SetupRoutes 设置路由 +func SetupRoutes(r *gin.Engine) { + // 定义路由 + r.POST("/submit", api.SubmitTransaction) + r.POST("/evaluate", api.EvaluateTransaction) +} diff --git a/asset-transfer-basic/application-gateway-go/logs/culture_platform.log b/asset-transfer-basic/application-gateway-go/logs/culture_platform.log new file mode 100644 index 00000000..e69de29b diff --git a/asset-transfer-basic/application-gateway-go/main.go b/asset-transfer-basic/application-gateway-go/main.go new file mode 100755 index 00000000..e830ab7f --- /dev/null +++ b/asset-transfer-basic/application-gateway-go/main.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 IBM All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "assetTransfer/cmd" + "fmt" + "os" +) + +func main() { + if err := cmd.Execute(); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } +}