1481 lines
59 KiB
Go
1481 lines
59 KiB
Go
/*
|
|
Copyright IBM Corp All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package pvtdata
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
docker "github.com/fsouza/go-dockerclient"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/ptypes"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric-protos-go/ledger/rwset"
|
|
"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
|
|
mspp "github.com/hyperledger/fabric-protos-go/msp"
|
|
ab "github.com/hyperledger/fabric-protos-go/orderer"
|
|
pb "github.com/hyperledger/fabric-protos-go/peer"
|
|
"github.com/hyperledger/fabric/common/crypto"
|
|
"github.com/hyperledger/fabric/core/ledger/util"
|
|
"github.com/hyperledger/fabric/integration/channelparticipation"
|
|
"github.com/hyperledger/fabric/integration/nwo"
|
|
"github.com/hyperledger/fabric/integration/nwo/commands"
|
|
"github.com/hyperledger/fabric/integration/pvtdata/marblechaincodeutil"
|
|
"github.com/hyperledger/fabric/msp"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/onsi/gomega/gbytes"
|
|
"github.com/onsi/gomega/gexec"
|
|
"github.com/pkg/errors"
|
|
"github.com/tedsuo/ifrit"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
// The chaincode used in these tests has two collections defined:
|
|
// collectionMarbles and collectionMarblePrivateDetails
|
|
// when calling QueryChaincode with first arg "readMarble", it will query collectionMarbles
|
|
// when calling QueryChaincode with first arg "readMarblePrivateDetails", it will query collectionMarblePrivateDetails
|
|
|
|
const channelID = "testchannel"
|
|
|
|
var _ bool = Describe("PrivateData", func() {
|
|
var (
|
|
network *nwo.Network
|
|
ordererProcess, peerProcess ifrit.Process
|
|
orderer *nwo.Orderer
|
|
)
|
|
|
|
AfterEach(func() {
|
|
testCleanup(network, ordererProcess, peerProcess)
|
|
})
|
|
|
|
Describe("Dissemination when pulling and reconciliation are disabled", func() {
|
|
BeforeEach(func() {
|
|
By("setting up the network")
|
|
network = initThreeOrgsSetup(true)
|
|
|
|
By("setting the pull retry threshold to 0 and disabling reconciliation on all peers")
|
|
for _, p := range network.Peers {
|
|
core := network.ReadPeerConfig(p)
|
|
core.Peer.Gossip.PvtData.PullRetryThreshold = 0
|
|
core.Peer.Gossip.PvtData.ReconciliationEnabled = false
|
|
network.WritePeerConfig(p, core)
|
|
}
|
|
|
|
By("starting the network")
|
|
ordererProcess, peerProcess, orderer = startNetwork(network)
|
|
})
|
|
|
|
It("disseminates private data per collections_config1 (positive test) and collections_config8 (negative test)", func() {
|
|
By("deploying legacy chaincode and adding marble1")
|
|
testChaincode := chaincode{
|
|
Chaincode: nwo.Chaincode{
|
|
Name: "marblesp",
|
|
Version: "1.0",
|
|
Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd",
|
|
Ctor: `{"Args":["init"]}`,
|
|
Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
// collections_config1.json defines the access as follows:
|
|
// 1. collectionMarbles - Org1, Org2 have access to this collection
|
|
// 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection
|
|
CollectionsConfig: CollectionConfig("collections_config1.json"),
|
|
},
|
|
isLegacy: true,
|
|
}
|
|
deployChaincode(network, orderer, testChaincode)
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name,
|
|
`{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`,
|
|
network.Peer("Org1", "peer0"),
|
|
)
|
|
|
|
assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1")
|
|
|
|
By("deploying chaincode with RequiredPeerCount greater than number of peers, endorsement will fail")
|
|
testChaincodeHighRequiredPeerCount := chaincode{
|
|
Chaincode: nwo.Chaincode{
|
|
Name: "marblespHighRequiredPeerCount",
|
|
Version: "1.0",
|
|
Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd",
|
|
Ctor: `{"Args":["init"]}`,
|
|
Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
CollectionsConfig: CollectionConfig("collections_config8_high_requiredPeerCount.json"),
|
|
},
|
|
isLegacy: true,
|
|
}
|
|
deployChaincode(network, orderer, testChaincodeHighRequiredPeerCount)
|
|
|
|
// attempt to add a marble with insufficient dissemination to meet RequiredPeerCount
|
|
marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(`{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`))
|
|
|
|
command := commands.ChaincodeInvoke{
|
|
ChannelID: channelID,
|
|
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: testChaincodeHighRequiredPeerCount.Name,
|
|
Ctor: `{"Args":["initMarble"]}`,
|
|
Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64),
|
|
PeerAddresses: []string{
|
|
network.PeerAddress(network.Peer("Org1", "peer0"), nwo.ListenPort),
|
|
},
|
|
WaitForEvent: true,
|
|
}
|
|
expectedErrMsg := `Error: endorsement failure during invoke. response: status:500 message:"error in simulation: failed to distribute private collection`
|
|
invokeChaincodeExpectErr(network, network.Peer("Org1", "peer0"), command, expectedErrMsg)
|
|
})
|
|
|
|
When("collection config does not have maxPeerCount or requiredPeerCount", func() {
|
|
It("disseminates private data per collections_config7 with default maxPeerCount and requiredPeerCount", func() {
|
|
By("deploying legacy chaincode and adding marble1")
|
|
testChaincode := chaincode{
|
|
Chaincode: nwo.Chaincode{
|
|
Name: "marblesp",
|
|
Version: "1.0",
|
|
Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd",
|
|
Ctor: `{"Args":["init"]}`,
|
|
Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
// collections_config1.json defines the access as follows:
|
|
// 1. collectionMarbles - Org1, Org2 have access to this collection
|
|
// 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection
|
|
CollectionsConfig: CollectionConfig("collections_config7.json"),
|
|
},
|
|
isLegacy: true,
|
|
}
|
|
deployChaincode(network, orderer, testChaincode)
|
|
peer := network.Peer("Org1", "peer0")
|
|
By("adding marble1")
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name,
|
|
`{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`,
|
|
peer,
|
|
)
|
|
|
|
By("asserting pvtdata in each collection for config7")
|
|
assertPvtdataPresencePerCollectionConfig7(network, testChaincode.Name, "marble1", peer)
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("Pvtdata behavior when a peer with new certs joins the network", func() {
|
|
var peerProcesses map[string]ifrit.Process
|
|
|
|
BeforeEach(func() {
|
|
By("setting up the network")
|
|
network = initThreeOrgsSetup(true)
|
|
|
|
By("starting the network")
|
|
peerProcesses = make(map[string]ifrit.Process)
|
|
network.Bootstrap()
|
|
|
|
orderer = network.Orderer("orderer")
|
|
ordererRunner := network.OrdererRunner(orderer)
|
|
ordererProcess = ifrit.Invoke(ordererRunner)
|
|
Eventually(ordererProcess.Ready()).Should(BeClosed())
|
|
|
|
org1peer0 := network.Peer("Org1", "peer0")
|
|
org2peer0 := network.Peer("Org2", "peer0")
|
|
org3peer0 := network.Peer("Org3", "peer0")
|
|
|
|
testPeers := []*nwo.Peer{org1peer0, org2peer0, org3peer0}
|
|
for _, peer := range testPeers {
|
|
pr := network.PeerRunner(peer)
|
|
p := ifrit.Invoke(pr)
|
|
peerProcesses[peer.ID()] = p
|
|
Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
}
|
|
|
|
channelparticipation.JoinOrdererJoinPeersAppChannel(network, channelID, orderer, ordererRunner)
|
|
|
|
By("verifying membership")
|
|
network.VerifyMembership(network.Peers, channelID)
|
|
|
|
By("installing and instantiating chaincode on all peers")
|
|
testChaincode := chaincode{
|
|
Chaincode: nwo.Chaincode{
|
|
Name: "marblesp",
|
|
Version: "1.0",
|
|
Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd",
|
|
Ctor: `{"Args":["init"]}`,
|
|
Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
CollectionsConfig: filepath.Join("testdata", "collection_configs", "collections_config1.json"),
|
|
},
|
|
isLegacy: true,
|
|
}
|
|
deployChaincode(network, orderer, testChaincode)
|
|
|
|
By("adding marble1 with an org 1 peer as endorser")
|
|
peer := network.Peer("Org1", "peer0")
|
|
marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, marbleDetails, peer)
|
|
|
|
By("waiting for block to propagate")
|
|
nwo.WaitUntilEqualLedgerHeight(network, channelID, nwo.GetLedgerHeight(network, network.Peers[0], channelID), network.Peers...)
|
|
|
|
org2Peer1 := &nwo.Peer{
|
|
Name: "peer1",
|
|
Organization: "Org2",
|
|
Channels: []*nwo.PeerChannel{}, // Don't set channels here so the UpdateConfig call doesn't try to fetch blocks for org2Peer1 with the default Admin user
|
|
}
|
|
network.Peers = append(network.Peers, org2Peer1)
|
|
})
|
|
|
|
AfterEach(func() {
|
|
for _, peerProcess := range peerProcesses {
|
|
if peerProcess != nil {
|
|
peerProcess.Signal(syscall.SIGTERM)
|
|
Eventually(peerProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
}
|
|
}
|
|
})
|
|
|
|
It("verifies private data is pulled when joining a new peer with new certs", func() {
|
|
By("generating new certs for org2Peer1")
|
|
org2Peer1 := network.Peer("Org2", "peer1")
|
|
tempCryptoDir, err := ioutil.TempDir("", "crypto")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
defer os.RemoveAll(tempCryptoDir)
|
|
generateNewCertsForPeer(network, tempCryptoDir, org2Peer1)
|
|
|
|
By("updating the channel config with the new certs")
|
|
updateConfigWithNewCertsForPeer(network, tempCryptoDir, orderer, org2Peer1)
|
|
|
|
By("starting the peer1.org2 process")
|
|
pr := network.PeerRunner(org2Peer1)
|
|
p := ifrit.Invoke(pr)
|
|
peerProcesses[org2Peer1.ID()] = p
|
|
Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
|
|
By("joining peer1.org2 to the channel with its Admin2 user")
|
|
tempFile, err := ioutil.TempFile("", "genesis-block")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
tempFile.Close()
|
|
defer os.Remove(tempFile.Name())
|
|
|
|
sess, err := network.PeerUserSession(org2Peer1, "Admin2", commands.ChannelFetch{
|
|
Block: "0",
|
|
ChannelID: channelID,
|
|
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
|
|
OutputFile: tempFile.Name(),
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
|
|
sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChannelJoin{
|
|
BlockPath: tempFile.Name(),
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
|
|
org2Peer1.Channels = append(org2Peer1.Channels, &nwo.PeerChannel{Name: channelID, Anchor: false})
|
|
|
|
ledgerHeight := nwo.GetLedgerHeight(network, network.Peers[0], channelID)
|
|
|
|
By("fetching latest blocks to peer1.org2")
|
|
// Retry channel fetch until peer1.org2 retrieves latest block
|
|
// Channel Fetch will repeatedly fail until org2Peer1 commits the config update adding its new cert
|
|
Eventually(fetchBlocksForPeer(network, org2Peer1, "Admin2"), network.EventuallyTimeout).Should(gbytes.Say(fmt.Sprintf("Received block: %d", ledgerHeight-1)))
|
|
|
|
By("installing chaincode on peer1.org2 to be able to query it")
|
|
chaincode := nwo.Chaincode{
|
|
Name: "marblesp",
|
|
Version: "1.0",
|
|
Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd",
|
|
Ctor: `{"Args":["init"]}`,
|
|
Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
CollectionsConfig: filepath.Join("testdata", "collection_configs", "collections_config1.json"),
|
|
}
|
|
|
|
sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeInstallLegacy{
|
|
Name: chaincode.Name,
|
|
Version: chaincode.Version,
|
|
Path: chaincode.Path,
|
|
Lang: chaincode.Lang,
|
|
PackageFile: chaincode.PackageFile,
|
|
ClientAuth: network.ClientAuthRequired,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
|
|
sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeListInstalledLegacy{
|
|
ClientAuth: network.ClientAuthRequired,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
Expect(sess).To(gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", chaincode.Name, chaincode.Version)))
|
|
|
|
expectedPeers := []*nwo.Peer{
|
|
network.Peer("Org1", "peer0"),
|
|
network.Peer("Org2", "peer0"),
|
|
network.Peer("Org2", "peer1"),
|
|
network.Peer("Org3", "peer0"),
|
|
}
|
|
|
|
By("making sure all peers have the same ledger height")
|
|
for _, peer := range expectedPeers {
|
|
Eventually(func() int {
|
|
var (
|
|
sess *gexec.Session
|
|
err error
|
|
)
|
|
if peer.ID() == "Org2.peer1" {
|
|
// use Admin2 user for peer1.org2
|
|
sess, err = network.PeerUserSession(peer, "Admin2", commands.ChannelInfo{
|
|
ChannelID: channelID,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
channelInfoStr := strings.TrimPrefix(string(sess.Buffer().Contents()[:]), "Blockchain info:")
|
|
channelInfo := cb.BlockchainInfo{}
|
|
err = json.Unmarshal([]byte(channelInfoStr), &channelInfo)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
return int(channelInfo.Height)
|
|
}
|
|
|
|
// If not Org2.peer1, just use regular getLedgerHeight call with User1
|
|
return nwo.GetLedgerHeight(network, peer, channelID)
|
|
}(), network.EventuallyTimeout).Should(Equal(ledgerHeight))
|
|
}
|
|
|
|
By("verifying membership")
|
|
expectedDiscoveredPeers := make([]nwo.DiscoveredPeer, 0, len(expectedPeers))
|
|
for _, peer := range expectedPeers {
|
|
expectedDiscoveredPeers = append(expectedDiscoveredPeers, network.DiscoveredPeer(peer, "_lifecycle", "marblesp"))
|
|
}
|
|
for _, peer := range expectedPeers {
|
|
By(fmt.Sprintf("checking expected peers for peer: %s", peer.ID()))
|
|
if peer.ID() == "Org2.peer1" {
|
|
// use Admin2 user for peer1.org2
|
|
Eventually(nwo.DiscoverPeers(network, peer, "Admin2", channelID), network.EventuallyTimeout).Should(ConsistOf(expectedDiscoveredPeers))
|
|
} else {
|
|
Eventually(nwo.DiscoverPeers(network, peer, "User1", channelID), network.EventuallyTimeout).Should(ConsistOf(expectedDiscoveredPeers))
|
|
}
|
|
}
|
|
|
|
By("verifying peer1.org2 got the private data that was created historically")
|
|
sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeQuery{
|
|
ChannelID: channelID,
|
|
Name: "marblesp",
|
|
Ctor: `{"Args":["readMarble","marble1"]}`,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
Expect(sess).To(gbytes.Say(`{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}`))
|
|
|
|
sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeQuery{
|
|
ChannelID: channelID,
|
|
Name: "marblesp",
|
|
Ctor: `{"Args":["readMarblePrivateDetails","marble1"]}`,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
Expect(sess).To(gbytes.Say(`{"docType":"marblePrivateDetails","name":"marble1","price":99}`))
|
|
})
|
|
})
|
|
|
|
Describe("Pvtdata behavior with untouched peer configs", func() {
|
|
var (
|
|
legacyChaincode nwo.Chaincode
|
|
newLifecycleChaincode nwo.Chaincode
|
|
testChaincode chaincode
|
|
|
|
org1Peer1, org2Peer1 *nwo.Peer
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
By("setting up the network")
|
|
network = initThreeOrgsSetup(true)
|
|
legacyChaincode = nwo.Chaincode{
|
|
Name: "marblesp",
|
|
Version: "1.0",
|
|
Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd",
|
|
Ctor: `{"Args":["init"]}`,
|
|
Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
// collections_config1.json defines the access as follows:
|
|
// 1. collectionMarbles - Org1, Org2 have access to this collection
|
|
// 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection
|
|
CollectionsConfig: CollectionConfig("collections_config1.json"),
|
|
}
|
|
|
|
newLifecycleChaincode = nwo.Chaincode{
|
|
Name: "marblesp",
|
|
Version: "1.0",
|
|
Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd"),
|
|
Lang: "binary",
|
|
PackageFile: filepath.Join(network.RootDir, "marbles-pvtdata.tar.gz"),
|
|
Label: "marbles-private-20",
|
|
SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
CollectionsConfig: CollectionConfig("collections_config1.json"),
|
|
Sequence: "1",
|
|
}
|
|
org1Peer1 = &nwo.Peer{
|
|
Name: "peer1",
|
|
Organization: "Org1",
|
|
Channels: []*nwo.PeerChannel{
|
|
{Name: channelID},
|
|
},
|
|
}
|
|
org2Peer1 = &nwo.Peer{
|
|
Name: "peer1",
|
|
Organization: "Org2",
|
|
Channels: []*nwo.PeerChannel{
|
|
{Name: channelID},
|
|
},
|
|
}
|
|
|
|
ordererProcess, peerProcess, orderer = startNetwork(network)
|
|
})
|
|
|
|
Describe("Reconciliation and pulling", func() {
|
|
var newPeerProcess ifrit.Process
|
|
|
|
BeforeEach(func() {
|
|
By("deploying legacy chaincode and adding marble1")
|
|
testChaincode = chaincode{
|
|
Chaincode: legacyChaincode,
|
|
isLegacy: true,
|
|
}
|
|
deployChaincode(network, orderer, testChaincode)
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name,
|
|
`{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`,
|
|
network.Peer("Org1", "peer0"),
|
|
)
|
|
})
|
|
|
|
AfterEach(func() {
|
|
if newPeerProcess != nil {
|
|
newPeerProcess.Signal(syscall.SIGTERM)
|
|
Eventually(newPeerProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
}
|
|
})
|
|
|
|
assertReconcileBehavior := func() {
|
|
It("disseminates private data per collections_config1", func() {
|
|
assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1")
|
|
})
|
|
|
|
When("org3 is added to collectionMarbles via chaincode upgrade with collections_config2", func() {
|
|
It("distributes and allows access to newly added private data per collections_config2", func() {
|
|
By("upgrading the chaincode and adding marble2")
|
|
// collections_config2.json defines the access as follows:
|
|
// 1. collectionMarbles - Org1, Org2 and Org3 have access to this collection
|
|
// 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection
|
|
// the change from collections_config1 - org3 was added to collectionMarbles
|
|
testChaincode.Version = "1.1"
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config2.json")
|
|
if !testChaincode.isLegacy {
|
|
testChaincode.Sequence = "2"
|
|
}
|
|
upgradeChaincode(network, orderer, testChaincode)
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name,
|
|
`{"name":"marble2", "color":"yellow", "size":53, "owner":"jerry", "price":22}`,
|
|
network.Peer("Org2", "peer0"),
|
|
)
|
|
|
|
assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble2")
|
|
})
|
|
})
|
|
|
|
When("a new peer in org1 joins the channel", func() {
|
|
It("causes the new peer to pull the existing private data only for collectionMarbles", func() {
|
|
By("adding a new peer")
|
|
newPeerProcess = addPeer(network, orderer, org1Peer1)
|
|
installChaincode(network, testChaincode, org1Peer1)
|
|
network.VerifyMembership(network.Peers, channelID, "marblesp")
|
|
|
|
assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1", org1Peer1)
|
|
})
|
|
})
|
|
}
|
|
|
|
When("chaincode is migrated from legacy to new lifecycle with same collection config", func() {
|
|
BeforeEach(func() {
|
|
testChaincode = chaincode{
|
|
Chaincode: newLifecycleChaincode,
|
|
isLegacy: false,
|
|
}
|
|
nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...)
|
|
upgradeChaincode(network, orderer, testChaincode)
|
|
})
|
|
assertReconcileBehavior()
|
|
})
|
|
})
|
|
|
|
Describe("BlockToLive", func() {
|
|
var newPeerProcess ifrit.Process
|
|
|
|
AfterEach(func() {
|
|
if newPeerProcess != nil {
|
|
newPeerProcess.Signal(syscall.SIGTERM)
|
|
Eventually(newPeerProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
}
|
|
})
|
|
|
|
It("purges private data after BTL and causes new peer not to pull the purged private data", func() {
|
|
By("deploying new lifecycle chaincode and adding marble1")
|
|
testChaincode = chaincode{
|
|
Chaincode: newLifecycleChaincode,
|
|
isLegacy: false,
|
|
}
|
|
nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...)
|
|
|
|
testChaincode.CollectionsConfig = CollectionConfig("short_btl_config.json")
|
|
deployChaincode(network, orderer, testChaincode)
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0"))
|
|
|
|
eligiblePeer := network.Peer("Org2", "peer0")
|
|
ccName := testChaincode.Name
|
|
By("adding three blocks")
|
|
for i := 0; i < 3; i++ {
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer)
|
|
}
|
|
|
|
By("verifying that marble1 still not purged in collection MarblesPD")
|
|
marblechaincodeutil.AssertPresentInCollectionMPD(network, channelID, ccName, "marble1", eligiblePeer)
|
|
|
|
By("adding one more block")
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, ccName, `{"name":"fun-marble-3", "color":"blue", "size":35, "owner":"tom", "price":99}`, eligiblePeer)
|
|
|
|
By("verifying that marble1 purged in collection MarblesPD")
|
|
marblechaincodeutil.AssertDoesNotExistInCollectionMPD(network, channelID, ccName, "marble1", eligiblePeer)
|
|
|
|
By("verifying that marble1 still not purged in collection Marbles")
|
|
marblechaincodeutil.AssertPresentInCollectionM(network, channelID, ccName, "marble1", eligiblePeer)
|
|
|
|
By("adding new peer that is eligible to receive data")
|
|
newPeerProcess = addPeer(network, orderer, org2Peer1)
|
|
installChaincode(network, testChaincode, org2Peer1)
|
|
network.VerifyMembership(network.Peers, channelID, ccName)
|
|
marblechaincodeutil.AssertPresentInCollectionM(network, channelID, ccName, "marble1", org2Peer1)
|
|
marblechaincodeutil.AssertDoesNotExistInCollectionMPD(network, channelID, ccName, "marble1", org2Peer1)
|
|
})
|
|
})
|
|
|
|
Describe("Org removal from collection", func() {
|
|
assertOrgRemovalBehavior := func() {
|
|
By("upgrading chaincode to remove org3 from collectionMarbles")
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config1.json")
|
|
testChaincode.Version = "1.1"
|
|
if !testChaincode.isLegacy {
|
|
testChaincode.Sequence = "2"
|
|
}
|
|
upgradeChaincode(network, orderer, testChaincode)
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, `{"name":"marble2", "color":"yellow", "size":53, "owner":"jerry", "price":22}`, network.Peer("Org2", "peer0"))
|
|
assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble2")
|
|
}
|
|
|
|
It("causes removed org not to get new data", func() {
|
|
By("deploying new lifecycle chaincode and adding marble1")
|
|
testChaincode = chaincode{
|
|
Chaincode: newLifecycleChaincode,
|
|
isLegacy: false,
|
|
}
|
|
nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...)
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config2.json")
|
|
deployChaincode(network, orderer, testChaincode)
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0"))
|
|
assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble1")
|
|
|
|
assertOrgRemovalBehavior()
|
|
})
|
|
})
|
|
|
|
When("migrating a chaincode from legacy lifecycle to new lifecycle", func() {
|
|
It("performs check against collection config from legacy lifecycle", func() {
|
|
By("deploying legacy chaincode")
|
|
testChaincode = chaincode{
|
|
Chaincode: legacyChaincode,
|
|
isLegacy: true,
|
|
}
|
|
deployChaincode(network, orderer, testChaincode)
|
|
nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...)
|
|
|
|
newLifecycleChaincode.CollectionsConfig = CollectionConfig("short_btl_config.json")
|
|
newLifecycleChaincode.PackageID = "test-package-id"
|
|
|
|
approveChaincodeForMyOrgExpectErr(
|
|
network,
|
|
orderer,
|
|
newLifecycleChaincode,
|
|
`the BlockToLive in an existing collection \[collectionMarblePrivateDetails\] modified. Existing value \[1000000\]`,
|
|
network.Peer("Org2", "peer0"))
|
|
})
|
|
})
|
|
|
|
Describe("Collection Config Endorsement Policy", func() {
|
|
When("using legacy lifecycle chaincode", func() {
|
|
It("ignores the collection config endorsement policy and successfully invokes the chaincode", func() {
|
|
testChaincode = chaincode{
|
|
Chaincode: legacyChaincode,
|
|
isLegacy: true,
|
|
}
|
|
By("setting the collection config endorsement policy to org2 or org3 peers")
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config4.json")
|
|
|
|
By("deploying legacy chaincode")
|
|
deployChaincode(network, orderer, testChaincode)
|
|
|
|
By("adding marble1 with an org 1 peer as endorser")
|
|
peer := network.Peer("Org1", "peer0")
|
|
marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, marbleDetails, peer)
|
|
})
|
|
})
|
|
|
|
When("using new lifecycle chaincode", func() {
|
|
BeforeEach(func() {
|
|
testChaincode = chaincode{
|
|
Chaincode: newLifecycleChaincode,
|
|
isLegacy: false,
|
|
}
|
|
nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peers...)
|
|
})
|
|
|
|
When("a peer specified in the chaincode endorsement policy but not in the collection config endorsement policy is used to invoke the chaincode", func() {
|
|
It("fails validation", func() {
|
|
By("setting the collection config endorsement policy to org2 or org3 peers")
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config4.json")
|
|
|
|
By("deploying new lifecycle chaincode")
|
|
// set collection endorsement policy to org2 or org3
|
|
deployChaincode(network, orderer, testChaincode)
|
|
|
|
By("adding marble1 with an org1 peer as endorser")
|
|
peer := network.Peer("Org1", "peer0")
|
|
marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`
|
|
marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails))
|
|
|
|
command := commands.ChaincodeInvoke{
|
|
ChannelID: channelID,
|
|
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: testChaincode.Name,
|
|
Ctor: `{"Args":["initMarble"]}`,
|
|
Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64),
|
|
PeerAddresses: []string{
|
|
network.PeerAddress(peer, nwo.ListenPort),
|
|
},
|
|
WaitForEvent: true,
|
|
}
|
|
|
|
sess, err := network.PeerUserSession(peer, "User1", command)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit())
|
|
Expect(sess.Err).To(gbytes.Say("ENDORSEMENT_POLICY_FAILURE"))
|
|
})
|
|
})
|
|
|
|
When("a peer specified in the collection endorsement policy but not in the chaincode endorsement policy is used to invoke the chaincode", func() {
|
|
When("the collection endorsement policy is a signature policy", func() {
|
|
It("successfully invokes the chaincode", func() {
|
|
// collection config endorsement policy specifies org2 or org3 peers for endorsement
|
|
By("setting the collection config endorsement policy to use a signature policy")
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config4.json")
|
|
|
|
By("setting the chaincode endorsement policy to org1 or org2 peers")
|
|
testChaincode.SignaturePolicy = `OR ('Org1MSP.member','Org2MSP.member')`
|
|
|
|
By("deploying new lifecycle chaincode")
|
|
// set collection endorsement policy to org2 or org3
|
|
deployChaincode(network, orderer, testChaincode)
|
|
|
|
By("adding marble1 with an org3 peer as endorser")
|
|
peer := network.Peer("Org3", "peer0")
|
|
marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, marbleDetails, peer)
|
|
})
|
|
})
|
|
|
|
When("the collection endorsement policy is a channel config policy reference", func() {
|
|
It("successfully invokes the chaincode", func() {
|
|
// collection config endorsement policy specifies channel config policy reference /Channel/Application/Readers
|
|
By("setting the collection config endorsement policy to use a channel config policy reference")
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config5.json")
|
|
|
|
By("setting the channel endorsement policy to org1 or org2 peers")
|
|
testChaincode.SignaturePolicy = `OR ('Org1MSP.member','Org2MSP.member')`
|
|
|
|
By("deploying new lifecycle chaincode")
|
|
deployChaincode(network, orderer, testChaincode)
|
|
|
|
By("adding marble1 with an org3 peer as endorser")
|
|
peer := network.Peer("Org3", "peer0")
|
|
marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, marbleDetails, peer)
|
|
})
|
|
})
|
|
})
|
|
|
|
When("the collection config endorsement policy specifies a semantically wrong, but well formed signature policy", func() {
|
|
It("fails to invoke the chaincode with an endorsement policy failure", func() {
|
|
By("setting the collection config endorsement policy to non existent org4 peers")
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config6.json")
|
|
|
|
By("deploying new lifecycle chaincode")
|
|
deployChaincode(network, orderer, testChaincode)
|
|
|
|
By("adding marble1 with an org1 peer as endorser")
|
|
peer := network.Peer("Org1", "peer0")
|
|
marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`
|
|
marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails))
|
|
|
|
command := commands.ChaincodeInvoke{
|
|
ChannelID: channelID,
|
|
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: testChaincode.Name,
|
|
Ctor: `{"Args":["initMarble"]}`,
|
|
Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64),
|
|
PeerAddresses: []string{
|
|
network.PeerAddress(peer, nwo.ListenPort),
|
|
},
|
|
WaitForEvent: true,
|
|
}
|
|
|
|
sess, err := network.PeerUserSession(peer, "User1", command)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit())
|
|
Expect(sess.Err).To(gbytes.Say("ENDORSEMENT_POLICY_FAILURE"))
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("marble APIs invocation and private data delivery", func() {
|
|
var (
|
|
newLifecycleChaincode nwo.Chaincode
|
|
testChaincode chaincode
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
By("setting up the network")
|
|
network = initThreeOrgsSetup(true)
|
|
|
|
newLifecycleChaincode = nwo.Chaincode{
|
|
Name: "marblesp",
|
|
Version: "1.0",
|
|
Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd"),
|
|
Lang: "binary",
|
|
PackageFile: filepath.Join(network.RootDir, "marbles-pvtdata.tar.gz"),
|
|
Label: "marbles-private-20",
|
|
SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`,
|
|
CollectionsConfig: CollectionConfig("collections_config1.json"),
|
|
Sequence: "1",
|
|
}
|
|
|
|
// In assertDeliverWithPrivateDataACLBehavior, there is a single goroutine that communicates to DeliverService.
|
|
// Set DeliverService concurrency limit to 1 in order to verify that the grpc server in a peer works correctly
|
|
// when reaching (but not exceeding) the concurrency limit.
|
|
By("setting deliverservice concurrency limit to 1")
|
|
for _, p := range network.Peers {
|
|
core := network.ReadPeerConfig(p)
|
|
core.Peer.Limits.Concurrency.DeliverService = 1
|
|
network.WritePeerConfig(p, core)
|
|
}
|
|
ordererProcess, peerProcess, orderer = startNetwork(network)
|
|
})
|
|
|
|
// call marble APIs: getMarblesByRange, transferMarble, delete, getMarbleHash, getMarblePrivateDetailsHash and verify ACL Behavior
|
|
assertMarbleAPIs := func() {
|
|
eligiblePeer := network.Peer("Org2", "peer0")
|
|
ccName := testChaincode.Name
|
|
|
|
// Verifies marble private chaincode APIs: getMarblesByRange, transferMarble, delete
|
|
|
|
By("adding five marbles")
|
|
for i := 0; i < 5; i++ {
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer)
|
|
}
|
|
|
|
By("getting marbles by range")
|
|
expectedMsg := `\Q[{"Key":"test-marble-0", "Record":{"docType":"marble","name":"test-marble-0","color":"blue","size":35,"owner":"tom"}},{"Key":"test-marble-1", "Record":{"docType":"marble","name":"test-marble-1","color":"blue","size":35,"owner":"tom"}}]\E`
|
|
marblechaincodeutil.AssertGetMarblesByRange(network, channelID, ccName, `"test-marble-0", "test-marble-2"`, expectedMsg, eligiblePeer)
|
|
|
|
By("transferring test-marble-0 to jerry")
|
|
marblechaincodeutil.TransferMarble(network, orderer, channelID, ccName, `{"name":"test-marble-0", "owner":"jerry"}`, eligiblePeer)
|
|
|
|
By("verifying the new ownership of test-marble-0")
|
|
marblechaincodeutil.AssertOwnershipInCollectionM(network, channelID, ccName, `test-marble-0`, "jerry", eligiblePeer)
|
|
|
|
By("deleting test-marble-0")
|
|
marblechaincodeutil.DeleteMarble(network, orderer, channelID, ccName, `{"name":"test-marble-0"}`, eligiblePeer)
|
|
|
|
By("verifying the deletion of test-marble-0")
|
|
marblechaincodeutil.AssertDoesNotExistInCollectionM(network, channelID, ccName, `test-marble-0`, eligiblePeer)
|
|
|
|
// This section verifies that chaincode can return private data hash.
|
|
// Unlike private data that can only be accessed from authorized peers as defined in the collection config,
|
|
// private data hash can be queried on any peer in the channel that has the chaincode instantiated.
|
|
// When calling QueryChaincode with "getMarbleHash", the cc will return the private data hash in collectionMarbles.
|
|
// When calling QueryChaincode with "getMarblePrivateDetailsHash", the cc will return the private data hash in collectionMarblePrivateDetails.
|
|
|
|
peerList := []*nwo.Peer{
|
|
network.Peer("Org1", "peer0"),
|
|
network.Peer("Org2", "peer0"),
|
|
network.Peer("Org3", "peer0"),
|
|
}
|
|
|
|
By("verifying getMarbleHash is accessible from all peers that has the chaincode instantiated")
|
|
expectedBytes := util.ComputeStringHash(`{"docType":"marble","name":"test-marble-1","color":"blue","size":35,"owner":"tom"}`)
|
|
marblechaincodeutil.AssertMarblesPrivateHashM(network, channelID, ccName, "test-marble-1", expectedBytes, peerList)
|
|
|
|
By("verifying getMarblePrivateDetailsHash is accessible from all peers that has the chaincode instantiated")
|
|
expectedBytes = util.ComputeStringHash(`{"docType":"marblePrivateDetails","name":"test-marble-1","price":99}`)
|
|
marblechaincodeutil.AssertMarblesPrivateDetailsHashMPD(network, channelID, ccName, "test-marble-1", expectedBytes, peerList)
|
|
|
|
// collection ACL while reading private data: not allowed to non-members
|
|
// collections_config3: collectionMarblePrivateDetails - member_only_read is set to true
|
|
|
|
By("querying collectionMarblePrivateDetails on org1-peer0 by org1-user1, shouldn't have read access")
|
|
marblechaincodeutil.AssertNoReadAccessToCollectionMPD(network, channelID, testChaincode.Name, "test-marble-1", network.Peer("Org1", "peer0"))
|
|
}
|
|
|
|
// verify DeliverWithPrivateData sends private data based on the ACL in collection config
|
|
// before and after upgrade.
|
|
assertDeliverWithPrivateDataACLBehavior := func() {
|
|
By("getting signing identity for a user in org1")
|
|
signingIdentity := network.PeerUserSigner(network.Peer("Org1", "peer0"), "User1")
|
|
|
|
By("adding a marble")
|
|
peer := network.Peer("Org2", "peer0")
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name, `{"name":"marble11", "color":"blue", "size":35, "owner":"tom", "price":99}`, peer)
|
|
|
|
By("getting the deliver event for newest block")
|
|
event := getEventFromDeliverService(network, peer, channelID, signingIdentity, 0)
|
|
|
|
By("verifying private data in deliver event contains 'collectionMarbles' only")
|
|
// it should receive pvtdata for 'collectionMarbles' only because memberOnlyRead is true
|
|
expectedKVWritesMap := map[string]map[string][]byte{
|
|
"collectionMarbles": {
|
|
"\000color~name\000blue\000marble11\000": []byte("\000"),
|
|
"marble11": getValueForCollectionMarbles("marble11", "blue", "tom", 35),
|
|
},
|
|
}
|
|
assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap)
|
|
|
|
By("upgrading chaincode with collections_config1.json where isMemberOnlyRead is false")
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config1.json")
|
|
testChaincode.Version = "1.1"
|
|
if !testChaincode.isLegacy {
|
|
testChaincode.Sequence = "2"
|
|
}
|
|
upgradeChaincode(network, orderer, testChaincode)
|
|
|
|
By("getting the deliver event for an old block committed before upgrade")
|
|
event = getEventFromDeliverService(network, peer, channelID, signingIdentity, event.BlockNum)
|
|
|
|
By("verifying the deliver event for the old block uses old config")
|
|
assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap)
|
|
|
|
By("adding a new marble after upgrade")
|
|
marblechaincodeutil.AddMarble(network, orderer, channelID, testChaincode.Name,
|
|
`{"name":"marble12", "color":"blue", "size":35, "owner":"tom", "price":99}`,
|
|
network.Peer("Org1", "peer0"),
|
|
)
|
|
By("getting the deliver event for a new block committed after upgrade")
|
|
event = getEventFromDeliverService(network, peer, channelID, signingIdentity, 0)
|
|
|
|
// it should receive pvtdata for both collections because memberOnlyRead is false
|
|
By("verifying the deliver event for the new block uses new config")
|
|
expectedKVWritesMap = map[string]map[string][]byte{
|
|
"collectionMarbles": {
|
|
"\000color~name\000blue\000marble12\000": []byte("\000"),
|
|
"marble12": getValueForCollectionMarbles("marble12", "blue", "tom", 35),
|
|
},
|
|
"collectionMarblePrivateDetails": {
|
|
"marble12": getValueForCollectionMarblePrivateDetails("marble12", 99),
|
|
},
|
|
}
|
|
assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap)
|
|
}
|
|
|
|
It("calls marbles APIs and delivers private data", func() {
|
|
By("deploying new lifecycle chaincode")
|
|
testChaincode = chaincode{
|
|
Chaincode: newLifecycleChaincode,
|
|
isLegacy: false,
|
|
}
|
|
nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...)
|
|
testChaincode.CollectionsConfig = CollectionConfig("collections_config3.json")
|
|
deployChaincode(network, orderer, testChaincode)
|
|
|
|
By("attempting to invoke chaincode from a user (org1) not in any collection member orgs (org2 and org3)")
|
|
peer2 := network.Peer("Org2", "peer0")
|
|
marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(`{"name":"memberonly-marble", "color":"blue", "size":35, "owner":"tom", "price":99}`))
|
|
command := commands.ChaincodeInvoke{
|
|
ChannelID: channelID,
|
|
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: "marblesp",
|
|
Ctor: `{"Args":["initMarble"]}`,
|
|
Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64),
|
|
PeerAddresses: []string{network.PeerAddress(peer2, nwo.ListenPort)},
|
|
WaitForEvent: true,
|
|
}
|
|
peer1 := network.Peer("Org1", "peer0")
|
|
expectedErrMsg := "tx creator does not have write access permission"
|
|
invokeChaincodeExpectErr(network, peer1, command, expectedErrMsg)
|
|
|
|
assertMarbleAPIs()
|
|
assertDeliverWithPrivateDataACLBehavior()
|
|
})
|
|
})
|
|
})
|
|
|
|
func initThreeOrgsSetup(removePeer1 bool) *nwo.Network {
|
|
var err error
|
|
testDir, err := ioutil.TempDir("", "e2e-pvtdata")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
client, err := docker.NewClientFromEnv()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
config := nwo.FullEtcdRaft()
|
|
|
|
// add org3 with one peer
|
|
config.Organizations = append(config.Organizations, &nwo.Organization{
|
|
Name: "Org3",
|
|
MSPID: "Org3MSP",
|
|
Domain: "org3.example.com",
|
|
EnableNodeOUs: true,
|
|
Users: 2,
|
|
CA: &nwo.CA{Hostname: "ca"},
|
|
})
|
|
config.Profiles[0].Organizations = append(config.Profiles[0].Organizations, "Org3")
|
|
config.Peers = append(config.Peers, &nwo.Peer{
|
|
Name: "peer0",
|
|
Organization: "Org3",
|
|
Channels: []*nwo.PeerChannel{
|
|
{Name: channelID, Anchor: true},
|
|
},
|
|
})
|
|
|
|
n := nwo.New(config, testDir, client, StartPort(), components)
|
|
n.GenerateConfigTree()
|
|
|
|
if !removePeer1 {
|
|
Expect(n.Peers).To(HaveLen(5))
|
|
return n
|
|
}
|
|
|
|
// remove peer1 from org1 and org2 so we can add it back later, we generate the config tree above
|
|
// with the two peers so the config files exist later when adding the peer back
|
|
peers := []*nwo.Peer{}
|
|
for _, p := range n.Peers {
|
|
if p.Name != "peer1" {
|
|
peers = append(peers, p)
|
|
}
|
|
}
|
|
n.Peers = peers
|
|
Expect(n.Peers).To(HaveLen(3))
|
|
|
|
return n
|
|
}
|
|
|
|
func startNetwork(n *nwo.Network) (ifrit.Process, ifrit.Process, *nwo.Orderer) {
|
|
n.Bootstrap()
|
|
|
|
// Start all the fabric processes
|
|
ordererRunner, ordererProcess, peerProcess := n.StartSingleOrdererNetwork("orderer")
|
|
|
|
orderer := n.Orderer("orderer")
|
|
channelparticipation.JoinOrdererJoinPeersAppChannel(n, channelID, orderer, ordererRunner)
|
|
|
|
By("verifying membership")
|
|
n.VerifyMembership(n.Peers, channelID)
|
|
|
|
return ordererProcess, peerProcess, orderer
|
|
}
|
|
|
|
func testCleanup(network *nwo.Network, ordererProcess, peerProcess ifrit.Process) {
|
|
if ordererProcess != nil {
|
|
ordererProcess.Signal(syscall.SIGTERM)
|
|
Eventually(ordererProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
}
|
|
if peerProcess != nil {
|
|
peerProcess.Signal(syscall.SIGTERM)
|
|
Eventually(peerProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
}
|
|
|
|
if network != nil {
|
|
network.Cleanup()
|
|
}
|
|
os.RemoveAll(network.RootDir)
|
|
}
|
|
|
|
type chaincode struct {
|
|
nwo.Chaincode
|
|
isLegacy bool
|
|
}
|
|
|
|
func addPeer(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer) ifrit.Process {
|
|
process := ifrit.Invoke(n.PeerRunner(peer))
|
|
Eventually(process.Ready(), n.EventuallyTimeout).Should(BeClosed())
|
|
|
|
n.JoinChannel(channelID, orderer, peer)
|
|
ledgerHeight := nwo.GetLedgerHeight(n, n.Peers[0], channelID)
|
|
sess, err := n.PeerAdminSession(
|
|
peer,
|
|
commands.ChannelFetch{
|
|
Block: "newest",
|
|
ChannelID: channelID,
|
|
Orderer: n.OrdererAddress(orderer, nwo.ListenPort),
|
|
OutputFile: filepath.Join(n.RootDir, "newest_block.pb"),
|
|
},
|
|
)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
|
|
Expect(sess.Err).To(gbytes.Say(fmt.Sprintf("Received block: %d", ledgerHeight-1)))
|
|
|
|
n.Peers = append(n.Peers, peer)
|
|
nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, n.Peers[0], channelID), n.Peers...)
|
|
|
|
return process
|
|
}
|
|
|
|
func deployChaincode(n *nwo.Network, orderer *nwo.Orderer, chaincode chaincode) {
|
|
if chaincode.isLegacy {
|
|
nwo.DeployChaincodeLegacy(n, channelID, orderer, chaincode.Chaincode)
|
|
} else {
|
|
nwo.DeployChaincode(n, channelID, orderer, chaincode.Chaincode)
|
|
}
|
|
}
|
|
|
|
func upgradeChaincode(n *nwo.Network, orderer *nwo.Orderer, chaincode chaincode) {
|
|
if chaincode.isLegacy {
|
|
nwo.UpgradeChaincodeLegacy(n, channelID, orderer, chaincode.Chaincode)
|
|
} else {
|
|
nwo.DeployChaincode(n, channelID, orderer, chaincode.Chaincode)
|
|
}
|
|
}
|
|
|
|
func installChaincode(n *nwo.Network, chaincode chaincode, peer *nwo.Peer) {
|
|
if chaincode.isLegacy {
|
|
nwo.InstallChaincodeLegacy(n, chaincode.Chaincode, peer)
|
|
} else {
|
|
nwo.PackageAndInstallChaincode(n, chaincode.Chaincode, peer)
|
|
}
|
|
}
|
|
|
|
func invokeChaincodeExpectErr(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeInvoke, expectedErrMsg string) {
|
|
sess, err := n.PeerUserSession(peer, "User1", command)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(1))
|
|
Expect(sess.Err).To(gbytes.Say(expectedErrMsg))
|
|
}
|
|
|
|
func approveChaincodeForMyOrgExpectErr(n *nwo.Network, orderer *nwo.Orderer, chaincode nwo.Chaincode, expectedErrMsg string, peers ...*nwo.Peer) {
|
|
// used to ensure we only approve once per org
|
|
approvedOrgs := map[string]bool{}
|
|
for _, p := range peers {
|
|
if _, ok := approvedOrgs[p.Organization]; !ok {
|
|
sess, err := n.PeerAdminSession(p, commands.ChaincodeApproveForMyOrg{
|
|
ChannelID: channelID,
|
|
Orderer: n.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: chaincode.Name,
|
|
Version: chaincode.Version,
|
|
PackageID: chaincode.PackageID,
|
|
Sequence: chaincode.Sequence,
|
|
EndorsementPlugin: chaincode.EndorsementPlugin,
|
|
ValidationPlugin: chaincode.ValidationPlugin,
|
|
SignaturePolicy: chaincode.SignaturePolicy,
|
|
ChannelConfigPolicy: chaincode.ChannelConfigPolicy,
|
|
InitRequired: chaincode.InitRequired,
|
|
CollectionsConfig: chaincode.CollectionsConfig,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
|
|
approvedOrgs[p.Organization] = true
|
|
Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(expectedErrMsg))
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertPvtdataPresencePerCollectionConfig1(n *nwo.Network, chaincodeName, marbleName string, peers ...*nwo.Peer) {
|
|
if len(peers) == 0 {
|
|
peers = n.Peers
|
|
}
|
|
for _, peer := range peers {
|
|
switch peer.Organization {
|
|
|
|
case "Org1":
|
|
marblechaincodeutil.AssertPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
marblechaincodeutil.AssertNotPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
|
|
case "Org2":
|
|
marblechaincodeutil.AssertPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
marblechaincodeutil.AssertPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
|
|
case "Org3":
|
|
marblechaincodeutil.AssertNotPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
marblechaincodeutil.AssertPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertPvtdataPresencePerCollectionConfig2(n *nwo.Network, chaincodeName, marbleName string, peers ...*nwo.Peer) {
|
|
if len(peers) == 0 {
|
|
peers = n.Peers
|
|
}
|
|
for _, peer := range peers {
|
|
switch peer.Organization {
|
|
|
|
case "Org1":
|
|
marblechaincodeutil.AssertPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
marblechaincodeutil.AssertNotPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
|
|
case "Org2", "Org3":
|
|
marblechaincodeutil.AssertPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
marblechaincodeutil.AssertPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertPvtdataPresencePerCollectionConfig7(n *nwo.Network, chaincodeName, marbleName string, excludedPeer *nwo.Peer, peers ...*nwo.Peer) {
|
|
if len(peers) == 0 {
|
|
peers = n.Peers
|
|
}
|
|
collectionMPresence := 0
|
|
collectionMPDPresence := 0
|
|
for _, peer := range peers {
|
|
// exclude the peer that invoked originally and count number of peers disseminated to
|
|
if peer != excludedPeer {
|
|
switch peer.Organization {
|
|
|
|
case "Org1":
|
|
collectionMPresence += marblechaincodeutil.CheckPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
marblechaincodeutil.AssertNotPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
|
|
case "Org2":
|
|
collectionMPresence += marblechaincodeutil.CheckPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
collectionMPDPresence += marblechaincodeutil.CheckPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
case "Org3":
|
|
marblechaincodeutil.AssertNotPresentInCollectionM(n, channelID, chaincodeName, marbleName, peer)
|
|
collectionMPDPresence += marblechaincodeutil.CheckPresentInCollectionMPD(n, channelID, chaincodeName, marbleName, peer)
|
|
}
|
|
}
|
|
}
|
|
Expect(collectionMPresence).To(Equal(1))
|
|
Expect(collectionMPDPresence).To(Equal(1))
|
|
}
|
|
|
|
// deliverEvent contains the response and related info from a DeliverWithPrivateData call
|
|
type deliverEvent struct {
|
|
BlockAndPvtData *pb.BlockAndPrivateData
|
|
BlockNum uint64
|
|
Err error
|
|
}
|
|
|
|
// getEventFromDeliverService send a request to DeliverWithPrivateData grpc service
|
|
// and receive the response
|
|
func getEventFromDeliverService(network *nwo.Network, peer *nwo.Peer, channelID string, signingIdentity *nwo.SigningIdentity, blockNum uint64) *deliverEvent {
|
|
ctx, cancelFunc1 := context.WithTimeout(context.Background(), network.EventuallyTimeout)
|
|
defer cancelFunc1()
|
|
eventCh, conn := registerForDeliverEvent(ctx, network, peer, channelID, signingIdentity, blockNum)
|
|
defer conn.Close()
|
|
event := &deliverEvent{}
|
|
Eventually(eventCh, network.EventuallyTimeout).Should(Receive(event))
|
|
Expect(event.Err).NotTo(HaveOccurred())
|
|
return event
|
|
}
|
|
|
|
func registerForDeliverEvent(
|
|
ctx context.Context,
|
|
network *nwo.Network,
|
|
peer *nwo.Peer,
|
|
channelID string,
|
|
signingIdentity *nwo.SigningIdentity,
|
|
blockNum uint64,
|
|
) (<-chan deliverEvent, *grpc.ClientConn) {
|
|
// create a grpc.ClientConn
|
|
conn := network.PeerClientConn(peer)
|
|
|
|
dp, err := pb.NewDeliverClient(conn).DeliverWithPrivateData(ctx)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// send a deliver request
|
|
envelope, err := createDeliverEnvelope(channelID, signingIdentity, blockNum)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = dp.Send(envelope)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = dp.CloseSend()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// create a goroutine to receive the response in a separate thread
|
|
eventCh := make(chan deliverEvent, 1)
|
|
go receiveDeliverResponse(dp, peer, eventCh)
|
|
|
|
return eventCh, conn
|
|
}
|
|
|
|
// receiveDeliverResponse expects to receive the BlockAndPrivateData response for the requested block.
|
|
func receiveDeliverResponse(dp pb.Deliver_DeliverWithPrivateDataClient, peer *nwo.Peer, eventCh chan<- deliverEvent) {
|
|
event := deliverEvent{}
|
|
|
|
resp, err := dp.Recv()
|
|
if err != nil {
|
|
event.Err = errors.WithMessagef(err, "error receiving deliver response from peer %s", peer.ID())
|
|
}
|
|
switch r := resp.Type.(type) {
|
|
case *pb.DeliverResponse_BlockAndPrivateData:
|
|
event.BlockAndPvtData = r.BlockAndPrivateData
|
|
event.BlockNum = r.BlockAndPrivateData.Block.Header.Number
|
|
case *pb.DeliverResponse_Status:
|
|
event.Err = errors.Errorf("deliver completed with status (%s) before DeliverResponse_BlockAndPrivateData received from peer %s", r.Status, peer.ID())
|
|
default:
|
|
event.Err = errors.Errorf("received unexpected response type (%T) from peer %s", r, peer.ID())
|
|
}
|
|
|
|
select {
|
|
case eventCh <- event:
|
|
default:
|
|
}
|
|
}
|
|
|
|
// createDeliverEnvelope creates a deliver request based on the block number.
|
|
// blockNum=0 means newest block
|
|
func createDeliverEnvelope(channelID string, signingIdentity *nwo.SigningIdentity, blockNum uint64) (*cb.Envelope, error) {
|
|
creator, err := signingIdentity.Serialize()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
header, err := createHeader(cb.HeaderType_DELIVER_SEEK_INFO, channelID, creator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// if blockNum is not greater than 0, seek the newest block
|
|
var seekInfo *ab.SeekInfo
|
|
if blockNum > 0 {
|
|
seekInfo = &ab.SeekInfo{
|
|
Start: &ab.SeekPosition{
|
|
Type: &ab.SeekPosition_Specified{
|
|
Specified: &ab.SeekSpecified{Number: blockNum},
|
|
},
|
|
},
|
|
Stop: &ab.SeekPosition{
|
|
Type: &ab.SeekPosition_Specified{
|
|
Specified: &ab.SeekSpecified{Number: blockNum},
|
|
},
|
|
},
|
|
}
|
|
} else {
|
|
seekInfo = &ab.SeekInfo{
|
|
Start: &ab.SeekPosition{
|
|
Type: &ab.SeekPosition_Newest{
|
|
Newest: &ab.SeekNewest{},
|
|
},
|
|
},
|
|
Stop: &ab.SeekPosition{
|
|
Type: &ab.SeekPosition_Newest{
|
|
Newest: &ab.SeekNewest{},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// create the envelope
|
|
raw := protoutil.MarshalOrPanic(seekInfo)
|
|
payload := &cb.Payload{
|
|
Header: header,
|
|
Data: raw,
|
|
}
|
|
payloadBytes := protoutil.MarshalOrPanic(payload)
|
|
signature, err := signingIdentity.Sign(payloadBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &cb.Envelope{
|
|
Payload: payloadBytes,
|
|
Signature: signature,
|
|
}, nil
|
|
}
|
|
|
|
func createHeader(txType cb.HeaderType, channelID string, creator []byte) (*cb.Header, error) {
|
|
ts, err := ptypes.TimestampProto(time.Now())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nonce, err := crypto.GetRandomNonce()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
chdr := &cb.ChannelHeader{
|
|
Type: int32(txType),
|
|
ChannelId: channelID,
|
|
TxId: protoutil.ComputeTxID(nonce, creator),
|
|
Epoch: 0,
|
|
Timestamp: ts,
|
|
}
|
|
chdrBytes := protoutil.MarshalOrPanic(chdr)
|
|
|
|
shdr := &cb.SignatureHeader{
|
|
Creator: creator,
|
|
Nonce: nonce,
|
|
}
|
|
shdrBytes := protoutil.MarshalOrPanic(shdr)
|
|
header := &cb.Header{
|
|
ChannelHeader: chdrBytes,
|
|
SignatureHeader: shdrBytes,
|
|
}
|
|
return header, nil
|
|
}
|
|
|
|
// verify collection names and pvtdataMap match expectedKVWritesMap
|
|
func assertPrivateDataAsExpected(pvtdataMap map[uint64]*rwset.TxPvtReadWriteSet, expectedKVWritesMap map[string]map[string][]byte) {
|
|
// In the test, each block has only 1 tx, so txSeqInBlock is 0
|
|
txPvtRwset := pvtdataMap[uint64(0)]
|
|
Expect(txPvtRwset.NsPvtRwset).To(HaveLen(1))
|
|
Expect(txPvtRwset.NsPvtRwset[0].Namespace).To(Equal("marblesp"))
|
|
Expect(txPvtRwset.NsPvtRwset[0].CollectionPvtRwset).To(HaveLen(len(expectedKVWritesMap)))
|
|
|
|
// verify the collections returned in private data have expected collection names and kvRwset.Writes
|
|
for _, col := range txPvtRwset.NsPvtRwset[0].CollectionPvtRwset {
|
|
Expect(expectedKVWritesMap).To(HaveKey(col.CollectionName))
|
|
expectedKvWrites := expectedKVWritesMap[col.CollectionName]
|
|
kvRwset := kvrwset.KVRWSet{}
|
|
err := proto.Unmarshal(col.GetRwset(), &kvRwset)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(kvRwset.Writes).To(HaveLen(len(expectedKvWrites)))
|
|
for _, kvWrite := range kvRwset.Writes {
|
|
Expect(expectedKvWrites).To(HaveKey(kvWrite.Key))
|
|
Expect(kvWrite.Value).To(Equal(expectedKvWrites[kvWrite.Key]))
|
|
}
|
|
}
|
|
}
|
|
|
|
func getValueForCollectionMarbles(marbleName, color, owner string, size int) []byte {
|
|
marbleJSONasString := `{"docType":"marble","name":"` + marbleName + `","color":"` + color + `","size":` + strconv.Itoa(size) + `,"owner":"` + owner + `"}`
|
|
return []byte(marbleJSONasString)
|
|
}
|
|
|
|
func getValueForCollectionMarblePrivateDetails(marbleName string, price int) []byte {
|
|
marbleJSONasString := `{"docType":"marblePrivateDetails","name":"` + marbleName + `","price":` + strconv.Itoa(price) + `}`
|
|
return []byte(marbleJSONasString)
|
|
}
|
|
|
|
// fetchBlocksForPeer attempts to fetch the newest block on the given peer.
|
|
// It skips the orderer and returns the session's Err buffer for parsing.
|
|
func fetchBlocksForPeer(n *nwo.Network, peer *nwo.Peer, user string) func() *gbytes.Buffer {
|
|
return func() *gbytes.Buffer {
|
|
sess, err := n.PeerUserSession(peer, user, commands.ChannelFetch{
|
|
Block: "newest",
|
|
ChannelID: channelID,
|
|
OutputFile: filepath.Join(n.RootDir, "newest_block.pb"),
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
|
|
return sess.Err
|
|
}
|
|
}
|
|
|
|
// updateConfigWithNewCertsForPeer updates the channel config with new certs for the designated peer
|
|
func updateConfigWithNewCertsForPeer(network *nwo.Network, tempCryptoDir string, orderer *nwo.Orderer, peer *nwo.Peer) {
|
|
org := network.Organization(peer.Organization)
|
|
|
|
By("fetching the channel policy")
|
|
currentConfig := nwo.GetConfig(network, network.Peers[0], orderer, channelID)
|
|
updatedConfig := proto.Clone(currentConfig).(*cb.Config)
|
|
|
|
By("parsing the old and new MSP configs")
|
|
oldConfig := &mspp.MSPConfig{}
|
|
err := proto.Unmarshal(
|
|
updatedConfig.ChannelGroup.Groups["Application"].Groups[org.Name].Values["MSP"].Value,
|
|
oldConfig)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
tempOrgMSPPath := filepath.Join(tempCryptoDir, "peerOrganizations", org.Domain, "msp")
|
|
newConfig, err := msp.GetVerifyingMspConfig(tempOrgMSPPath, org.MSPID, "bccsp")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
oldMspConfig := &mspp.FabricMSPConfig{}
|
|
newMspConfig := &mspp.FabricMSPConfig{}
|
|
err = proto.Unmarshal(oldConfig.Config, oldMspConfig)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = proto.Unmarshal(newConfig.Config, newMspConfig)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
By("merging the two MSP configs")
|
|
updateOldMspConfigWithNewMspConfig(oldMspConfig, newMspConfig)
|
|
|
|
By("updating the channel config")
|
|
updatedConfig.ChannelGroup.Groups["Application"].Groups[org.Name].Values["MSP"].Value = protoutil.MarshalOrPanic(
|
|
&mspp.MSPConfig{
|
|
Type: oldConfig.Type,
|
|
Config: protoutil.MarshalOrPanic(oldMspConfig),
|
|
})
|
|
nwo.UpdateConfig(network, orderer, channelID, currentConfig, updatedConfig, false, network.Peer(org.Name, "peer0"))
|
|
}
|
|
|
|
// updateOldMspConfigWithNewMspConfig updates the oldMspConfig with certs from the newMspConfig
|
|
func updateOldMspConfigWithNewMspConfig(oldMspConfig, newMspConfig *mspp.FabricMSPConfig) {
|
|
oldMspConfig.RootCerts = append(oldMspConfig.RootCerts, newMspConfig.RootCerts...)
|
|
oldMspConfig.TlsRootCerts = append(oldMspConfig.TlsRootCerts, newMspConfig.TlsRootCerts...)
|
|
oldMspConfig.FabricNodeOus.PeerOuIdentifier.Certificate = nil
|
|
oldMspConfig.FabricNodeOus.ClientOuIdentifier.Certificate = nil
|
|
oldMspConfig.FabricNodeOus.AdminOuIdentifier.Certificate = nil
|
|
}
|
|
|
|
// generateNewCertsForPeer generates new certs with cryptogen for the designated peer and copies
|
|
// the necessary certs to the original crypto dir as well as creating an Admin2 user to use for
|
|
// any peer operations involving the peer
|
|
func generateNewCertsForPeer(network *nwo.Network, tempCryptoDir string, peer *nwo.Peer) {
|
|
sess, err := network.Cryptogen(commands.Generate{
|
|
Config: network.CryptoConfigPath(),
|
|
Output: tempCryptoDir,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
|
|
|
|
By("copying the new msp certs for the peer to the original crypto dir")
|
|
oldPeerMSPPath := network.PeerLocalMSPDir(peer)
|
|
org := network.Organization(peer.Organization)
|
|
tempPeerMSPPath := filepath.Join(
|
|
tempCryptoDir,
|
|
"peerOrganizations",
|
|
org.Domain,
|
|
"peers",
|
|
fmt.Sprintf("%s.%s", peer.Name, org.Domain),
|
|
"msp",
|
|
)
|
|
os.RemoveAll(oldPeerMSPPath)
|
|
err = exec.Command("cp", "-r", tempPeerMSPPath, oldPeerMSPPath).Run()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// This lets us keep the old user certs for the org for any peers still remaining in the org
|
|
// using the old certs
|
|
By("copying the new Admin user cert to the original user certs dir as Admin2")
|
|
oldAdminUserPath := filepath.Join(
|
|
network.RootDir,
|
|
"crypto",
|
|
"peerOrganizations",
|
|
org.Domain,
|
|
"users",
|
|
fmt.Sprintf("Admin2@%s", org.Domain),
|
|
)
|
|
tempAdminUserPath := filepath.Join(
|
|
tempCryptoDir,
|
|
"peerOrganizations",
|
|
org.Domain,
|
|
"users",
|
|
fmt.Sprintf("Admin@%s", org.Domain),
|
|
)
|
|
os.RemoveAll(oldAdminUserPath)
|
|
err = exec.Command("cp", "-r", tempAdminUserPath, oldAdminUserPath).Run()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
// We need to rename the signcert from Admin to Admin2 as well
|
|
err = os.Rename(
|
|
filepath.Join(oldAdminUserPath, "msp", "signcerts", fmt.Sprintf("Admin@%s-cert.pem", org.Domain)),
|
|
filepath.Join(oldAdminUserPath, "msp", "signcerts", fmt.Sprintf("Admin2@%s-cert.pem", org.Domain)),
|
|
)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|