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

Fix: Solve issue #331 by adding FirstEvent support in FireFly config #332

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,27 @@ go install github.com/hyperledger/firefly-cli/ff@latest
$ ff init <stack_name>
```

### Stack Initialization Options

When initializing a new stack, you can configure various options including:

```
--from-block For multiparty networks only: specify the starting block for event processing.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should only work with remote-node-url as with the local chain that the CLI creates we want to start from 0

Copy link
Contributor

Choose a reason for hiding this comment

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

as it should point to when the contract was deployed - I'm more tempted for this to be --contract-block as you pass in the MultiParty Contract address through --contract

Use 'newest' for latest block, or a specific block number (default: "0")
```

Examples:
```
# Initialize with default settings (starts from block 0)
ff init mystack

# Initialize starting from the latest block
ff init mystack --from-block newest

# Initialize starting from a specific block number
ff init mystack --from-block 1234567
Comment on lines +70 to +77
Copy link
Contributor

Choose a reason for hiding this comment

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

I would make these examples to be with --emote-node-url set

```

## Start a stack

```
Expand Down
3 changes: 2 additions & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ func init() {
initCmd.PersistentFlags().StringArrayVar(&initOptions.OrgNames, "org-name", []string{}, "Organization name")
initCmd.PersistentFlags().StringArrayVar(&initOptions.NodeNames, "node-name", []string{}, "Node name")
initCmd.PersistentFlags().BoolVar(&initOptions.RemoteNodeDeploy, "remote-node-deploy", false, "Enable or disable deployment of FireFly contracts on remote nodes")
initCmd.PersistentFlags().StringToStringVar(&initOptions.EnvironmentVars, "environment-vars", map[string]string{}, "Common environment variables to set on all containers in FireFly stack")
initCmd.PersistentFlags().StringToStringVar(&initOptions.EnvironmentVars, "environment-vars", map[string]string{}, "Common environment variables to set on all containers in FireFly stack")
initCmd.Flags().StringVar(&initOptions.FromBlock, "from-block", "0","For multiparty networks only: specify the starting block for event processing (default: 0)")
rootCmd.AddCommand(initCmd)
}
3 changes: 3 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ This command will start a stack and run it in the background.
},
}


// ... existing code ...
Copy link
Contributor

Choose a reason for hiding this comment

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

remove this


func init() {
startCmd.Flags().BoolVarP(&startOptions.NoRollback, "no-rollback", "b", false, "Do not automatically rollback changes if first time setup fails")
rootCmd.AddCommand(startCmd)
Expand Down
6 changes: 5 additions & 1 deletion internal/blockchain/ethereum/ethtypes/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ package ethtypes
type CompiledContracts struct {
Contracts map[string]*CompiledContract `json:"contracts"`
}

// StartOptions holds the options for starting Firefly CLI
type StartOptions struct {
NoRollback bool
FirstEvent string // "0", "newest", or a specific block number
}
type CompiledContract struct {
Name string `json:"name"`
ABI interface{} `json:"abi"`
Expand Down
136 changes: 89 additions & 47 deletions internal/stacks/stack_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package stacks

import (
Expand Down Expand Up @@ -611,44 +610,44 @@ func (s *StackManager) createMember(id string, index int, options *types.InitOpt

func (s *StackManager) StartStack(options *types.StartOptions) (messages []string, err error) {
fmt.Printf("starting FireFly stack '%s'... ", s.Stack.Name)
// Check to make sure all of our ports are available

// Check port availability
Copy link
Contributor

Choose a reason for hiding this comment

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

why the comment change?

err = s.checkPortsAvailable()
if err != nil {
return messages, err
}

hasBeenRun, err := s.Stack.HasRunBefore()
if err != nil {
return messages, err
}
if !hasBeenRun {
setupMessages, err := s.runFirstTimeSetup(options)
messages = append(messages, setupMessages...)
// Pass FirstEvent to the first-time setup
setupMessages, err := s.runFirstTimeSetup(&types.InitOptions{
FromBlock: options.FromBlock,
})
Comment on lines +626 to +628
Copy link
Contributor

Choose a reason for hiding this comment

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

FromBlock doesn't exist in InitOptions, it should be StartOptions. Getting errors in the IDE

messages = append(messages, setupMessages...)
if err != nil {
// Something bad happened during setup
// Handle rollback if necessary
if options.NoRollback {
return messages, err
} else {
// Rollback changes
s.Log.Error(fmt.Errorf("an error occurred - rolling back changes"))
resetErr := s.ResetStack()

var finalErr error

if resetErr != nil {
finalErr = fmt.Errorf("%s - error resetting stack: %s", err.Error(), resetErr.Error())
} else {
finalErr = fmt.Errorf("%s - all changes rolled back", err.Error())
return messages, fmt.Errorf("%s - error resetting stack: %s", err.Error(), resetErr.Error())
}

return messages, finalErr
return messages, fmt.Errorf("%s - all changes rolled back", err.Error())
}
}
} else {
// Standard startup sequence
err = s.runStartupSequence(false)
if err != nil {
return messages, err
}
}

return messages, s.ensureFireflyNodesUp(true)
}

Expand Down Expand Up @@ -864,7 +863,7 @@ func checkPortAvailable(port int) (bool, error) {
}

//nolint:gocyclo // TODO: Breaking this function apart would be great for code tidiness, but it's not an urgent priority
func (s *StackManager) runFirstTimeSetup(options *types.StartOptions) (messages []string, err error) {
func (s *StackManager) runFirstTimeSetup(options *types.InitOptions) (messages []string, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should still be the StartOptions

configDir := filepath.Join(s.Stack.RuntimeDir, "config")

for i := 0; i < len(s.Stack.Members); i++ {
Expand Down Expand Up @@ -943,26 +942,12 @@ func (s *StackManager) runFirstTimeSetup(options *types.StartOptions) (messages
var contractDeploymentResult *types.ContractDeploymentResult
if s.Stack.MultipartyEnabled {
if s.Stack.ContractAddress == "" {
// TODO: This code assumes that there is only one plugin instance per type. When we add support for
// multiple namespaces, this code will likely have to change a lot
s.Log.Info("deploying FireFly smart contracts")
contractDeploymentResult, err = s.blockchainProvider.DeployFireFlyContract()
if err != nil {
return messages, err
}
if contractDeploymentResult != nil {
if contractDeploymentResult.Message != "" {
messages = append(messages, contractDeploymentResult.Message)
}
s.Stack.State.DeployedContracts = append(s.Stack.State.DeployedContracts, contractDeploymentResult.DeployedContract)
}
}
}

for _, member := range s.Stack.Members {
orgConfig := s.blockchainProvider.GetOrgConfig(s.Stack, member)
newConfig.Namespaces.Predefined[0].DefaultKey = orgConfig.Key
Copy link
Contributor

Choose a reason for hiding this comment

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

need this key as well

Comment on lines -946 to -964
Copy link
Contributor

Choose a reason for hiding this comment

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

As I mentioned in my previous review, keep this code as it was

if s.Stack.MultipartyEnabled {
Copy link
Contributor

Choose a reason for hiding this comment

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

we still need this MultipartyEnabled wrapping this section

var contractLocation interface{}
if s.Stack.ContractAddress != "" {
contractLocation = map[string]interface{}{
Expand All @@ -971,40 +956,97 @@ func (s *StackManager) runFirstTimeSetup(options *types.StartOptions) (messages
} else {
contractLocation = contractDeploymentResult.DeployedContract.Location
}

options := make(map[string]interface{})
if s.Stack.CustomPinSupport {
options["customPinSupport"] = true
}

newConfig.Namespaces.Predefined[0].Multiparty = &types.MultipartyConfig{
Enabled: true,
Org: orgConfig,
Node: &types.NodeConfig{
Name: member.NodeName,
if newConfig.Namespaces.Predefined[0].Multiparty == nil {
newConfig.Namespaces.Predefined[0].Multiparty = &types.MultipartyConfig{
Enabled: true,
Org: orgConfig,
Node: &types.NodeConfig{
Name: member.NodeName,
},
}
}

newConfig.Namespaces.Predefined[0].Multiparty.Contract = []*types.ContractConfig{
{
Location: contractLocation,
FirstEvent: options.FromBlock,
Options: options,
},
Contract: []*types.ContractConfig{
}

if contractDeploymentResult.Message != "" {
messages = append(messages, contractDeploymentResult.Message)
}
s.Stack.State.DeployedContracts = append(s.Stack.State.DeployedContracts, contractDeploymentResult.DeployedContract)
if err = s.writeStackStateJSON(s.Stack.RuntimeDir); err != nil {
return messages, err
}
}
}

for _, member := range s.Stack.Members {
orgConfig := s.blockchainProvider.GetOrgConfig(s.Stack, member)
newConfig.Namespaces.Predefined[0].DefaultKey = orgConfig.Key

if s.Stack.MultipartyEnabled {
if s.Stack.ContractAddress == "" {
s.Log.Info("deploying FireFly smart contracts")
contractDeploymentResult, err = s.blockchainProvider.DeployFireFlyContract()
if err != nil {
return messages, err
}

var contractLocation interface{}
if s.Stack.ContractAddress != "" {
contractLocation = map[string]interface{}{
"address": s.Stack.ContractAddress,
}
} else {
contractLocation = contractDeploymentResult.DeployedContract.Location
}

options := make(map[string]interface{})
if s.Stack.CustomPinSupport {
options["customPinSupport"] = true
}

if newConfig.Namespaces.Predefined[0].Multiparty == nil {
newConfig.Namespaces.Predefined[0].Multiparty = &types.MultipartyConfig{
Enabled: true,
Org: orgConfig,
Node: &types.NodeConfig{
Name: member.NodeName,
},
}
}

newConfig.Namespaces.Predefined[0].Multiparty.Contract = []*types.ContractConfig{
{
Location: contractLocation,
FirstEvent: "0",
FirstEvent: options.FromBlock,
Options: options,
},
},
}

if contractDeploymentResult.Message != "" {
messages = append(messages, contractDeploymentResult.Message)
}
s.Stack.State.DeployedContracts = append(s.Stack.State.DeployedContracts, contractDeploymentResult.DeployedContract)
if err = s.writeStackStateJSON(s.Stack.RuntimeDir); err != nil {
return messages, err
}
}
}

if err := s.patchFireFlyCoreConfigs(configDir, member, newConfig); err != nil {
return messages, err
}

// Create data directory with correct permissions inside volume
dataVolumeName := fmt.Sprintf("%s_firefly_core_data_%s", s.Stack.Name, member.ID)
if err := docker.CreateVolume(s.ctx, dataVolumeName); err != nil {
return messages, err
}
if err := docker.MkdirInVolume(s.ctx, dataVolumeName, "db"); err != nil {
return messages, err
}

}

// Re-write the docker-compose config again, in case new values have been added
Expand Down