diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index c291679b181..57172298c3e 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -281,6 +281,9 @@ # NFTStopCreateEnableEpoch represents the epoch when NFT stop create feature is enabled NFTStopCreateEnableEpoch = 3 + # ChangeOwnerAddressCrossShardThroughSCEnableEpoch represents the epoch when the change owner address built in function will work also through a smart contract call cross shard + ChangeOwnerAddressCrossShardThroughSCEnableEpoch = 3 + # BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers BLSMultiSignerEnableEpoch = [ { EnableEpoch = 0, Type = "no-KOSK" }, diff --git a/common/enablers/enableEpochsHandler.go b/common/enablers/enableEpochsHandler.go index 844d513d9f4..7e5ea0bc18e 100644 --- a/common/enablers/enableEpochsHandler.go +++ b/common/enablers/enableEpochsHandler.go @@ -131,6 +131,7 @@ func (handler *enableEpochsHandler) EpochConfirmed(epoch uint32, _ uint64) { handler.setFlagValue(epoch >= handler.enableEpochsConfig.SCProcessorV2EnableEpoch, handler.scProcessorV2Flag, "scProcessorV2Flag", epoch, handler.enableEpochsConfig.SCProcessorV2EnableEpoch) handler.setFlagValue(epoch >= handler.enableEpochsConfig.DynamicGasCostForDataTrieStorageLoadEnableEpoch, handler.dynamicGasCostForDataTrieStorageLoadFlag, "dynamicGasCostForDataTrieStorageLoadFlag", epoch, handler.enableEpochsConfig.DynamicGasCostForDataTrieStorageLoadEnableEpoch) handler.setFlagValue(epoch >= handler.enableEpochsConfig.NFTStopCreateEnableEpoch, handler.nftStopCreateFlag, "nftStopCreateFlag", epoch, handler.enableEpochsConfig.NFTStopCreateEnableEpoch) + handler.setFlagValue(epoch >= handler.enableEpochsConfig.ChangeOwnerAddressCrossShardThroughSCEnableEpoch, handler.changeOwnerAddressCrossShardThroughSCFlag, "changeOwnerAddressCrossShardThroughSCFlag", epoch, handler.enableEpochsConfig.ChangeOwnerAddressCrossShardThroughSCEnableEpoch) } func (handler *enableEpochsHandler) setFlagValue(value bool, flag *atomic.Flag, flagName string, epoch uint32, flagEpoch uint32) { diff --git a/common/enablers/epochFlags.go b/common/enablers/epochFlags.go index 93133010473..2b0ca8d884c 100644 --- a/common/enablers/epochFlags.go +++ b/common/enablers/epochFlags.go @@ -103,6 +103,7 @@ type epochFlagsHolder struct { fixDelegationChangeOwnerOnAccountFlag *atomic.Flag dynamicGasCostForDataTrieStorageLoadFlag *atomic.Flag nftStopCreateFlag *atomic.Flag + changeOwnerAddressCrossShardThroughSCFlag *atomic.Flag } func newEpochFlagsHolder() *epochFlagsHolder { @@ -205,6 +206,7 @@ func newEpochFlagsHolder() *epochFlagsHolder { fixDelegationChangeOwnerOnAccountFlag: &atomic.Flag{}, dynamicGasCostForDataTrieStorageLoadFlag: &atomic.Flag{}, nftStopCreateFlag: &atomic.Flag{}, + changeOwnerAddressCrossShardThroughSCFlag: &atomic.Flag{}, } } @@ -750,3 +752,8 @@ func (holder *epochFlagsHolder) IsDynamicGasCostForDataTrieStorageLoadEnabled() func (holder *epochFlagsHolder) NFTStopCreateEnabled() bool { return holder.nftStopCreateFlag.IsSet() } + +// IsChangeOwnerAddressCrossShardThroughSCEnabled return true if the changeOwnerAddressCrossShardThroughSCFlag is enabled +func (holder *epochFlagsHolder) IsChangeOwnerAddressCrossShardThroughSCEnabled() bool { + return holder.changeOwnerAddressCrossShardThroughSCFlag.IsSet() +} diff --git a/common/interface.go b/common/interface.go index bd543086c09..52cdce6aefe 100644 --- a/common/interface.go +++ b/common/interface.go @@ -396,6 +396,7 @@ type EnableEpochsHandler interface { IsDynamicGasCostForDataTrieStorageLoadEnabled() bool FixDelegationChangeOwnerOnAccountEnabled() bool NFTStopCreateEnabled() bool + IsChangeOwnerAddressCrossShardThroughSCEnabled() bool IsInterfaceNil() bool } diff --git a/config/epochConfig.go b/config/epochConfig.go index 649966b5a6c..bbdfe39284e 100644 --- a/config/epochConfig.go +++ b/config/epochConfig.go @@ -106,6 +106,7 @@ type EnableEpochs struct { FixDelegationChangeOwnerOnAccountEnableEpoch uint32 DynamicGasCostForDataTrieStorageLoadEnableEpoch uint32 NFTStopCreateEnableEpoch uint32 + ChangeOwnerAddressCrossShardThroughSCEnableEpoch uint32 BLSMultiSignerEnableEpoch []MultiSignerConfig } diff --git a/config/tomlConfig_test.go b/config/tomlConfig_test.go index 1b0c20cb147..a844be408c0 100644 --- a/config/tomlConfig_test.go +++ b/config/tomlConfig_test.go @@ -828,6 +828,9 @@ func TestEnableEpochConfig(t *testing.T) { # NFTStopCreateEnableEpoch represents the epoch when NFT stop create feature is enabled NFTStopCreateEnableEpoch = 89 + # ChangeOwnerAddressCrossShardThroughSCEnableEpoch represents the epoch when the change owner address built in function will work also through a smart contract call cross shard + ChangeOwnerAddressCrossShardThroughSCEnableEpoch = 90 + # MaxNodesChangeEnableEpoch holds configuration for changing the maximum number of nodes and the enabling epoch MaxNodesChangeEnableEpoch = [ { EpochEnable = 44, MaxNumNodes = 2169, NodesToShufflePerShard = 80 }, @@ -937,6 +940,7 @@ func TestEnableEpochConfig(t *testing.T) { FixDelegationChangeOwnerOnAccountEnableEpoch: 87, ScToScLogEventEnableEpoch: 88, NFTStopCreateEnableEpoch: 89, + ChangeOwnerAddressCrossShardThroughSCEnableEpoch: 90, MaxNodesChangeEnableEpoch: []MaxNodesChangeConfig{ { EpochEnable: 44, diff --git a/go.mod b/go.mod index 9f5aa96dcd9..8f32d663f1a 100644 --- a/go.mod +++ b/go.mod @@ -20,8 +20,8 @@ require ( github.com/multiversx/mx-chain-logger-go v1.0.13 github.com/multiversx/mx-chain-scenario-go v1.2.1 github.com/multiversx/mx-chain-storage-go v1.0.13 - github.com/multiversx/mx-chain-vm-common-go v1.5.5 - github.com/multiversx/mx-chain-vm-go v1.5.16 + github.com/multiversx/mx-chain-vm-common-go v1.5.7 + github.com/multiversx/mx-chain-vm-go v1.5.18 github.com/multiversx/mx-chain-vm-v1_2-go v1.2.61 github.com/multiversx/mx-chain-vm-v1_3-go v1.3.62 github.com/multiversx/mx-chain-vm-v1_4-go v1.4.88 diff --git a/go.sum b/go.sum index b5149a6dfc5..f1656c727c6 100644 --- a/go.sum +++ b/go.sum @@ -398,10 +398,10 @@ github.com/multiversx/mx-chain-scenario-go v1.2.1 h1:9eC6VcOEAKRRKZ7EbSWPLzCdNIM github.com/multiversx/mx-chain-scenario-go v1.2.1/go.mod h1:EuZY7DpNFHVNSxJR8dKE1z2I8gBYfEFFPSwNUOXptqE= github.com/multiversx/mx-chain-storage-go v1.0.13 h1:i41VPDJZ0pn5gf18zTXrac5xeiolUOztNuzL3wEXRuI= github.com/multiversx/mx-chain-storage-go v1.0.13/go.mod h1:sJ2q49tgjxNpMpsHysjABqCAB0FLBmDblbjBkQ8XfmA= -github.com/multiversx/mx-chain-vm-common-go v1.5.5 h1:NoG73lvcHSeUcoFlYybG8ceGuJ6KptD3QJjUNEnGDVk= -github.com/multiversx/mx-chain-vm-common-go v1.5.5/go.mod h1:sqkKMCnwkWl8DURdb9q7pctK8IANghdHY1KJLE0ox2c= -github.com/multiversx/mx-chain-vm-go v1.5.16 h1:vBahY/aRe03yRlBPUu/hV+cNTx3MiVLYF+q9Uca+dfU= -github.com/multiversx/mx-chain-vm-go v1.5.16/go.mod h1:F5OoQjCuYNr1hYWvwZKCcWYQir3+r2QVBxQux/eo0Ak= +github.com/multiversx/mx-chain-vm-common-go v1.5.7 h1:GiT2MWG2aCQX59iOk5waB+z7XSLHH7N5xO8b91j6L6s= +github.com/multiversx/mx-chain-vm-common-go v1.5.7/go.mod h1:sqkKMCnwkWl8DURdb9q7pctK8IANghdHY1KJLE0ox2c= +github.com/multiversx/mx-chain-vm-go v1.5.18 h1:jLgB/9vcqkQz+2P/lFYIfrubDT5gErn+ioPd74+nga8= +github.com/multiversx/mx-chain-vm-go v1.5.18/go.mod h1:uZ4uinTCa2r3UT1WwbGg6yMq5HLeivHnFemmPG/4V+o= github.com/multiversx/mx-chain-vm-v1_2-go v1.2.61 h1:7c3VRhr5JDu7qs3AkmKQu7DzWGGIoiHfSIMrzw3x5Ao= github.com/multiversx/mx-chain-vm-v1_2-go v1.2.61/go.mod h1:bQFh+KuUIEBmCfKJ0qVN2+DbRRbAqW0huKfHpiTbyEE= github.com/multiversx/mx-chain-vm-v1_3-go v1.3.62 h1:rQaWRbrQwrEhSN0ZEQQ0JAbttgi+OrMf/CLziWpRUCA= diff --git a/integrationTests/vm/txsFee/multiShard/asyncCallWithChangeOwner_test.go b/integrationTests/vm/txsFee/multiShard/asyncCallWithChangeOwner_test.go new file mode 100644 index 00000000000..aac3723f294 --- /dev/null +++ b/integrationTests/vm/txsFee/multiShard/asyncCallWithChangeOwner_test.go @@ -0,0 +1,96 @@ +package multiShard + +import ( + "encoding/hex" + "math/big" + "strings" + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" + "github.com/multiversx/mx-chain-go/integrationTests/vm" + "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/require" +) + +func TestDoChangeOwnerCrossShardFromAContract(t *testing.T) { + if testing.Short() { + t.Skip("cannot run with -race -short; requires Wasm VM fix") + } + + enableEpochs := config.EnableEpochs{ + DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, + ChangeOwnerAddressCrossShardThroughSCEnableEpoch: 0, + } + + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + require.Nil(t, err) + defer testContextSource.Close() + + // STEP 1 + // deploy first contract (this contract has a function that will call the `ChangeOwnerAddress` built-in function) + // on shard 0 + pathToContract := "../testdata/changeOwner/contract.wasm" + firstContract, firstOwner := utils.DoDeployWithCustomParams(t, testContextSource, pathToContract, big.NewInt(100000000000), 15000, nil) + + utils.CleanAccumulatedIntermediateTransactions(t, testContextSource) + testContextSource.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) + testContextSource.TxsLogsProcessor.Clean() + + require.Equal(t, uint32(0), testContextSource.ShardCoordinator.ComputeId(firstContract)) + require.Equal(t, uint32(0), testContextSource.ShardCoordinator.ComputeId(firstOwner)) + + testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + require.Nil(t, err) + defer testContextSecondContract.Close() + + // STEP 2 + // deploy the second contract on shard 1 + pathToContract = "../testdata/first/output/first.wasm" + secondSCAddress, deployer := utils.DoDeployWithCustomParams(t, testContextSecondContract, pathToContract, big.NewInt(100000000000), 15000, nil) + require.Equal(t, uint32(1), testContextSource.ShardCoordinator.ComputeId(secondSCAddress)) + + // STEP 3 + // change the owner of the second contract -- the new owner will be the first contract + gasPrice := uint64(10) + txData := []byte(core.BuiltInFunctionChangeOwnerAddress + "@" + hex.EncodeToString(firstContract)) + tx := vm.CreateTransaction(1, big.NewInt(0), deployer, secondSCAddress, gasPrice, 1000, txData) + returnCode, err := testContextSecondContract.TxProcessor.ProcessTransaction(tx) + require.Nil(t, err) + require.Equal(t, vmcommon.Ok, returnCode) + _, err = testContextSecondContract.Accounts.Commit() + require.Nil(t, err) + utils.CheckOwnerAddr(t, testContextSecondContract, secondSCAddress, firstContract) + + // STEP 3 + // call `change_owner` function from the first contract + gasLimit := uint64(5000000) + dataField := append([]byte("change_owner"), []byte("@"+hex.EncodeToString(secondSCAddress))...) + dataField = append(dataField, []byte("@"+hex.EncodeToString(firstOwner))...) + tx = vm.CreateTransaction(1, big.NewInt(0), firstOwner, firstContract, gasPrice, gasLimit, dataField) + + retCode, err := testContextSource.TxProcessor.ProcessTransaction(tx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) + + intermediateTxs := testContextSource.GetIntermediateTransactions(t) + require.Equal(t, 1, len(intermediateTxs)) + + expectedDataField := core.BuiltInFunctionChangeOwnerAddress + "@" + hex.EncodeToString(firstOwner) + require.True(t, strings.HasPrefix(string(intermediateTxs[0].GetData()), expectedDataField)) + + logs := testContextSource.TxsLogsProcessor.GetAllCurrentLogs() + require.NotNil(t, logs) + + // STEP 4 + // executed results smart contract results of the shard1 where the second contract was deployed + testContextSecondContract.TxsLogsProcessor.Clean() + utils.ProcessSCRResult(t, testContextSecondContract, intermediateTxs[0], vmcommon.Ok, nil) + utils.CheckOwnerAddr(t, testContextSecondContract, secondSCAddress, firstOwner) + + logs = testContextSecondContract.TxsLogsProcessor.GetAllCurrentLogs() + require.NotNil(t, logs) + require.Equal(t, core.BuiltInFunctionChangeOwnerAddress, string(logs[0].GetLogEvents()[0].GetIdentifier())) +} diff --git a/integrationTests/vm/txsFee/testdata/changeOwner/contract.wasm b/integrationTests/vm/txsFee/testdata/changeOwner/contract.wasm new file mode 100755 index 00000000000..748ee908524 Binary files /dev/null and b/integrationTests/vm/txsFee/testdata/changeOwner/contract.wasm differ diff --git a/sharding/mock/enableEpochsHandlerMock.go b/sharding/mock/enableEpochsHandlerMock.go index 8516a318510..d5e925262d6 100644 --- a/sharding/mock/enableEpochsHandlerMock.go +++ b/sharding/mock/enableEpochsHandlerMock.go @@ -9,6 +9,11 @@ type EnableEpochsHandlerMock struct { IsFixOldTokenLiquidityFlagEnabledField bool } +// IsChangeOwnerAddressCrossShardThroughSCEnabled - +func (mock *EnableEpochsHandlerMock) IsChangeOwnerAddressCrossShardThroughSCEnabled() bool { + return false +} + // BlockGasAndFeesReCheckEnableEpoch returns 0 func (mock *EnableEpochsHandlerMock) BlockGasAndFeesReCheckEnableEpoch() uint32 { return 0 diff --git a/testscommon/enableEpochsHandlerMock/enableEpochsHandlerStub.go b/testscommon/enableEpochsHandlerMock/enableEpochsHandlerStub.go index 47bc57cd31e..8b8cb4e0b40 100644 --- a/testscommon/enableEpochsHandlerMock/enableEpochsHandlerStub.go +++ b/testscommon/enableEpochsHandlerMock/enableEpochsHandlerStub.go @@ -132,6 +132,7 @@ type EnableEpochsHandlerStub struct { FixDelegationChangeOwnerOnAccountEnabledField bool IsDynamicGasCostForDataTrieStorageLoadEnabledField bool IsNFTStopCreateEnabledField bool + IsChangeOwnerAddressCrossShardThroughSCEnabledField bool } // ResetPenalizedTooMuchGasFlag - @@ -1133,6 +1134,14 @@ func (stub *EnableEpochsHandlerStub) NFTStopCreateEnabled() bool { return stub.IsNFTStopCreateEnabledField } +// IsChangeOwnerAddressCrossShardThroughSCEnabled - +func (stub *EnableEpochsHandlerStub) IsChangeOwnerAddressCrossShardThroughSCEnabled() bool { + stub.RLock() + defer stub.RUnlock() + + return stub.IsChangeOwnerAddressCrossShardThroughSCEnabledField +} + // IsInterfaceNil - func (stub *EnableEpochsHandlerStub) IsInterfaceNil() bool { return stub == nil