Skip to content
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
7 changes: 6 additions & 1 deletion ecc_go/chaincode/enclave_go/enclave.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/hyperledger/fabric-private-chaincode/ecc_go/chaincode/enclave_go/attestation"
"github.com/hyperledger/fabric-private-chaincode/internal/crypto"
"github.com/hyperledger/fabric-private-chaincode/internal/protos"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/factory"
"github.com/hyperledger/fabric/common/flogging"
Expand All @@ -37,6 +38,7 @@ type EnclaveStub struct {
hostParams *protos.HostParameters
chaincodeParams *protos.CCParameters
fabricCryptoProvider bccsp.BCCSP
stubProvider func(shim.ChaincodeStubInterface, *pb.ChaincodeInput, *readWriteSet, StateEncryptionFunctions) shim.ChaincodeStubInterface
}

func NewEnclaveStub(cc shim.Chaincode) *EnclaveStub {
Expand All @@ -49,6 +51,9 @@ func NewEnclaveStub(cc shim.Chaincode) *EnclaveStub {
csp: crypto.GetDefaultCSP(),
ccRef: cc,
fabricCryptoProvider: cryptoProvider,
stubProvider: func(stub shim.ChaincodeStubInterface, input *pb.ChaincodeInput, rwset *readWriteSet, sep StateEncryptionFunctions) shim.ChaincodeStubInterface {
return NewFpcStubInterface(stub, input, rwset, sep)
},
}
}

Expand Down Expand Up @@ -161,7 +166,7 @@ func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface, chaincod

// Invoke chaincode
// we wrap the stub with our FpcStubInterface
fpcStub := NewFpcStubInterface(stub, cleartextChaincodeRequest.GetInput(), rwset, e.ccKeys)
fpcStub := e.stubProvider(stub, cleartextChaincodeRequest.GetInput(), rwset, e.ccKeys)
ccResponse := e.ccRef.Invoke(fpcStub)

// marshal chaincode response
Expand Down
20 changes: 20 additions & 0 deletions ecc_go/chaincode/enclave_go/skvs_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package enclave_go

import (
"github.com/hyperledger/fabric-chaincode-go/shim"
pb "github.com/hyperledger/fabric-protos-go/peer"
)

func NewSkvsStub(cc shim.Chaincode) *EnclaveStub {
enclaveStub := NewEnclaveStub(cc)
enclaveStub.stubProvider = func(stub shim.ChaincodeStubInterface, input *pb.ChaincodeInput, rwset *readWriteSet, sep StateEncryptionFunctions) shim.ChaincodeStubInterface {
return NewSkvsStubInterface(stub, input, rwset, sep)
}
return enclaveStub
}
117 changes: 117 additions & 0 deletions ecc_go/chaincode/enclave_go/skvs_stub_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package enclave_go

import (
"encoding/json"
"fmt"

"github.com/hyperledger/fabric-chaincode-go/shim"
pb "github.com/hyperledger/fabric-protos-go/peer"
)

const SKVSKey = "SKVS"

type SkvsStubInterface struct {
*FpcStubInterface
allDataOld map[string][]byte
allDataNew map[string][]byte
key string
}

func NewSkvsStubInterface(stub shim.ChaincodeStubInterface, input *pb.ChaincodeInput, rwset *readWriteSet, sep StateEncryptionFunctions) *SkvsStubInterface {
fpcStub := NewFpcStubInterface(stub, input, rwset, sep)
skvsStub := &SkvsStubInterface{
FpcStubInterface: fpcStub,
allDataOld: make(map[string][]byte),
allDataNew: make(map[string][]byte),
key: SKVSKey,
}
err := skvsStub.initSKVS()
if err != nil {
panic(fmt.Sprintf("Initializing SKVS failed, err: %v", err))
}
return skvsStub
}

func (s *SkvsStubInterface) initSKVS() error {

// get current state, this will only operate once
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it makes sense to protect the entire Init method with sync.Once?
https://pkg.go.dev/sync#Once

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this function is safe even it runs multiple time, since the
encValue, err := s.GetPublicState(s.key) will always fetch the latest value, if someone try to do something malicious by calling this init function they will just get the latest public state.
Plus I already change the init function into a private function thus I think it should be okay without the sync.Once.

encValue, err := s.GetPublicState(s.key)
if err != nil {
return err
}

// return if the key initially does not exist
if len(encValue) == 0 {
logger.Warningf("SKVS is empty, Initiating.")
Comment on lines +50 to +51
Copy link
Contributor

Choose a reason for hiding this comment

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

should we also handle this error differently? Can we actually continue if encValue is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, we can continue if the encValue is empty.

return nil
}

value, err := s.sep.DecryptState(encValue)
if err != nil {
return err
}
logger.Debug("SKVS has default value, loading current value.")

err = json.Unmarshal(value, &s.allDataOld)
if err != nil {
logger.Errorf("SKVS Json unmarshal error: %s", err)
return err
}
err = json.Unmarshal(value, &s.allDataNew)
if err != nil {
logger.Errorf("SKVS Json unmarshal error: %s", err)
return err
}
return nil
}

func (s *SkvsStubInterface) GetState(key string) ([]byte, error) {
value, found := s.allDataOld[key]
if !found {
logger.Errorf("skvs allDataOld key: %s, not found", key)
return nil, nil
}
return value, nil
}

func (s *SkvsStubInterface) PutState(key string, value []byte) error {

s.allDataNew[key] = value
byteAllData, err := json.Marshal(s.allDataNew)
if err != nil {
return err
}
encValue, err := s.sep.EncryptState(byteAllData)
if err != nil {
return err
}

return s.PutPublicState(s.key, encValue)
}

func (s *SkvsStubInterface) DelState(key string) error {
delete(s.allDataNew, key)
byteAllData, err := json.Marshal(s.allDataNew)
if err != nil {
return err
}
encValue, err := s.sep.EncryptState(byteAllData)
if err != nil {
return err
}
return s.PutPublicState(s.key, encValue)
}

func (s *SkvsStubInterface) GetStateByRange(startKey string, endKey string) (shim.StateQueryIteratorInterface, error) {
panic("not implemented") // TODO: Implement
}

func (s *SkvsStubInterface) GetStateByRangeWithPagination(startKey string, endKey string, pageSize int32, bookmark string) (shim.StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
panic("not implemented") // TODO: Implement
}
14 changes: 12 additions & 2 deletions ecc_go/chaincode/private.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,24 @@ import (
"github.com/hyperledger/fabric-private-chaincode/internal/endorsement"
)

type BuildOption func(*chaincode.EnclaveChaincode, shim.Chaincode)

// NewPrivateChaincode creates a new chaincode! This is for go support only!!!
func NewPrivateChaincode(cc shim.Chaincode) *chaincode.EnclaveChaincode {
func NewPrivateChaincode(cc shim.Chaincode, options ...BuildOption) *chaincode.EnclaveChaincode {
ecc := &chaincode.EnclaveChaincode{
Enclave: enclave_go.NewEnclaveStub(cc),
Validator: endorsement.NewValidator(),
Extractor: &chaincode.ExtractorImpl{},
Ercc: &ercc.StubImpl{},
}

for _, o := range options {
o(ecc, cc)
}
return ecc
}

func WithSKVS() BuildOption {
return func(ecc *chaincode.EnclaveChaincode, cc shim.Chaincode) {
ecc.Enclave = enclave_go.NewSkvsStub(cc)
}
}
9 changes: 8 additions & 1 deletion samples/chaincode/secret-keeper-go/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
# SPDX-License-Identifier: Apache-2.0

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

CC_NAME ?= fpc-secret-keeper-go

# Define paths for cmd subdirectories
DEFAULT= cmd/naive/main.go
SKVS_PATH = cmd/skvs/main.go

ECC_MAIN_FILES ?=$(DEFAULT)

include $(TOP)/ecc_go/build.mk
41 changes: 41 additions & 0 deletions samples/chaincode/secret-keeper-go/cmd/skvs/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright IBM Corp. All Rights Reserved.
Copyright 2020 Intel Corporation

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
"os"

"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
fpc "github.com/hyperledger/fabric-private-chaincode/ecc_go/chaincode"
"github.com/hyperledger/fabric-private-chaincode/samples/chaincode/secret-keeper-go/chaincode"
)

func main() {

ccid := os.Getenv("CHAINCODE_PKG_ID")
addr := os.Getenv("CHAINCODE_SERVER_ADDRESS")

// create chaincode
secretChaincode, _ := contractapi.NewChaincode(&chaincode.SecretKeeper{})
skvsChaincode := fpc.NewPrivateChaincode(secretChaincode, fpc.WithSKVS())

// start chaincode as a service
server := &shim.ChaincodeServer{
CCID: ccid,
Address: addr,
CC: skvsChaincode,
TLSProps: shim.TLSProperties{
Disabled: true, // just for testing good enough
},
}

if err := server.Start(); err != nil {
panic(err)
}
}