Skip to content

Commit

Permalink
Restart support and per channel capabilities (resolves #64, resolves #65
Browse files Browse the repository at this point in the history
) (#66)

Signed-off-by: Simon Stone <[email protected]>
  • Loading branch information
Simon Stone authored Oct 23, 2020
1 parent 8ca54ce commit f0809c1
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 24 deletions.
1 change: 1 addition & 0 deletions internal/app/microfabd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Organization struct {
type Channel struct {
Name string `json:"name"`
EndorsingOrganizations []string `json:"endorsing_organizations"`
CapabilityLevel string `json:"capability_level"`
}

// Config represents the configuration.
Expand Down
135 changes: 124 additions & 11 deletions internal/app/microfabd/microfabd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
package microfabd

import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"log"
"os"
Expand All @@ -21,10 +24,12 @@ import (
"github.com/IBM-Blockchain/microfab/internal/pkg/channel"
"github.com/IBM-Blockchain/microfab/internal/pkg/console"
"github.com/IBM-Blockchain/microfab/internal/pkg/couchdb"
"github.com/IBM-Blockchain/microfab/internal/pkg/identity"
"github.com/IBM-Blockchain/microfab/internal/pkg/orderer"
"github.com/IBM-Blockchain/microfab/internal/pkg/organization"
"github.com/IBM-Blockchain/microfab/internal/pkg/peer"
"github.com/IBM-Blockchain/microfab/internal/pkg/proxy"
"github.com/IBM-Blockchain/microfab/pkg/client"
"github.com/hyperledger/fabric-protos-go/common"
"golang.org/x/net/context"
"golang.org/x/sync/errgroup"
Expand All @@ -42,6 +47,7 @@ type Microfab struct {
done chan struct{}
started bool
config *Config
state *State
ordererOrganization *organization.Organization
endorsingOrganizations []*organization.Organization
organizations []*organization.Organization
Expand All @@ -58,6 +64,12 @@ type Microfab struct {
currentPort int
}

// State represents the state that should be persisted between instances.
type State struct {
Hash []byte `json:"hash"`
CAS map[string]*client.Identity `json:"cas"`
}

// New creates an instance of the Microfab application.
func New() (*Microfab, error) {
config, err := DefaultConfig()
Expand Down Expand Up @@ -87,11 +99,32 @@ func (m *Microfab) Start() error {
}
}()

// Ensure the directory exists and is empty.
err := m.ensureDirectory()
// Calculate the config hash.
config, err := json.Marshal(m.config)
if err != nil {
return err
}
hash := sha256.Sum256(config)

// See if the state exists.
if m.stateExists() {
if temp, err := m.loadState(); err != nil {
logger.Printf("Could not load state: %v\n", err)
} else if bytes.Equal(hash[:], temp.Hash) {
logger.Println("Loaded state")
m.state = temp
} else {
logger.Println("Config has changed, loaded state is invalid")
}
}

// Ensure the directory exists and is empty.
if m.state == nil {
err = m.ensureDirectory()
if err != nil {
return err
}
}

// Create all of the organizations.
ctx := context.Background()
Expand Down Expand Up @@ -201,13 +234,21 @@ func (m *Microfab) Start() error {
}()

// Create and join all of the channels.
for i := range m.config.Channels {
channel := m.config.Channels[i]
eg.Go(func() error {
return m.createAndJoinChannel(channel)
})
if m.state == nil {
for i := range m.config.Channels {
channel := m.config.Channels[i]
eg.Go(func() error {
return m.createAndJoinChannel(channel)
})
}
err = eg.Wait()
if err != nil {
return err
}
}
err = eg.Wait()

// Write the state for next time.
err = m.saveState()
if err != nil {
return err
}
Expand Down Expand Up @@ -301,9 +342,66 @@ func (m *Microfab) removeDirectory() error {
return nil
}

func (m *Microfab) stateExists() bool {
statePath := path.Join(m.config.Directory, "state.json")
if _, err := os.Stat(statePath); os.IsNotExist(err) {
return false
}
return true
}

func (m *Microfab) loadState() (*State, error) {
statePath := path.Join(m.config.Directory, "state.json")
file, err := os.Open(statePath)
if err != nil {
return nil, err
}
defer file.Close()
state := &State{}
err = json.NewDecoder(file).Decode(&state)
if err != nil {
return nil, err
}
return state, nil
}

func (m *Microfab) saveState() error {
statePath := path.Join(m.config.Directory, "state.json")
file, err := os.OpenFile(statePath, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
config, err := json.Marshal(m.config)
if err != nil {
return err
}
hash := sha256.Sum256(config)
state := &State{
Hash: hash[:],
CAS: map[string]*client.Identity{},
}
state.CAS[m.ordererOrganization.Name()] = m.ordererOrganization.CA().ToClient()
for _, endorsingOrganization := range m.endorsingOrganizations {
state.CAS[endorsingOrganization.Name()] = endorsingOrganization.CA().ToClient()
}
return json.NewEncoder(file).Encode(&state)
}

func (m *Microfab) createOrderingOrganization(config Organization) error {
logger.Printf("Creating ordering organization %s ...", config.Name)
organization, err := organization.New(config.Name)
var ca *identity.Identity
if m.state != nil {
temp, ok := m.state.CAS[config.Name]
if ok {
var err error
ca, err = identity.FromClient(temp)
if err != nil {
return err
}
}
}
organization, err := organization.New(config.Name, ca)
if err != nil {
return err
}
Expand All @@ -316,7 +414,18 @@ func (m *Microfab) createOrderingOrganization(config Organization) error {

func (m *Microfab) createEndorsingOrganization(config Organization) error {
logger.Printf("Creating endorsing organization %s ...", config.Name)
organization, err := organization.New(config.Name)
var ca *identity.Identity
if m.state != nil {
temp, ok := m.state.CAS[config.Name]
if ok {
var err error
ca, err = identity.FromClient(temp)
if err != nil {
return err
}
}
}
organization, err := organization.New(config.Name, ca)
if err != nil {
return err
}
Expand Down Expand Up @@ -457,8 +566,12 @@ func (m *Microfab) createAndStartCA(organization *organization.Organization, api

func (m *Microfab) createChannel(config Channel) (*common.Block, error) {
logger.Printf("Creating channel %s ...", config.Name)
capabilityLevel := config.CapabilityLevel
if capabilityLevel == "" {
capabilityLevel = m.config.CapabilityLevel
}
opts := []channel.Option{
channel.WithCapabilityLevel(m.config.CapabilityLevel),
channel.WithCapabilityLevel(capabilityLevel),
}
for _, endorsingOrganization := range m.endorsingOrganizations {
found := false
Expand Down
29 changes: 25 additions & 4 deletions internal/pkg/identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"encoding/asn1"
"encoding/pem"
"math/big"
"strings"
"time"

"github.com/IBM-Blockchain/microfab/internal/pkg/identity/certificate"
Expand Down Expand Up @@ -141,11 +142,31 @@ func FromClient(c *client.Identity) (*Identity, error) {
if err != nil {
return nil, err
}
ca, err := certificate.FromBytes(c.CA)
if err != nil {
return nil, err
var ca *certificate.Certificate
if c.CA != nil {
ca, err = certificate.FromBytes(c.CA)
if err != nil {
return nil, err
}
}
return &Identity{name, cert, pk, ca, ca == nil}, nil
}

// ToClient saves an identity into a client identity object.
func (i *Identity) ToClient() *client.Identity {
id := strings.ToLower(i.name)
var ca []byte
if !i.isCA {
ca = i.CA().Bytes()
}
return &client.Identity{
ID: id,
DisplayName: i.name,
Type: "identity",
Certificate: i.Certificate().Bytes(),
PrivateKey: i.PrivateKey().Bytes(),
CA: ca,
}
return &Identity{name, cert, pk, ca, false}, nil
}

// Name returns the name of the identity.
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/orderer/orderer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var _ = Describe("the orderer package", func() {

BeforeEach(func() {
var err error
testOrganization, err = organization.New("Org1")
testOrganization, err = organization.New("Org1", nil)
Expect(err).NotTo(HaveOccurred())
testDirectory, err = ioutil.TempDir("", "ut-peer")
Expect(err).NotTo(HaveOccurred())
Expand Down
13 changes: 8 additions & 5 deletions internal/pkg/organization/organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ type Organization struct {
}

// New creates a new organization.
func New(name string) (*Organization, error) {
caName := fmt.Sprintf("%s CA", name)
ca, err := identity.New(caName, identity.WithIsCA(true))
if err != nil {
return nil, err
func New(name string, ca *identity.Identity) (*Organization, error) {
if ca == nil {
caName := fmt.Sprintf("%s CA", name)
var err error
ca, err = identity.New(caName, identity.WithIsCA(true))
if err != nil {
return nil, err
}
}
adminName := fmt.Sprintf("%s Admin", name)
admin, err := identity.New(adminName, identity.WithOrganizationalUnit("admin"), identity.UsingSigner(ca))
Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/organization/organization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var _ = Describe("the organization package", func() {

When("called with a name with no special characters", func() {
It("creates a new organization", func() {
o, err := organization.New("Org1")
o, err := organization.New("Org1", nil)
Expect(err).NotTo(HaveOccurred())
Expect(o.Name()).To(Equal("Org1"))
Expect(o.MSPID()).To(Equal("Org1MSP"))
Expand All @@ -35,7 +35,7 @@ var _ = Describe("the organization package", func() {

When("called with a name with special characters", func() {
It("creates a new organization", func() {
o, err := organization.New("Org @ 1")
o, err := organization.New("Org @ 1", nil)
Expect(err).NotTo(HaveOccurred())
Expect(o.Name()).To(Equal("Org @ 1"))
Expect(o.MSPID()).To(Equal("Org1MSP"))
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/peer/peer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var _ = Describe("the peer package", func() {

BeforeEach(func() {
var err error
testOrganization, err = organization.New("Org1")
testOrganization, err = organization.New("Org1", nil)
Expect(err).NotTo(HaveOccurred())
testDirectory, err = ioutil.TempDir("", "ut-peer")
Expect(err).NotTo(HaveOccurred())
Expand Down

0 comments on commit f0809c1

Please sign in to comment.