From 99368a56e59c14e91888a0c55dff5d74a912101a Mon Sep 17 00:00:00 2001 From: MKVEERENDRA Date: Sun, 17 Nov 2024 17:16:58 +0530 Subject: [PATCH] Fix: Solve issue #331 by adding FirstEvent support in FireFly config --- cmd/start.go | 1 + .../blockchain/ethereum/ethtypes/types.go | 6 +- internal/stacks/stack_manager.go | 154 ++++++++++-------- 3 files changed, 93 insertions(+), 68 deletions(-) diff --git a/cmd/start.go b/cmd/start.go index 95e0f496..ebd78fcb 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -104,5 +104,6 @@ This command will start a stack and run it in the background. func init() { startCmd.Flags().BoolVarP(&startOptions.NoRollback, "no-rollback", "b", false, "Do not automatically rollback changes if first time setup fails") + startCmd.Flags().StringVarP(&startOptions.FirstEvent, "first-event", "f", "0", "Specify the starting block for event processing (default: 0)") rootCmd.AddCommand(startCmd) } diff --git a/internal/blockchain/ethereum/ethtypes/types.go b/internal/blockchain/ethereum/ethtypes/types.go index 4e8bb080..b73c6d10 100644 --- a/internal/blockchain/ethereum/ethtypes/types.go +++ b/internal/blockchain/ethereum/ethtypes/types.go @@ -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"` diff --git a/internal/stacks/stack_manager.go b/internal/stacks/stack_manager.go index 687de986..a8d8e861 100644 --- a/internal/stacks/stack_manager.go +++ b/internal/stacks/stack_manager.go @@ -611,44 +611,52 @@ 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 + + // Validate the FirstEvent value + if options.FirstEvent != "0" && options.FirstEvent != "newest" { + if _, err := strconv.Atoi(options.FirstEvent); err != nil { + return messages, fmt.Errorf("invalid FirstEvent value: %s", options.FirstEvent) + } + } + + // Check port availability 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.StartOptions{ + NoRollback: false, // Example + FirstEvent: "newest", // Or the user-defined value + }) + 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) } @@ -940,61 +948,73 @@ func (s *StackManager) runFirstTimeSetup(options *types.StartOptions) (messages newConfig.Namespaces.Predefined[0].Plugins = append(newConfig.Namespaces.Predefined[0].Plugins, types.FFEnumArrayToStrings(s.Stack.TokenProviders)...) + + 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 - if s.Stack.MultipartyEnabled { - 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 - } - - newConfig.Namespaces.Predefined[0].Multiparty = &types.MultipartyConfig{ - Enabled: true, - Org: orgConfig, - Node: &types.NodeConfig{ - Name: member.NodeName, - }, - Contract: []*types.ContractConfig{ - { - Location: contractLocation, - FirstEvent: "0", - Options: options, - }, - }, - } - } - - if err := s.patchFireFlyCoreConfigs(configDir, member, newConfig); err != nil { - return messages, err - } + 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 + } + } + } + + for _, member := range s.Stack.Members { + orgConfig := s.blockchainProvider.GetOrgConfig(s.Stack, member) + + // Default to "0" if FirstEvent is not provided + firstEvent := "0" + if options.FirstEvent != "" { + firstEvent = options.FirstEvent + } + + var contractLocation interface{} + if s.Stack.ContractAddress != "" { + contractLocation = map[string]interface{}{ + "address": s.Stack.ContractAddress, + } + } else { + contractLocation = contractDeploymentResult.DeployedContract.Location + } + + optionsMap := make(map[string]interface{}) + if s.Stack.CustomPinSupport { + optionsMap["customPinSupport"] = true + } + + newConfig := &types.FireflyConfig{ + Namespaces: &types.NamespacesConfig{ + Default: "default", + Predefined: []*types.Namespace{ + { + Name: "default", + Description: "Default predefined namespace", + Plugins: []string{"database0", "blockchain0", "dataexchange0", "sharedstorage0"}, + Multiparty: &types.MultipartyConfig{ + Enabled: true, + Org: orgConfig, + Node: &types.NodeConfig{ + Name: member.NodeName, + }, + Contract: []*types.ContractConfig{ + { + Location: contractLocation, + FirstEvent: firstEvent, // Now configurable + Options: optionsMap, + }, + }, + }, + }, + }, + }, + } + + 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)