Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial client go sdk prototype #452

Merged
merged 1 commit into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
g2flyer marked this conversation as resolved.
Show resolved Hide resolved
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) {
mbrandenburger marked this conversation as resolved.
Show resolved Hide resolved
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)
mbrandenburger marked this conversation as resolved.
Show resolved Hide resolved
}

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
}
Comment on lines +1 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you assume we surface pdo crypto via cgo? As discussed in #412, i thought conceptually bridging at a higher-level (for arguments discussed there). The devil is in the details and what i propose might not work. However, i feel we should make a conscious decision on this interface. Right now it seems to me we end up making an implicit decision while not necessarily have considered the implication (maybe you did, but then i think we should reflect it in #412 and also resolve it there?)

To some extent it also seems to related on what exactly the semantics is of the "Transaction Crypto Library" boxes in mural?

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
}

g2flyer marked this conversation as resolved.
Show resolved Hide resolved
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)
Comment on lines +45 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be worth "outsourcing" this in a separate function as a simply cmd-line utility wrapper would be exactly the transform you would need in the peer cli (i.e., only a single and simple implementation and also a simplification of the peer cli script ...


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