Skip to content
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Bumped Rollups Contracts to 2.0
- Changed `cartesi-rollups-cli app register --prt` to assert the detected consensus type instead of forcing PRT registration, and `cartesi-rollups-cli deploy application --consensus` to reject PRT consensus addresses
- Normalized boolean configuration parameters (`CARTESI_LEGACY_BLOCKCHAIN_ENABLED`, `CARTESI_FEATURE_CLAIMER_ENABLED`, `CARTESI_FEATURE_MACHINE_HASH_CHECK_ENABLED`, `CARTESI_EXPERIMENTAL_SERVER_MANAGER_LOG_BYPASS_ENABLED` and `CARTESI_LOG_PRETTY_ENABLED`) and adjusted their logic accordingly

### Removed
Expand All @@ -24,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed `server-manager` from rollups-node Docker image
- Removed support to host mode

### Fixed

- Fixed `cartesi-rollups-cli` consensus type detection to use EIP-165 instead of probing a quorum method

## [1.5.1] 2024-08-26

### Added
Expand Down
73 changes: 41 additions & 32 deletions cmd/cartesi-rollups-cli/root/app/register/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ func init() {
"Enable or disable machine hash check (DO NOT DISABLE IN PRODUCTION)")
cobra.CheckErr(viper.BindPFlag(config.FEATURE_MACHINE_HASH_CHECK_ENABLED, Cmd.Flags().Lookup("machine-hash-check")))

Cmd.Flags().BoolVarP(&applicationTypePRT, "prt", "", false, "Register as PRT application.")
Cmd.Flags().BoolVarP(&applicationTypePRT, "prt", "", false,
"Assert that the application consensus is a PRT (Dave) consensus.")

origHelpFunc := Cmd.HelpFunc()
Cmd.SetHelpFunc(func(command *cobra.Command, strings []string) {
Expand Down Expand Up @@ -160,7 +161,18 @@ func run(cmd *cobra.Command, _ []string) {
}
}

if !cmd.Flags().Changed("epoch-length") && !applicationTypePRT {
consensusType, err := getConsensusType(ctx, consensus)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to detect consensus type: %v\n", err)
os.Exit(1)
}
if err := validateRegistrationConsensusType(consensusType, applicationTypePRT); err != nil {
fmt.Fprintf(os.Stderr, "Consensus type validation failed: %v\n", err)
os.Exit(1)
}
isPRT := consensusType == model.Consensus_PRT

if !cmd.Flags().Changed("epoch-length") && !isPRT {
epochLength, err = getEpochLength(ctx, consensus)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to get epoch length from consensus: %v\n",
Expand Down Expand Up @@ -215,12 +227,6 @@ func run(cmd *cobra.Command, _ []string) {
os.Exit(1)
}

consensusType, err := getConsensusType(ctx, consensus, applicationTypePRT)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to detect consensus type: %v\n", err)
os.Exit(1)
}

application := model.Application{
Name: validName,
IApplicationAddress: address,
Expand Down Expand Up @@ -368,19 +374,10 @@ func getClaimStagingPeriod(
return ethutil.GetClaimStagingPeriod(ctx, client, consensusAddr)
}

type quorumConsensusProbe interface {
NumOfValidators(opts *bind.CallOpts) (*big.Int, error)
}

func getConsensusType(
ctx context.Context,
consensusAddr common.Address,
applicationTypePRT bool,
) (model.Consensus, error) {
if applicationTypePRT {
return model.Consensus_PRT, nil
}

ethEndpoint, err := config.GetBlockchainHttpEndpoint()
if err != nil {
return "", fmt.Errorf("failed to get blockchain http endpoint address: %w", err)
Expand All @@ -389,28 +386,40 @@ func getConsensusType(
if err != nil {
return "", fmt.Errorf("failed to connect to the blockchain http endpoint: %s", ethEndpoint)
}
quorum, err := iquorum.NewIQuorum(consensusAddr, client)

callOpts := &bind.CallOpts{Context: ctx}
caller, err := iconsensus.NewIConsensusCaller(consensusAddr, client)
if err != nil {
return "", fmt.Errorf("failed to bind consensus contract: %w", err)
}
detected, _, err := ethutil.DetectConsensusType(callOpts, caller)
if err != nil {
return "", err
}
return consensusTypeFromQuorumProbe(applicationTypePRT, quorum)

if detected == ethutil.ConsensusQuorum {
quorum, err := iquorum.NewIQuorum(consensusAddr, client)
if err != nil {
return "", err
}
if err := ethutil.ValidateQuorum(callOpts, quorum); err != nil {
return "", err
}
}

return detected.ToModel(), nil
}

func consensusTypeFromQuorumProbe(
// validateRegistrationConsensusType keeps --prt as an assertion now that the
// consensus type comes from the chain instead of the flag.
func validateRegistrationConsensusType(
consensusType model.Consensus,
applicationTypePRT bool,
probe quorumConsensusProbe,
) (model.Consensus, error) {
if applicationTypePRT {
return model.Consensus_PRT, nil
}
numOfValidators, err := probe.NumOfValidators(nil)
if err != nil {
return model.Consensus_Authority, nil
}
if numOfValidators == nil || numOfValidators.Sign() == 0 {
return "", fmt.Errorf("quorum consensus reports zero validators")
) error {
if applicationTypePRT && consensusType != model.Consensus_PRT {
return fmt.Errorf("--prt was passed, but the consensus is %s", consensusType)
}
return model.Consensus_Quorum, nil
return nil
}

func readApplicationWithdrawalConfig(
Expand Down
97 changes: 41 additions & 56 deletions cmd/cartesi-rollups-cli/root/app/register/register_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,71 +4,56 @@
package register

import (
"errors"
"math/big"
"testing"

"github.com/cartesi/rollups-node/internal/model"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"
)

type quorumConsensusProbeStub struct {
numOfValidators *big.Int
err error
called bool
}

func (p *quorumConsensusProbeStub) NumOfValidators(_ *bind.CallOpts) (*big.Int, error) {
p.called = true
return p.numOfValidators, p.err
}

func TestConsensusTypeFromQuorumProbe_PRTSkipsProbe(t *testing.T) {
probe := &quorumConsensusProbeStub{
numOfValidators: big.NewInt(3),
func TestValidateRegistrationConsensusType(t *testing.T) {
tests := []struct {
name string
consensusType model.Consensus
applicationTypePRT bool
wantErr string
}{
{
name: "prt flag with prt consensus",
consensusType: model.Consensus_PRT,
applicationTypePRT: true,
},
{
name: "prt flag with authority consensus",
consensusType: model.Consensus_Authority,
applicationTypePRT: true,
wantErr: "--prt was passed",
},
{
name: "prt flag with quorum consensus",
consensusType: model.Consensus_Quorum,
applicationTypePRT: true,
wantErr: "--prt was passed",
},
{
name: "no prt flag with prt consensus",
consensusType: model.Consensus_PRT,
},
{
name: "no prt flag with authority consensus",
consensusType: model.Consensus_Authority,
},
}

consensusType, err := consensusTypeFromQuorumProbe(true, probe)

require.NoError(t, err)
require.Equal(t, model.Consensus_PRT, consensusType)
require.False(t, probe.called)
}

func TestConsensusTypeFromQuorumProbe_AuthorityWhenProbeFails(t *testing.T) {
probe := &quorumConsensusProbeStub{
err: errors.New("execution reverted"),
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateRegistrationConsensusType(tt.consensusType, tt.applicationTypePRT)
if tt.wantErr != "" {
require.ErrorContains(t, err, tt.wantErr)
return
}
require.NoError(t, err)
})
}

consensusType, err := consensusTypeFromQuorumProbe(false, probe)

require.NoError(t, err)
require.Equal(t, model.Consensus_Authority, consensusType)
require.True(t, probe.called)
}

func TestConsensusTypeFromQuorumProbe_QuorumWhenValidatorsExist(t *testing.T) {
probe := &quorumConsensusProbeStub{
numOfValidators: big.NewInt(3),
}

consensusType, err := consensusTypeFromQuorumProbe(false, probe)

require.NoError(t, err)
require.Equal(t, model.Consensus_Quorum, consensusType)
require.True(t, probe.called)
}

func TestConsensusTypeFromQuorumProbe_RejectsZeroValidatorQuorum(t *testing.T) {
probe := &quorumConsensusProbeStub{
numOfValidators: big.NewInt(0),
}

_, err := consensusTypeFromQuorumProbe(false, probe)

require.ErrorContains(t, err, "zero validators")
require.True(t, probe.called)
}

func TestRegisterCommandDoesNotExposeLegacyInputBoxFlags(t *testing.T) {
Expand Down
Loading
Loading