diff --git a/integrationTests/relayers/slowTests/framework/keys.go b/integrationTests/relayers/slowTests/framework/keys.go index 6264ead2..f90a9dd2 100644 --- a/integrationTests/relayers/slowTests/framework/keys.go +++ b/integrationTests/relayers/slowTests/framework/keys.go @@ -3,6 +3,7 @@ package framework import ( "bytes" "crypto/ecdsa" + "crypto/rand" "encoding/hex" "encoding/pem" "fmt" @@ -33,6 +34,7 @@ type KeysHolder struct { type KeysStore struct { testing.TB RelayersKeys []KeysHolder + OraclesKeys []KeysHolder SCExecutorKeys KeysHolder OwnerKeys KeysHolder DepositorKeys KeysHolder @@ -51,6 +53,7 @@ func NewKeysStore( tb testing.TB, workingDir string, numRelayers int, + numOracles int, ) *KeysStore { keysStore := &KeysStore{ TB: tb, @@ -60,6 +63,7 @@ func NewKeysStore( } keysStore.generateRelayersKeys(numRelayers) + keysStore.OraclesKeys = keysStore.generateKeys(numOracles, "generated oracle") keysStore.SCExecutorKeys = keysStore.generateKey("") keysStore.OwnerKeys = keysStore.generateKey(ethOwnerSK) log.Info("generated owner", @@ -92,6 +96,24 @@ func (keyStore *KeysStore) generateRelayersKeys(numKeys int) { } } +func (keyStore *KeysStore) generateKeys(numKeys int, message string) []KeysHolder { + keys := make([]KeysHolder, 0, numKeys) + + for i := 0; i < numKeys; i++ { + ethPrivateKeyBytes := make([]byte, 32) + _, _ = rand.Read(ethPrivateKeyBytes) + + key := keyStore.generateKey(hex.EncodeToString(ethPrivateKeyBytes)) + log.Info(message, "index", i, + "MvX address", key.MvxAddress.Bech32(), + "Eth address", key.EthAddress.String()) + + keys = append(keys, key) + } + + return keys +} + func (keyStore *KeysStore) generateKey(ethSkHex string) KeysHolder { var err error @@ -112,6 +134,7 @@ func (keyStore *KeysStore) generateKey(ethSkHex string) KeysHolder { func (keyStore *KeysStore) getAllKeys() []KeysHolder { allKeys := make([]KeysHolder, 0, len(keyStore.RelayersKeys)+10) allKeys = append(allKeys, keyStore.RelayersKeys...) + allKeys = append(allKeys, keyStore.OraclesKeys...) allKeys = append(allKeys, keyStore.SCExecutorKeys, keyStore.OwnerKeys, keyStore.DepositorKeys, keyStore.TestKeys) return allKeys diff --git a/integrationTests/relayers/slowTests/framework/multiversxHandler.go b/integrationTests/relayers/slowTests/framework/multiversxHandler.go index f0a794c7..35878073 100644 --- a/integrationTests/relayers/slowTests/framework/multiversxHandler.go +++ b/integrationTests/relayers/slowTests/framework/multiversxHandler.go @@ -33,7 +33,7 @@ const ( createDepositGasLimit = 20000000 // 20 million gasLimitPerDataByte = 1500 - aggregatorContractPath = "testdata/contracts/mvx/aggregator.wasm" + aggregatorContractPath = "testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm" wrapperContractPath = "testdata/contracts/mvx/bridged-tokens-wrapper.wasm" multiTransferContractPath = "testdata/contracts/mvx/multi-transfer-esdt.wasm" safeContractPath = "testdata/contracts/mvx/esdt-safe.wasm" @@ -139,9 +139,12 @@ func (handler *MultiversxHandler) deployContracts(ctx context.Context) { hex.EncodeToString([]byte("EGLD")), hex.EncodeToString(stakeValue.Bytes()), "01", - "01", - "01", - handler.OwnerKeys.MvxAddress.Hex(), + "03", + "03", + } + + for _, oracleKey := range handler.OraclesKeys { + aggregatorDeployParams = append(aggregatorDeployParams, oracleKey.MvxAddress.Hex()) } hash := "" @@ -153,7 +156,7 @@ func (handler *MultiversxHandler) deployContracts(ctx context.Context) { aggregatorDeployParams, ) require.NotEqual(handler, emptyAddress, handler.AggregatorAddress) - log.Info("Deploy: aggregator contract", "address", handler.AggregatorAddress, "transaction hash", hash) + log.Info("Deploy: aggregator contract", "address", handler.AggregatorAddress, "transaction hash", hash, "num oracles", len(handler.OraclesKeys)) // deploy wrapper handler.WrapperAddress, hash, _ = handler.ChainSimulator.DeploySC( @@ -425,7 +428,7 @@ func (handler *MultiversxHandler) finishSettings(ctx context.Context) { handler.stakeAddressesOnContract(ctx, handler.MultisigAddress, handler.RelayersKeys) // stake relayers on price aggregator - handler.stakeAddressesOnContract(ctx, handler.AggregatorAddress, []KeysHolder{handler.OwnerKeys}) + handler.stakeAddressesOnContract(ctx, handler.AggregatorAddress, handler.OraclesKeys) // unpause multisig hash, txResult = handler.callContractNoParams(ctx, handler.MultisigAddress, unpauseFunction) @@ -807,6 +810,12 @@ func (handler *MultiversxHandler) getTokenNameFromResult(txResult data.Transacti // SubmitAggregatorBatch will submit the aggregator batch func (handler *MultiversxHandler) SubmitAggregatorBatch(ctx context.Context, params IssueTokenParams) { + for _, key := range handler.OraclesKeys { + handler.submitAggregatorBatchForKey(ctx, key, params) + } +} + +func (handler *MultiversxHandler) submitAggregatorBatchForKey(ctx context.Context, key KeysHolder, params IssueTokenParams) { timestamp := handler.ChainSimulator.GetBlockchainTimeStamp(ctx) require.Greater(handler, timestamp, uint64(0), "something went wrong and the chain simulator returned 0 for the current timestamp") @@ -814,7 +823,7 @@ func (handler *MultiversxHandler) SubmitAggregatorBatch(ctx context.Context, par hash, txResult := handler.ChainSimulator.ScCall( ctx, - handler.OwnerKeys.MvxSk, + key.MvxSk, handler.AggregatorAddress, zeroStringValue, setCallsGasLimit, @@ -825,7 +834,8 @@ func (handler *MultiversxHandler) SubmitAggregatorBatch(ctx context.Context, par hex.EncodeToString(timestampAsBigInt.Bytes()), hex.EncodeToString(feeInt.Bytes()), fmt.Sprintf("%02x", params.NumOfDecimalsChainSpecific)}) - log.Info("submit aggregator batch tx executed", "hash", hash, "submitter", handler.OwnerKeys.MvxAddress, "status", txResult.Status) + + log.Info("submit aggregator batch tx executed", "transaction hash", hash, "submitter", key.MvxAddress.Bech32(), "status", txResult.Status) } // CreateDepositsOnMultiversxForToken will send the deposit transactions on MultiversX returning how many tokens should be minted on Ethereum diff --git a/integrationTests/relayers/slowTests/framework/testSetup.go b/integrationTests/relayers/slowTests/framework/testSetup.go index 4f199474..15fc563e 100644 --- a/integrationTests/relayers/slowTests/framework/testSetup.go +++ b/integrationTests/relayers/slowTests/framework/testSetup.go @@ -22,6 +22,7 @@ const ( proxyCacherExpirationSeconds = 600 proxyMaxNoncesDelta = 7 NumRelayers = 3 + NumOracles = 4 quorum = "03" ) @@ -57,7 +58,7 @@ func NewTestSetup(tb testing.TB) *TestSetup { esdtBalanceForSafe: make(map[string]*big.Int), ethBalanceTestAddress: make(map[string]*big.Int), } - setup.KeysStore = NewKeysStore(tb, setup.WorkingDir, NumRelayers) + setup.KeysStore = NewKeysStore(tb, setup.WorkingDir, NumRelayers, NumOracles) // create a test context setup.Ctx, setup.ctxCancel = context.WithCancel(context.Background()) diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/aggregator.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/aggregator.wasm deleted file mode 100644 index ad204ad3..00000000 Binary files a/integrationTests/relayers/slowTests/testdata/contracts/mvx/aggregator.wasm and /dev/null differ diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.abi.json new file mode 100644 index 00000000..e68a5a84 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.abi.json @@ -0,0 +1,440 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.80.0-nightly", + "commitHash": "791adf759cc065316f054961875052d5bc03e16c", + "commitDate": "2024-05-21", + "channel": "Nightly", + "short": "rustc 1.80.0-nightly (791adf759 2024-05-21)" + }, + "contractCrate": { + "name": "multiversx-price-aggregator-sc", + "version": "0.47.1", + "gitVersion": "v0.45.2.1-reproducible-337-g5478c6e" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.51.1" + } + }, + "name": "PriceAggregator", + "constructor": { + "inputs": [ + { + "name": "staking_token", + "type": "EgldOrEsdtTokenIdentifier" + }, + { + "name": "staking_amount", + "type": "BigUint" + }, + { + "name": "slash_amount", + "type": "BigUint" + }, + { + "name": "slash_quorum", + "type": "u32" + }, + { + "name": "submission_count", + "type": "u32" + }, + { + "name": "oracles", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + "endpoints": [ + { + "name": "changeAmounts", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "staking_amount", + "type": "BigUint" + }, + { + "name": "slash_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "addOracles", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "oracles", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Also receives submission count,", + "so the owner does not have to update it manually with setSubmissionCount before this call" + ], + "name": "removeOracles", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "submission_count", + "type": "u32" + }, + { + "name": "oracles", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "submit", + "mutability": "mutable", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + }, + { + "name": "submission_timestamp", + "type": "u64" + }, + { + "name": "price", + "type": "BigUint" + }, + { + "name": "decimals", + "type": "u8" + } + ], + "outputs": [] + }, + { + "name": "submitBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "submissions", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "latestRoundData", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "latestPriceFeed", + "mutability": "readonly", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "u32" + }, + { + "type": "bytes" + }, + { + "type": "bytes" + }, + { + "type": "u64" + }, + { + "type": "BigUint" + }, + { + "type": "u8" + } + ] + }, + { + "name": "latestPriceFeedOptional", + "mutability": "readonly", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "optional>", + "multi_result": true + } + ] + }, + { + "name": "setSubmissionCount", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "submission_count", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "getOracles", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ] + }, + { + "name": "setPairDecimals", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + }, + { + "name": "decimals", + "type": "u8" + } + ], + "outputs": [] + }, + { + "name": "getPairDecimals", + "mutability": "readonly", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "u8" + } + ] + }, + { + "name": "submission_count", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "stake", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [] + }, + { + "name": "unstake", + "mutability": "mutable", + "inputs": [ + { + "name": "unstake_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "voteSlashMember", + "mutability": "mutable", + "inputs": [ + { + "name": "member_to_slash", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "cancelVoteSlashMember", + "mutability": "mutable", + "inputs": [ + { + "name": "member_to_slash", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "slashMember", + "mutability": "mutable", + "inputs": [ + { + "name": "member_to_slash", + "type": "Address" + } + ], + "outputs": [] + } + ], + "events": [ + { + "identifier": "new_round", + "inputs": [ + { + "name": "from", + "type": "bytes", + "indexed": true + }, + { + "name": "to", + "type": "bytes", + "indexed": true + }, + { + "name": "epoch", + "type": "u64", + "indexed": true + }, + { + "name": "new_round_event", + "type": "NewRoundEvent" + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "NewRoundEvent": { + "type": "struct", + "fields": [ + { + "name": "price", + "type": "BigUint" + }, + { + "name": "timestamp", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + }, + { + "name": "block", + "type": "u64" + }, + { + "name": "epoch", + "type": "u64" + } + ] + }, + "PriceFeed": { + "type": "struct", + "fields": [ + { + "name": "round_id", + "type": "u32" + }, + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + }, + { + "name": "timestamp", + "type": "u64" + }, + { + "name": "price", + "type": "BigUint" + }, + { + "name": "decimals", + "type": "u8" + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm new file mode 100644 index 00000000..07062958 Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm differ