Skip to content

Commit

Permalink
Merge Solution Single Key Value Storage (SKVS) (#3)
Browse files Browse the repository at this point in the history
Create a solution for protecting against rollback attack.
Solution: Single Key Value Storage (SKVS)

Just need to apply one line of change in your chaincode main.go to use SKVS solution:
```
secretChaincode, _ := contractapi.NewChaincode(&chaincode.SecretKeeper{})
chaincode := fpc.NewSkvsChaincode(secretChaincode)
```

Skvs solution will store all the key under the main key "SKVS".
before: {"Alice": "DataAlice", "Bob": "DataBob"}
After: {"SKVS": {"Alice": "DataAlice", "Bob": "DataBob"} }

By using this approach we are able to protect against Rollback + key mismatch problem since all the data are now just store under one single key.

Special notes for your reviewer:
This approach is very user friendly because you just need to change one line of your main application to get the properties of rollback protection.
However, which this approach will reduce the performance of FPC a lot because it didn't perform well in concurrent situation because every changes will cause a conflict in their Transactions happened to be in a same block.
Thus if developer would like to use this approach will need to make sure your application doesn't required high throughput and will expected it to update once a while.
----
* add singleKVS wrapper
* able to save into single key
* change to only getState Once
* change storing type and add del state
* refactor part 1 - private can run
* refactor part 2 - skvs run successfully
* remove unnecessary logging change back samples

Signed-off-by: chenchanglew <[email protected]>
Signed-off-by: chenchanglew <[email protected]>
  • Loading branch information
chenchanglew committed Oct 10, 2023
1 parent da44436 commit 5e48d42
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 4 deletions.
8 changes: 6 additions & 2 deletions 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,9 +38,10 @@ type EnclaveStub struct {
hostParams *protos.HostParameters
chaincodeParams *protos.CCParameters
fabricCryptoProvider bccsp.BCCSP
newStubInterfaceFunc func(stub shim.ChaincodeStubInterface, input *pb.ChaincodeInput, rwset *readWriteSet, sep StateEncryptionFunctions) shim.ChaincodeStubInterface
}

func NewEnclaveStub(cc shim.Chaincode) *EnclaveStub {
func NewEnclaveStub(cc shim.Chaincode, newStubInterfaceFunc func(stub shim.ChaincodeStubInterface, input *pb.ChaincodeInput, rwset *readWriteSet, sep StateEncryptionFunctions) shim.ChaincodeStubInterface) *EnclaveStub {
if err := factory.InitFactories(nil); err != nil {
panic(err)
}
Expand All @@ -49,6 +51,7 @@ func NewEnclaveStub(cc shim.Chaincode) *EnclaveStub {
csp: crypto.GetDefaultCSP(),
ccRef: cc,
fabricCryptoProvider: cryptoProvider,
newStubInterfaceFunc: newStubInterfaceFunc,
}
}

Expand Down Expand Up @@ -161,7 +164,8 @@ 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.newStubInterfaceFunc(stub, cleartextChaincodeRequest.GetInput(), rwset, e.ccKeys)
// fpcStub := NewFpcStubInterface(stub, cleartextChaincodeRequest.GetInput(), rwset, e.ccKeys)
ccResponse := e.ccRef.Invoke(fpcStub)

// marshal chaincode response
Expand Down
2 changes: 1 addition & 1 deletion ecc_go/chaincode/enclave_go/shim.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type FpcStubInterface struct {
sep StateEncryptionFunctions
}

func NewFpcStubInterface(stub shim.ChaincodeStubInterface, input *pb.ChaincodeInput, rwset *readWriteSet, sep StateEncryptionFunctions) *FpcStubInterface {
func NewFpcStubInterface(stub shim.ChaincodeStubInterface, input *pb.ChaincodeInput, rwset *readWriteSet, sep StateEncryptionFunctions) shim.ChaincodeStubInterface {
return &FpcStubInterface{
stub: stub,
input: input,
Expand Down
98 changes: 98 additions & 0 deletions ecc_go/chaincode/enclave_go/skvs_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package enclave_go

import (
"encoding/json"

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

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) shim.ChaincodeStubInterface {
logger.Debugf("===== Creating New Skvs Interface =====")
fpcStub := NewFpcStubInterface(stub, input, rwset, sep)
skvsStub := SkvsStubInterface{fpcStub.(*FpcStubInterface), map[string][]byte{}, map[string][]byte{}, "SKVS"}
err := skvsStub.InitSKVS()
if err != nil {
logger.Warningf("Error!! Initializing SKVS failed")
}
return &skvsStub
}

func (s *SkvsStubInterface) InitSKVS() error {
// get current state, this will only operate once
encValue, err := s.GetPublicState(s.key)
if err != nil {
return nil
}

if len(encValue) == 0 {
logger.Debugf("SKVS is empty, Initiating.")
} else {
value, err := s.sep.DecryptState(encValue)
if err != nil {
return err
}
logger.Debugf("SKVS has default value, loading current value.")

err = json.Unmarshal(value, &s.allDataOld)
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 := s.allDataOld[key]
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
}
3 changes: 2 additions & 1 deletion ecc_go/chaincode/private.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import (

// NewPrivateChaincode creates a new chaincode! This is for go support only!!!
func NewPrivateChaincode(cc shim.Chaincode) *chaincode.EnclaveChaincode {
newStubInterfaceFunc := enclave_go.NewFpcStubInterface
ecc := &chaincode.EnclaveChaincode{
Enclave: enclave_go.NewEnclaveStub(cc),
Enclave: enclave_go.NewEnclaveStub(cc, newStubInterfaceFunc),
Validator: endorsement.NewValidator(),
Extractor: &chaincode.ExtractorImpl{},
Ercc: &ercc.StubImpl{},
Expand Down
29 changes: 29 additions & 0 deletions ecc_go/chaincode/singleKVS.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package chaincode

import (
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-private-chaincode/ecc/chaincode"
"github.com/hyperledger/fabric-private-chaincode/ecc/chaincode/ercc"
"github.com/hyperledger/fabric-private-chaincode/ecc_go/chaincode/enclave_go"
"github.com/hyperledger/fabric-private-chaincode/internal/endorsement"
"github.com/hyperledger/fabric/common/flogging"
)

var logger = flogging.MustGetLogger("enclave_go")

func NewSkvsChaincode(cc shim.Chaincode) *chaincode.EnclaveChaincode {
newStubInterfaceFunc := enclave_go.NewSkvsStubInterface
ecc := &chaincode.EnclaveChaincode{
Enclave: enclave_go.NewEnclaveStub(cc, newStubInterfaceFunc),
Validator: endorsement.NewValidator(),
Extractor: &chaincode.ExtractorImpl{},
Ercc: &ercc.StubImpl{},
}
return ecc
}
1 change: 1 addition & 0 deletions samples/chaincode/secret-keeper-go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func main() {
// create chaincode
secretChaincode, _ := contractapi.NewChaincode(&chaincode.SecretKeeper{})
chaincode := fpc.NewPrivateChaincode(secretChaincode)
// chaincode := fpc.NewSkvsChaincode(secretChaincode)

// start chaincode as a service
server := &shim.ChaincodeServer{
Expand Down

0 comments on commit 5e48d42

Please sign in to comment.