-
Notifications
You must be signed in to change notification settings - Fork 3
Refactor native token transfers to use deposited transactions instead of withdrawals #58
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
Merged
Merged
Changes from 90 commits
Commits
Show all changes
93 commits
Select commit
Hold shift + click to select a range
ab9939f
Rebuild on main
vladimirlogachev 26244d2
Fix MultipleTransfersViaDepositsTestSuite
vladimirlogachev 15cf88a
Combine two features into a single feature
vladimirlogachev d0f8a6a
Fix "Enough C2E transfers" test case
vladimirlogachev 92ec9d2
Add condition for the `from` field vaidation in validateC2EAssetTransfer
vladimirlogachev c8383b8
Refactor `requireTransfer` to use extractors
vladimirlogachev 4635bc4
Fix unit test
vladimirlogachev 28211b6
Clean up
vladimirlogachev 92ef93f
Remove limitation on native transfers number
vladimirlogachev 2ebbdba
Update comments
vladimirlogachev 75481bb
Add more logs
vladimirlogachev 6b84915
Reorder `depositedTransactions` in `updateHeadAndStartBuildingPayload`
vladimirlogachev 22c6814
Fix a bug with `MaxWithdrawals` usage
vladimirlogachev 2961a88
Add ManyTransfersTestSuite
vladimirlogachev d286e36
Clean up tests
vladimirlogachev e39b09e
Apply fix for transfers order
vladimirlogachev 4420057
Add more cases to C2ETransfersTestSuite
vladimirlogachev 7568708
Add one more test case
vladimirlogachev ed9f26f
Add more test cases
vladimirlogachev 171d430
Fix strict transfers unit test
vladimirlogachev d80738b
Update consensus-client-it/src/test/scala/units/ManyTransfersTestSuit…
vladimirlogachev aeeb38f
Update consensus-client-it/src/test/scala/units/ManyTransfersTestSuit…
vladimirlogachev 4d3870d
Update src/main/scala/units/client/contract/ChainContractClient.scala
vladimirlogachev 6bf5293
Fix according to the review
vladimirlogachev 8ceebd7
Simplify `updateHeadAndStartBuildingPayload`
vladimirlogachev c0a0c95
SImplify ManyTransfersTestSuite
vladimirlogachev 6f6e79a
Fix formatting
vladimirlogachev 0a410a2
Simplify `depositedTransactions` calculation
vladimirlogachev d261096
Fix error message to match the validation
vladimirlogachev fc250ca
Merge branch 'main' into native-token-transfers-deposits-2
vladimirlogachev 5ea1249
Merge branch 'main' into native-token-transfers-deposits-2
vladimirlogachev af61bb2
Merge branch 'main' into native-token-transfers-deposits-2
vladimirlogachev 53b092a
Adjust the check
vladimirlogachev 951a09e
Overrride `equals` for DepositedTransaction, add unit test
vladimirlogachev 63d252f
Fix validation for strict transfers enabled
vladimirlogachev 9e8838a
Remove logs
vladimirlogachev a4ddabc
Fix a bug in validation
vladimirlogachev ff1fe63
Fix a bug with indices, unify range creation
vladimirlogachev fa810e1
Extract `prepareTransactions` method
vladimirlogachev 2d2fb5c
Merge remote-tracking branch 'origin/main' into native-token-transfer…
vladimirlogachev 5ab6b87
Add ec2 to BaseDockerTestSuite
vladimirlogachev 91feaa0
Add BlockValidationTestSuite (WIP)
vladimirlogachev b1aa2b9
Remove ec2
vladimirlogachev 0b9b5c1
Add `enableMining` flag to WavesNodeContainer
vladimirlogachev dd2f92b
Override `waves1` in BlockValidationTestSuite
vladimirlogachev 32a9a59
Add 2 more EL miners
vladimirlogachev e7eb0dc
WIP
vladimirlogachev 6bea7eb
Register the EL block on chain contract
vladimirlogachev f915bd2
Fix vrf calculation
vladimirlogachev 0f9e562
Fix assertions
vladimirlogachev 52fe888
Add assertion: Block exists on EC1
vladimirlogachev fcdf650
Update comments
vladimirlogachev db8436c
Remove block validation test cases from C2ETransfersTestSuite
vladimirlogachev dc5191c
Refactor to use `Handshake.encode` method
vladimirlogachev fe60889
Refactor to use netty.bootstrap
vladimirlogachev dd0e912
Simplify BlockValidationTestSuite
vladimirlogachev 20a9cb2
Simplify TestNetworkClient
vladimirlogachev 8c0a3ce
Simplify TestNetworkClient
vladimirlogachev 815add9
Adjust wording
vladimirlogachev 74ca44b
Add successful case to BlockValidationTestSuite
vladimirlogachev 8c0c802
WIP
vladimirlogachev bface09
Add log
vladimirlogachev ccfc9da
WIP
vladimirlogachev debad71
Fix successful case
vladimirlogachev da42eb2
SImplify BlockValidationTestSuite
vladimirlogachev fc03b1e
Speed up test
vladimirlogachev fbd475c
Extract BaseBlockValidationSuite
vladimirlogachev 7970696
Refactor BlockValidationTestSuite
vladimirlogachev 637fc13
Add more test cases
vladimirlogachev c1c35db
Remove logs
vladimirlogachev 373f8a8
Add type signatures
vladimirlogachev 05d037b
Add block validation test for asset transfers (WIP)
vladimirlogachev 7709568
Fix amount in deposited transaction
vladimirlogachev aa43240
Fix successful case
vladimirlogachev 9bd134c
Adjust BaseBlockValidationSuite for asset transfers
vladimirlogachev 95aca82
Add more test cases
vladimirlogachev c1b0970
Add more test cases
vladimirlogachev a370d4d
Update src/main/scala/units/ELUpdater.scala
vladimirlogachev f9708c9
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev 619dabb
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev 45f20ee
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev 501cc78
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev a3d4cec
Reuse NetworkClient
phearnot b915893
Rename fields
vladimirlogachev ceccb4b
Update consensus-client-it/src/test/scala/units/TestNetworkClient.scala
vladimirlogachev 42e6c73
Refactor to use WavesNodeContainer in TestNetworkClient
vladimirlogachev 34bde39
Fix a bug in TestNetworkClient
vladimirlogachev 6877173
Extract BaseBlockValidationSuite
vladimirlogachev 8018bf6
Extract test suites to separate files
vladimirlogachev 50e71fd
Fix ELUpdater formatting
vladimirlogachev da9ac60
Use calculateRandao from ELUpdater
vladimirlogachev 26c6425
Remove `correctedTime` from BaseBlockValidationSuite
vladimirlogachev ace6fd2
Move getLastWithdrawalIndex to EngineApiClient extension
vladimirlogachev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
299 changes: 299 additions & 0 deletions
299
consensus-client-it/src/test/scala/units/BaseBlockValidationSuite.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,299 @@ | ||
| package units | ||
|
|
||
| import com.wavesplatform.* | ||
| import com.wavesplatform.account.* | ||
| import com.wavesplatform.common.state.ByteStr | ||
| import com.wavesplatform.common.utils.EitherExt2.explicitGet | ||
| import com.wavesplatform.crypto.Keccak256 | ||
| import com.wavesplatform.state.{Height, IntegerDataEntry} | ||
| import com.wavesplatform.transaction.Asset.IssuedAsset | ||
| import com.wavesplatform.transaction.smart.InvokeScriptTransaction | ||
| import com.wavesplatform.transaction.{Asset, TxHelpers} | ||
| import monix.execution.atomic.AtomicInt | ||
| import org.web3j.protocol.core.DefaultBlockParameterName | ||
| import org.web3j.tx.RawTransactionManager | ||
| import org.web3j.tx.gas.DefaultGasProvider | ||
| import play.api.libs.json.* | ||
| import units.BlockHash | ||
| import units.client.engine.model.Withdrawal.WithdrawalIndex | ||
| import units.client.engine.model.{EcBlock, Withdrawal} | ||
| import units.docker.EcContainer | ||
| import units.el.* | ||
| import units.eth.{EmptyL2Block, EthAddress, EthereumConstants} | ||
| import units.util.{BlockToPayloadMapper, HexBytesConverter} | ||
|
|
||
| import scala.annotation.tailrec | ||
| import scala.concurrent.duration.DurationInt | ||
| import scala.jdk.OptionConverters.RichOptional | ||
|
|
||
| trait BaseBlockValidationSuite extends BaseDockerTestSuite { | ||
| protected val setupMiner: SeedKeyPair = miner11Account // Leaves after setting up the contracts | ||
| protected val actingMiner: SeedKeyPair = miner12Account | ||
| protected val actingMinerRewardAddress: EthAddress = miner12RewardAddress | ||
|
|
||
| // Note: additional miners are needed to avoid the actingMiner having majority of the stake | ||
| protected val additionalMiner1: SeedKeyPair = miner21Account | ||
| protected val additionalMiner1RewardAddress: EthAddress = miner21RewardAddress | ||
| protected val additionalMiner2: SeedKeyPair = miner31Account | ||
| protected val additionalMiner2RewardAddress: EthAddress = miner31RewardAddress | ||
|
|
||
| // transfers | ||
| protected val clSender: SeedKeyPair = clRichAccount1 | ||
| protected val elRecipient: EthAddress = elRichAddress1 | ||
|
|
||
| // native transfers | ||
| protected val userNativeTokenAmount = 1 | ||
| protected val clNativeTokenAmount: Long = UnitsConvert.toUnitsInWaves(userNativeTokenAmount) | ||
| protected val elNativeTokenAmount: BigInt = UnitsConvert.toWei(userNativeTokenAmount) | ||
|
|
||
| // asset transfers | ||
| protected val gasProvider = new DefaultGasProvider | ||
| protected lazy val txnManager = new RawTransactionManager(ec1.web3j, elRichAccount1, EcContainer.ChainId, 20, 2000) | ||
| protected lazy val terc20 = new Erc20Client(ec1.web3j, TErc20Address, txnManager, gasProvider) | ||
| protected val issueAssetDecimals: Byte = 8.toByte | ||
| protected lazy val issueAsset: IssuedAsset = chainContract.getRegisteredAsset(1) match { | ||
| case ia: IssuedAsset => ia | ||
| case _ => fail("Expected issued asset") | ||
| } | ||
| protected val userAssetTokenAmount = 1 | ||
| protected val clAssetTokenAmount: Long = UnitsConvert.toWavesAtomic(userAssetTokenAmount, issueAssetDecimals) | ||
| protected val elAssetTokenAmount: BigInt = UnitsConvert.toAtomic(userAssetTokenAmount, TErc20Decimals) | ||
|
|
||
| private def correctedTime(): Long = { | ||
| val ntpTimestamp = System.currentTimeMillis() | ||
| val nanoTime = System.nanoTime() | ||
| val timestamp = ntpTimestamp | ||
| val offset = (System.nanoTime() - nanoTime) / 1000000 | ||
| timestamp + offset | ||
| } | ||
|
|
||
| private def calculateRandao(hitSource: ByteStr, parentHash: BlockHash): String = { | ||
| val msg = hitSource.arr ++ HexBytesConverter.toBytes(parentHash) | ||
| HexBytesConverter.toHex(crypto.secureHash(msg)) | ||
| } | ||
|
|
||
| @tailrec | ||
| private def getLastWithdrawalIndex(hash: BlockHash): JobResult[WithdrawalIndex] = | ||
vsuharnikov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ec1.engineApi.getBlockByHash(hash) match { | ||
| case Left(e) => Left(e) | ||
| case Right(None) => Left(ClientError(s"Can't find $hash block on EC during withdrawal search")) | ||
| case Right(Some(ecBlock)) => | ||
| ecBlock.withdrawals.lastOption match { | ||
| case Some(lastWithdrawal) => Right(lastWithdrawal.index) | ||
| case None => | ||
| if (ecBlock.height == 0) Right(-1L) | ||
| else getLastWithdrawalIndex(ecBlock.parentHash) | ||
| } | ||
| } | ||
|
|
||
| protected final def mkRewardWithdrawal(elParentBlock: EcBlock): Withdrawal = { | ||
| val chainContractOptions = chainContract.getOptions | ||
|
|
||
| val elWithdrawalIndexBefore = (elParentBlock.withdrawals.lastOption.map(_.index) match { | ||
| case Some(r) => Right(r) | ||
| case None => | ||
| if (elParentBlock.height - 1 <= EthereumConstants.GenesisBlockHeight) Right(-1L) | ||
| else getLastWithdrawalIndex(elParentBlock.parentHash) | ||
| }).explicitGet() | ||
| Withdrawal(elWithdrawalIndexBefore + 1, elParentBlock.minerRewardL2Address, chainContractOptions.miningReward) | ||
| } | ||
|
|
||
| protected final def mkSimulatedBlock( | ||
| elParentBlock: EcBlock, | ||
| withdrawals: Seq[Withdrawal], | ||
| depositedTransactions: Seq[DepositedTransaction] | ||
| ): (JsObject, String, ByteStr) = { | ||
|
|
||
| step("Building a simulated block") | ||
| val feeRecipient = actingMinerRewardAddress | ||
|
|
||
| val currentUnixTs = correctedTime() / 1000 | ||
| val blockDelay = 6 | ||
| val nextBlockUnixTs = (elParentBlock.timestamp + blockDelay).max(currentUnixTs) | ||
|
|
||
| val currentEpochHeader = waves1.api.blockHeader(waves1.api.height()).value | ||
| val hitSource = ByteStr.decodeBase58(currentEpochHeader.VRF).get | ||
| val prevRandao = calculateRandao(hitSource, elParentBlock.hash) | ||
|
|
||
| val txHashes = depositedTransactions.map(t => HexBytesConverter.toHex(Keccak256.hash(HexBytesConverter.toBytes(t.toHex)))).mkString(", ") | ||
| log.debug(s"Deposited transactions hashes: $txHashes") | ||
|
|
||
| val simulatedBlock: JsObject = ec1.engineApi | ||
| .simulate( | ||
| EmptyL2Block.mkSimulateCall(elParentBlock, feeRecipient, nextBlockUnixTs, prevRandao, withdrawals, depositedTransactions), | ||
| elParentBlock.hash | ||
| ) | ||
| .explicitGet() | ||
| .head | ||
|
|
||
| val payload = BlockToPayloadMapper.toPayloadJson( | ||
| simulatedBlock, | ||
| Json.obj( | ||
| "transactions" -> depositedTransactions.map(_.toHex), | ||
| "withdrawals" -> Json.toJson(withdrawals) | ||
| ) | ||
| ) | ||
|
|
||
| val simulatedBlockHash: String = (simulatedBlock \ "hash").as[String] | ||
|
|
||
| (payload, simulatedBlockHash, hitSource) | ||
| } | ||
|
|
||
| protected def deployContractsAndActivateTransferFeatures(): Unit = { | ||
| deploySolidityContracts() | ||
|
|
||
| step("Enable token transfers") | ||
| val activationEpoch = waves1.api.height() + 1 | ||
| waves1.api.broadcastAndWait( | ||
| ChainContract.enableTokenTransfersWithWaves( | ||
| StandardBridgeAddress, | ||
| WWavesAddress, | ||
| activationEpoch = activationEpoch | ||
| ) | ||
| ) | ||
|
|
||
| step("Set strict C2E transfers feature activation epoch") | ||
| waves1.api.broadcastAndWait( | ||
| TxHelpers.dataEntry( | ||
| chainContractAccount, | ||
| IntegerDataEntry("strictC2ETransfersActivationEpoch", activationEpoch) | ||
| ) | ||
| ) | ||
|
|
||
| step("Wait for features activation") | ||
| waves1.api.waitForHeight(activationEpoch) | ||
| } | ||
|
|
||
| protected def transferNativeTokenToClSender(): Unit = { | ||
| step("Prepare: issue tokens on chain contract and transfer to a user") | ||
| waves1.api.broadcastAndWait( | ||
| TxHelpers.reissue( | ||
| asset = chainContract.nativeTokenId, | ||
| sender = chainContractAccount, | ||
| amount = clNativeTokenAmount | ||
| ) | ||
| ) | ||
| waves1.api.broadcastAndWait( | ||
| TxHelpers.transfer( | ||
| from = chainContractAccount, | ||
| to = clSender.toAddress, | ||
| amount = clNativeTokenAmount, | ||
| asset = chainContract.nativeTokenId | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| protected def transferAssetTokenToClSender(): Unit = { | ||
| step("Register asset") | ||
| waves1.api.broadcastAndWait(ChainContract.issueAndRegister(TErc20Address, TErc20Decimals, "TERC20", "Test ERC20 token", issueAssetDecimals)) | ||
|
|
||
| eventually { | ||
| standardBridge.isRegistered(TErc20Address, ignoreExceptions = true) shouldBe true | ||
| } | ||
|
|
||
| step("Transfer asset from EL to CL") | ||
|
|
||
| val currNonce = | ||
| AtomicInt(ec1.web3j.ethGetTransactionCount(elRichAddress1.hex, DefaultBlockParameterName.PENDING).send().getTransactionCount.intValueExact()) | ||
| def nextNonce: Int = currNonce.getAndIncrement() | ||
|
|
||
| waitFor(terc20.sendApprove(StandardBridgeAddress, elAssetTokenAmount, nextNonce)) | ||
|
|
||
| val e2cIssuedTxn = standardBridge.sendBridgeErc20(elRichAccount1, TErc20Address, clSender.toAddress, elAssetTokenAmount, nextNonce) | ||
|
|
||
| chainContract.waitForEpoch(waves1.api.height() + 1) | ||
| val e2cReceipt = | ||
| eventually { | ||
| val hash = e2cIssuedTxn.getTransactionHash | ||
| withClue(s"$hash: ") { | ||
| ec1.web3j.ethGetTransactionReceipt(hash).send().getTransactionReceipt.toScala.value | ||
| } | ||
| } | ||
|
|
||
| val e2cBlockHash = BlockHash(e2cReceipt.getBlockHash) | ||
|
|
||
| val e2cLogsInBlock = ec1.engineApi | ||
| .getLogs(e2cBlockHash, List(NativeBridgeAddress, StandardBridgeAddress), Nil) | ||
| .explicitGet() | ||
| .filter(_.topics.intersect(E2CTopics).nonEmpty) | ||
|
|
||
| val e2cBlockConfirmationHeight = eventually { | ||
| chainContract.getBlock(e2cBlockHash).value.height | ||
| } | ||
|
|
||
| step(s"Wait for block $e2cBlockHash ($e2cBlockConfirmationHeight) finalization") | ||
| eventually { | ||
| val currFinalizedHeight = chainContract.getFinalizedBlock.height | ||
| step(s"Current finalized height: $currFinalizedHeight") | ||
| currFinalizedHeight should be >= e2cBlockConfirmationHeight | ||
| } | ||
|
|
||
| step("Broadcast withdrawAsset transactions") | ||
| waves1.api.broadcastAndWait( | ||
| ChainContract.withdrawAsset( | ||
| sender = clSender, | ||
| blockHash = e2cBlockHash, | ||
| merkleProof = BridgeMerkleTree.mkTransferProofs(e2cLogsInBlock, 0).explicitGet().reverse, | ||
| transferIndexInBlock = 0, | ||
| amount = UnitsConvert.toWavesAtomic(userAssetTokenAmount, issueAssetDecimals), | ||
| asset = issueAsset | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| protected def leaveSetupMinerAndJoinOthers(): Unit = { | ||
| log.debug(s"setupMiner: ${setupMiner.toAddress}") | ||
| log.debug(s"actingMiner: ${actingMiner.toAddress}") | ||
| log.debug(s"additionalMiner1: ${additionalMiner1.toAddress}") | ||
| log.debug(s"additionalMiner2: ${additionalMiner2.toAddress}") | ||
|
|
||
| step(s"additionalMiner1 join") | ||
| waves1.api.broadcastAndWait( | ||
| ChainContract.join( | ||
| minerAccount = additionalMiner1, | ||
| elRewardAddress = additionalMiner1RewardAddress | ||
| ) | ||
| ) | ||
|
|
||
| step(s"Wait additionalMiner1 epoch") | ||
| chainContract.waitForMinerEpoch(additionalMiner1) | ||
|
|
||
| step(s"setupMiner leave") | ||
| eventually(interval(500 millis)) { | ||
| waves1.api.broadcastAndWait(ChainContract.leave(setupMiner)) | ||
| } | ||
|
|
||
| step(s"additionalMiner2 join") | ||
| waves1.api.broadcastAndWait( | ||
| ChainContract.join( | ||
| minerAccount = additionalMiner2, | ||
| elRewardAddress = additionalMiner2RewardAddress | ||
| ) | ||
| ) | ||
|
|
||
| step(s"actingMiner join") | ||
| waves1.api.broadcastAndWait( | ||
| ChainContract.join( | ||
| minerAccount = actingMiner, | ||
| elRewardAddress = actingMinerRewardAddress | ||
| ) | ||
| ) | ||
|
|
||
| step(s"Wait actingMiner epoch") | ||
| chainContract.waitForMinerEpoch(actingMiner) | ||
| } | ||
|
|
||
| protected def setupForNativeTokenTransfer(): Unit = { | ||
| super.beforeAll() | ||
| deployContractsAndActivateTransferFeatures() | ||
| transferNativeTokenToClSender() | ||
| leaveSetupMinerAndJoinOthers() | ||
| } | ||
|
|
||
| protected def setupForAssetTokenTransfer(): Unit = { | ||
| super.beforeAll() | ||
| deployContractsAndActivateTransferFeatures() | ||
| transferAssetTokenToClSender() | ||
| leaveSetupMinerAndJoinOthers() | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.