Skip to content

Commit

Permalink
Add initial client go sdk prototype
Browse files Browse the repository at this point in the history
- initial go sdk extension for FPC that hides the FPC transaction flow
  from the user
- integrate with test-network
- CreateEnclave requires target peer endpoint
- FPC invocations are invoked (queried) at a defined endpoint
- Update go sdk version and address review

Signed-off-by: bur <[email protected]>
Co-authored-by: Michael Steiner <[email protected]>
  • Loading branch information
mbrandenburger and g2flyer committed Oct 27, 2020
1 parent fdccf21 commit f95c5e5
Show file tree
Hide file tree
Showing 10 changed files with 624 additions and 2 deletions.
11 changes: 11 additions & 0 deletions client_sdk/go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# FPC Go SDK

we build on top of the new gateway API of the Fabric Go SDK


Ensure you use the at least the following go sdk version or newer.
```go
go get -u github.com/hyperledger/fabric-sdk-go@2f93a3201bb40cf30b7c73dd402511ed788cceca
```

## Testing
20 changes: 20 additions & 0 deletions client_sdk/go/fpc/attestation/transformation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package attestation

import (
"log"

"github.com/hyperledger-labs/fabric-private-chaincode/internal/protos"
)

func ToEvidence(credentials *protos.Credentials) (*protos.Credentials, error) {

// do something
log.Printf("Perform attestation to evidence transformation\n")

// TODO call brunos attestation_to_evidence.sh
// call $FPC_PATH/common/crypto/attestation-api/conversion/attestation_to_evidence.sh

//credentials.Evidence = credentials.Attestation

return credentials, nil
}
188 changes: 188 additions & 0 deletions client_sdk/go/fpc/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
Copyright 2020 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package fpc

import (
"encoding/base64"
"encoding/json"
"log"

"github.com/hyperledger-labs/fabric-private-chaincode/internal/utils"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
"github.com/pkg/errors"
)

type ContractInterface interface {
ManagementInterface
Name() string
EvaluateTransaction(name string, args ...string) ([]byte, error)
SubmitTransaction(name string, args ...string) ([]byte, error)
//CreateTransaction(name string, opts ...gateway.TransactionOption) (*gateway.Transaction, error)
RegisterEvent(eventFilter string) (fab.Registration, <-chan *fab.CCEvent, error)
Unregister(registration fab.Registration)
}

func GetContract(network *gateway.Network, chaincodeId string) ContractInterface {
contract := network.GetContract(chaincodeId)
ercc := network.GetContract("ercc")
return &Contract{contract, ercc, nil, nil}
}

type Contract struct {
contract *gateway.Contract
ercc *gateway.Contract
cachedChaincodeEncryptionKey []byte
enclavePeers []string
}

func (c *Contract) Name() string {
return c.contract.Name()
}

func (c *Contract) getChaincodeEncryptionKey() ([]byte, error) {
if c.cachedChaincodeEncryptionKey == nil {
ccKeyBytes, err := c.ercc.EvaluateTransaction("queryChaincodeEncryptionKey", c.Name())
if err != nil {
return nil, err
}
c.cachedChaincodeEncryptionKey = ccKeyBytes
}
return c.cachedChaincodeEncryptionKey, nil
}

func (c *Contract) getEnclavePeers() ([]string, error) {
if c.enclavePeers == nil {
// TODO: implement me to support multi-peer scenarios (currently createEnclave also populates c.enclavePeers ...
}
return c.enclavePeers, nil
}

// TODO better move to TX. crypto lib?! TBD
func (c *Contract) prepareChaincodeInvocation(name string, args []string, resultEncryptionKey []byte) (string, error) {
p := &utils.ChaincodeParams{
Function: name,
Args: args,
ResultEncryptionKey: resultEncryptionKey,
}

pBytes, err := json.Marshal(p)
if err != nil {
return "", err
}

log.Printf("prepping chaincode params: %s\n", p)

k, err := c.getChaincodeEncryptionKey()
if err != nil {
return "", err
}

encryptedParams, err := Encrypt(pBytes, k)
if err != nil {
return "", err
}

return base64.StdEncoding.EncodeToString(encryptedParams), nil
}

func (c *Contract) EvaluateTransaction(name string, args ...string) ([]byte, error) {

// pick response encryption key
resultEncryptionKey, err := KeyGen()
if err != nil {
return nil, err
}

encryptedParamsBase64, err := c.prepareChaincodeInvocation(name, args, resultEncryptionKey)
if err != nil {
return nil, err
}

// note that WithEndorsingPeers is only used with txn.Submit!!!
// GO SDK needs to be patched! We should create a PR for that!
txn, err := c.contract.CreateTransaction(
"__invoke",
gateway.WithEndorsingPeers(c.enclavePeers...),
)
if err != nil {
return nil, err
}

log.Printf("calling __invoke!\n")
responseBytes, err := txn.Evaluate(encryptedParamsBase64)
if err != nil {
return nil, err
}

// TODO maybe move this to a sub-function like prepareChaincodeInvocation?! TBD
response, err := utils.UnmarshalResponse(responseBytes)
if err != nil {
return nil, err
}

// decrypt result
return Decrypt(response.ResponseData, resultEncryptionKey)
}

func (c *Contract) SubmitTransaction(name string, args ...string) ([]byte, error) {

// pick response encryption key
resultEncryptionKey, err := KeyGen()
if err != nil {
return nil, err
}

encryptedParamsBase64, err := c.prepareChaincodeInvocation(name, args, resultEncryptionKey)
if err != nil {
return nil, err
}

txn, err := c.contract.CreateTransaction(
"__invoke",
gateway.WithEndorsingPeers(c.enclavePeers...),
)
if err != nil {
return nil, err
}

log.Printf("calling __invoke!\n")
//responseBytes, err := c.contract.EvaluateTransaction("__invoke", encryptedParamsBase64)
responseBytes, err := txn.Evaluate(encryptedParamsBase64)

// first invoke (query) fpc chaincode
//responseBytes, err := c.contract.EvaluateTransaction("__invoke", encryptedParamsBase64)
if err != nil {
return nil, errors.Wrap(err, "evaluation transaction failed")
}

log.Printf("calling __endorse!\n")
// next invoke chaincode endorsement
_, err = c.contract.SubmitTransaction("__endorse", base64.StdEncoding.EncodeToString(responseBytes))
if err != nil {
return nil, err
}

response, err := utils.UnmarshalResponse(responseBytes)
if err != nil {
return nil, err
}

return Decrypt(response.ResponseData, resultEncryptionKey)
}

//func (c *Contract) CreateTransaction(name string, opts ...gateway.TransactionOption) (*gateway.Transaction, error) {
// return c.CreateTransaction(name, opts...)
//}

func (c *Contract) RegisterEvent(eventFilter string) (fab.Registration, <-chan *fab.CCEvent, error) {
return c.contract.RegisterEvent(eventFilter)
}

func (c *Contract) Unregister(registration fab.Registration) {
c.contract.Unregister(registration)
}
13 changes: 13 additions & 0 deletions client_sdk/go/fpc/crypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fpc

func Encrypt(input []byte, encryptionKey []byte) ([]byte, error) {
return input, nil
}

func KeyGen() ([]byte, error) {
return []byte("fake key"), nil
}

func Decrypt(encryptedResponse []byte, resultEncryptionKey []byte) ([]byte, error) {
return encryptedResponse, nil
}
70 changes: 70 additions & 0 deletions client_sdk/go/fpc/lifecycle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package fpc

import (
"encoding/base64"
"encoding/json"
"fmt"
"log"

"github.com/hyperledger-labs/fabric-private-chaincode/client_sdk/go/fpc/attestation"
"github.com/hyperledger-labs/fabric-private-chaincode/internal/utils"

"github.com/golang/protobuf/proto"
"github.com/hyperledger-labs/fabric-private-chaincode/internal/protos"
"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
"github.com/hyperledger/fabric/protoutil"
)

type ManagementInterface interface {
CreateEnclave(peer string, attestationParams ...string) error
}

type ManagementAPI struct {
network *gateway.Network
}

func (c *Contract) CreateEnclave(peer string, attestationParams ...string) error {

p, err := json.Marshal(&utils.AttestationParams{Params: attestationParams})
attestationParamsBase64 := base64.StdEncoding.EncodeToString(p)
log.Printf("Prep attestation params: %s\n", attestationParamsBase64)

txn, err := c.contract.CreateTransaction(
"__initEnclave",
gateway.WithEndorsingPeers(peer),
)
if err != nil {
return err
}

credentialsBytes, err := txn.Evaluate(attestationParamsBase64)
if err != nil {
return fmt.Errorf("evaluation error: %s", err)
}

credentials := &protos.Credentials{}
err = proto.Unmarshal(credentialsBytes, credentials)
if err != nil {
return fmt.Errorf("cannot unmarshal credentials: %s", err)
}
log.Printf("Received credentials from enclave: %s\n", credentials)

// perform attestation evidence transformation
credentials, err = attestation.ToEvidence(credentials)
if err != nil {
return err
}

credentialsBytes = protoutil.MarshalOrPanic(credentials)
credentialsBase64 := base64.StdEncoding.EncodeToString(credentialsBytes)

log.Printf("Call registerEnclave at ERCC: %s\n", attestationParamsBase64)
_, err = c.ercc.SubmitTransaction("registerEnclave", credentialsBase64)
if err != nil {
return err
}

c.enclavePeers = append(c.enclavePeers, peer)

return nil
}
20 changes: 20 additions & 0 deletions client_sdk/go/test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2019 Intel Corporation
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0

TOP = ../../..
include $(TOP)/build.mk

build:
$(GO) build

test:
$(GO) test $(GOTAGS) -v ./...

clean:
$(GO) clean
rm -rf keystore wallet

run:
$(GO) run .
Loading

0 comments on commit f95c5e5

Please sign in to comment.