mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-23 01:55:10 +00:00
REST api added for asset transfer in Golang
Signed-off-by: Basil K Y <techiebasil@gmail.com>
This commit is contained in:
parent
ea533d61b9
commit
9485e4bd10
8 changed files with 289 additions and 0 deletions
3
asset-transfer-basic/rest-api-go/.gitignore
vendored
Normal file
3
asset-transfer-basic/rest-api-go/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
go.sum
|
||||||
|
Requests.http
|
||||||
|
rest-api-go
|
||||||
39
asset-transfer-basic/rest-api-go/README.md
Normal file
39
asset-transfer-basic/rest-api-go/README.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Asset Transfer REST API Sample
|
||||||
|
|
||||||
|
This is a simple REST server written in golang with endpoints for chaincode invoke and query.
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
- Setup fabric test network and deploy the asset transfer chaincode by [following this instructions](https://hyperledger-fabric.readthedocs.io/en/release-2.4/test_network.html).
|
||||||
|
|
||||||
|
- cd into rest-api-go directory
|
||||||
|
- Download required dependencies using `go mod download`
|
||||||
|
- Run `go run main.go` to run the REST server
|
||||||
|
|
||||||
|
## Sending Requests
|
||||||
|
|
||||||
|
Invoke endpoint accepts POST requests with chaincode function and arguments. Query endpoint accepts get requests with chaincode function and arguments.
|
||||||
|
|
||||||
|
Sample chaincode invoke for the "createAsset" function. Response will contain transaction ID for a successful invoke.
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
curl --request POST \
|
||||||
|
--url http://localhost:3000/invoke \
|
||||||
|
--header 'content-type: application/x-www-form-urlencoded' \
|
||||||
|
--data = \
|
||||||
|
--data channelid=mychannel \
|
||||||
|
--data chaincodeid=basic \
|
||||||
|
--data function=createAsset \
|
||||||
|
--data args=Asset123 \
|
||||||
|
--data args=yellow \
|
||||||
|
--data args=54 \
|
||||||
|
--data args=Tom \
|
||||||
|
--data args=13005
|
||||||
|
```
|
||||||
|
Sample chaincode query for getting asset details.
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
curl --request GET \
|
||||||
|
--url 'http://localhost:3000/query?channelid=mychannel&chaincodeid=basic&function=ReadAsset&args=Asset123'
|
||||||
|
```
|
||||||
19
asset-transfer-basic/rest-api-go/go.mod
Normal file
19
asset-transfer-basic/rest-api-go/go.mod
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
module rest-api-go
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/hyperledger/fabric-gateway v1.1.0
|
||||||
|
google.golang.org/grpc v1.49.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/hyperledger/fabric-protos-go-apiv2 v0.0.0-20220615102044-467be1c7b2e7 // indirect
|
||||||
|
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220526153639-5463443f8c37 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20220527130721-00d5c0f3be58 // indirect
|
||||||
|
google.golang.org/protobuf v1.28.0 // indirect
|
||||||
|
)
|
||||||
26
asset-transfer-basic/rest-api-go/main.go
Normal file
26
asset-transfer-basic/rest-api-go/main.go
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"rest-api-go/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
//Initialize setup for Org1
|
||||||
|
cryptoPath := "../../test-network/organizations/peerOrganizations/org1.example.com"
|
||||||
|
orgConfig := web.OrgSetup{
|
||||||
|
OrgName: "Org1",
|
||||||
|
MSPID: "Org1MSP",
|
||||||
|
CertPath: cryptoPath + "/users/User1@org1.example.com/msp/signcerts/cert.pem",
|
||||||
|
KeyPath: cryptoPath + "/users/User1@org1.example.com/msp/keystore/",
|
||||||
|
TLSCertPath: cryptoPath + "/peers/peer0.org1.example.com/tls/ca.crt",
|
||||||
|
PeerEndpoint: "localhost:7051",
|
||||||
|
GatewayPeer: "peer0.org1.example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
orgSetup, err := web.Initialize(orgConfig)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error initializing setup for Org1: ", err)
|
||||||
|
}
|
||||||
|
web.Serve(web.OrgSetup(*orgSetup))
|
||||||
|
}
|
||||||
31
asset-transfer-basic/rest-api-go/web/app.go
Normal file
31
asset-transfer-basic/rest-api-go/web/app.go
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OrgSetup contains organization's config to interact with the network.
|
||||||
|
type OrgSetup struct {
|
||||||
|
OrgName string
|
||||||
|
MSPID string
|
||||||
|
CryptoPath string
|
||||||
|
CertPath string
|
||||||
|
KeyPath string
|
||||||
|
TLSCertPath string
|
||||||
|
PeerEndpoint string
|
||||||
|
GatewayPeer string
|
||||||
|
Gateway client.Gateway
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve starts http web server.
|
||||||
|
func Serve(setups OrgSetup) {
|
||||||
|
http.HandleFunc("/query", setups.Query)
|
||||||
|
http.HandleFunc("/invoke", setups.Invoke)
|
||||||
|
fmt.Println("Listening (http://localhost:3000/)...")
|
||||||
|
if err := http.ListenAndServe(":3000", nil); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
106
asset-transfer-basic/rest-api-go/web/initialize.go
Normal file
106
asset-transfer-basic/rest-api-go/web/initialize.go
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/identity"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Initialize the setup for the organization.
|
||||||
|
func Initialize(setup OrgSetup) (*OrgSetup, error) {
|
||||||
|
log.Printf("Initializing connection for %s...\n", setup.OrgName)
|
||||||
|
clientConnection := setup.newGrpcConnection()
|
||||||
|
id := setup.newIdentity()
|
||||||
|
sign := setup.newSign()
|
||||||
|
|
||||||
|
gateway, err := client.Connect(
|
||||||
|
id,
|
||||||
|
client.WithSign(sign),
|
||||||
|
client.WithClientConnection(clientConnection),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
setup.Gateway = *gateway
|
||||||
|
log.Println("Initialization complete")
|
||||||
|
return &setup, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGrpcConnection creates a gRPC connection to the Gateway server.
|
||||||
|
func (setup OrgSetup) newGrpcConnection() *grpc.ClientConn {
|
||||||
|
certificate, err := loadCertificate(setup.TLSCertPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
certPool.AddCert(certificate)
|
||||||
|
transportCredentials := credentials.NewClientTLSFromCert(certPool, setup.GatewayPeer)
|
||||||
|
|
||||||
|
connection, err := grpc.Dial(setup.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 (setup OrgSetup) newIdentity() *identity.X509Identity {
|
||||||
|
certificate, err := loadCertificate(setup.CertPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := identity.NewX509Identity(setup.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 (setup OrgSetup) newSign() identity.Sign {
|
||||||
|
files, err := ioutil.ReadDir(setup.KeyPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to read private key directory: %w", err))
|
||||||
|
}
|
||||||
|
privateKeyPEM, err := ioutil.ReadFile(path.Join(setup.KeyPath, files[0].Name()))
|
||||||
|
|
||||||
|
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 loadCertificate(filename string) (*x509.Certificate, error) {
|
||||||
|
certificatePEM, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read certificate file: %w", err)
|
||||||
|
}
|
||||||
|
return identity.CertificateFromPEM(certificatePEM)
|
||||||
|
}
|
||||||
40
asset-transfer-basic/rest-api-go/web/invoke.go
Normal file
40
asset-transfer-basic/rest-api-go/web/invoke.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Invoke handles chaincode invoke requests.
|
||||||
|
func (setup *OrgSetup) Invoke(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println("Received Invoke request")
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
fmt.Fprintf(w, "ParseForm() err: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
chainCodeName := r.FormValue("chaincodeid")
|
||||||
|
channelID := r.FormValue("channelid")
|
||||||
|
function := r.FormValue("function")
|
||||||
|
args := r.Form["args"]
|
||||||
|
fmt.Printf("channel: %s, chaincode: %s, function: %s, args: %s\n", channelID, chainCodeName, function, args)
|
||||||
|
network := setup.Gateway.GetNetwork(channelID)
|
||||||
|
contract := network.GetContract(chainCodeName)
|
||||||
|
txn_proposal, err := contract.NewProposal(function, client.WithArguments(args...))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error creating txn proposal: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txn_endorsed, err := txn_proposal.Endorse()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error endorsing txn: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txn_committed, err := txn_endorsed.Submit()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error submitting transaction: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "Transaction ID : %s Response: %s", txn_committed.TransactionID(), txn_endorsed.Result())
|
||||||
|
}
|
||||||
25
asset-transfer-basic/rest-api-go/web/query.go
Normal file
25
asset-transfer-basic/rest-api-go/web/query.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Query handles chaincode query requests.
|
||||||
|
func (setup OrgSetup) Query(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println("Received Query request")
|
||||||
|
queryParams := r.URL.Query()
|
||||||
|
chainCodeName := queryParams.Get("chaincodeid")
|
||||||
|
channelID := queryParams.Get("channelid")
|
||||||
|
function := queryParams.Get("function")
|
||||||
|
args := r.URL.Query()["args"]
|
||||||
|
fmt.Printf("channel: %s, chaincode: %s, function: %s, args: %s\n", channelID, chainCodeName, function, args)
|
||||||
|
network := setup.Gateway.GetNetwork(channelID)
|
||||||
|
contract := network.GetContract(chainCodeName)
|
||||||
|
evaluateResponse, err := contract.EvaluateTransaction(function, args...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "Response: %s", evaluateResponse)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue