Skip to content
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
ab9939f
Rebuild on main
vladimirlogachev Jul 8, 2025
26244d2
Fix MultipleTransfersViaDepositsTestSuite
vladimirlogachev Jul 8, 2025
15cf88a
Combine two features into a single feature
vladimirlogachev Jul 8, 2025
d0f8a6a
Fix "Enough C2E transfers" test case
vladimirlogachev Jul 8, 2025
92ec9d2
Add condition for the `from` field vaidation in validateC2EAssetTransfer
vladimirlogachev Jul 8, 2025
c8383b8
Refactor `requireTransfer` to use extractors
vladimirlogachev Jul 8, 2025
4635bc4
Fix unit test
vladimirlogachev Jul 9, 2025
28211b6
Clean up
vladimirlogachev Jul 9, 2025
92ef93f
Remove limitation on native transfers number
vladimirlogachev Jul 9, 2025
2ebbdba
Update comments
vladimirlogachev Jul 9, 2025
75481bb
Add more logs
vladimirlogachev Jul 10, 2025
6b84915
Reorder `depositedTransactions` in `updateHeadAndStartBuildingPayload`
vladimirlogachev Jul 10, 2025
22c6814
Fix a bug with `MaxWithdrawals` usage
vladimirlogachev Jul 14, 2025
2961a88
Add ManyTransfersTestSuite
vladimirlogachev Jul 15, 2025
d286e36
Clean up tests
vladimirlogachev Jul 17, 2025
e39b09e
Apply fix for transfers order
vladimirlogachev Jul 17, 2025
4420057
Add more cases to C2ETransfersTestSuite
vladimirlogachev Jul 17, 2025
7568708
Add one more test case
vladimirlogachev Jul 17, 2025
ed9f26f
Add more test cases
vladimirlogachev Jul 17, 2025
171d430
Fix strict transfers unit test
vladimirlogachev Jul 17, 2025
d80738b
Update consensus-client-it/src/test/scala/units/ManyTransfersTestSuit…
vladimirlogachev Jul 18, 2025
aeeb38f
Update consensus-client-it/src/test/scala/units/ManyTransfersTestSuit…
vladimirlogachev Jul 18, 2025
4d3870d
Update src/main/scala/units/client/contract/ChainContractClient.scala
vladimirlogachev Jul 18, 2025
6bf5293
Fix according to the review
vladimirlogachev Jul 18, 2025
8ceebd7
Simplify `updateHeadAndStartBuildingPayload`
vladimirlogachev Jul 18, 2025
c0a0c95
SImplify ManyTransfersTestSuite
vladimirlogachev Jul 18, 2025
6f6e79a
Fix formatting
vladimirlogachev Jul 18, 2025
0a410a2
Simplify `depositedTransactions` calculation
vladimirlogachev Jul 18, 2025
d261096
Fix error message to match the validation
vladimirlogachev Jul 25, 2025
fc250ca
Merge branch 'main' into native-token-transfers-deposits-2
vladimirlogachev Aug 7, 2025
5ea1249
Merge branch 'main' into native-token-transfers-deposits-2
vladimirlogachev Aug 11, 2025
af61bb2
Merge branch 'main' into native-token-transfers-deposits-2
vladimirlogachev Aug 14, 2025
53b092a
Adjust the check
vladimirlogachev Aug 14, 2025
951a09e
Overrride `equals` for DepositedTransaction, add unit test
vladimirlogachev Aug 15, 2025
63d252f
Fix validation for strict transfers enabled
vladimirlogachev Aug 25, 2025
9e8838a
Remove logs
vladimirlogachev Aug 25, 2025
a4ddabc
Fix a bug in validation
vladimirlogachev Aug 25, 2025
ff1fe63
Fix a bug with indices, unify range creation
vladimirlogachev Aug 25, 2025
fa810e1
Extract `prepareTransactions` method
vladimirlogachev Aug 26, 2025
2d2fb5c
Merge remote-tracking branch 'origin/main' into native-token-transfer…
vladimirlogachev Sep 11, 2025
5ab6b87
Add ec2 to BaseDockerTestSuite
vladimirlogachev Sep 11, 2025
91feaa0
Add BlockValidationTestSuite (WIP)
vladimirlogachev Sep 24, 2025
b1aa2b9
Remove ec2
vladimirlogachev Sep 24, 2025
0b9b5c1
Add `enableMining` flag to WavesNodeContainer
vladimirlogachev Sep 24, 2025
dd2f92b
Override `waves1` in BlockValidationTestSuite
vladimirlogachev Sep 24, 2025
32a9a59
Add 2 more EL miners
vladimirlogachev Sep 25, 2025
e7eb0dc
WIP
vladimirlogachev Oct 1, 2025
6bea7eb
Register the EL block on chain contract
vladimirlogachev Oct 2, 2025
f915bd2
Fix vrf calculation
vladimirlogachev Oct 2, 2025
0f9e562
Fix assertions
vladimirlogachev Oct 2, 2025
52fe888
Add assertion: Block exists on EC1
vladimirlogachev Oct 2, 2025
fcdf650
Update comments
vladimirlogachev Oct 2, 2025
db8436c
Remove block validation test cases from C2ETransfersTestSuite
vladimirlogachev Oct 2, 2025
dc5191c
Refactor to use `Handshake.encode` method
vladimirlogachev Oct 2, 2025
fe60889
Refactor to use netty.bootstrap
vladimirlogachev Oct 2, 2025
dd0e912
Simplify BlockValidationTestSuite
vladimirlogachev Oct 2, 2025
20a9cb2
Simplify TestNetworkClient
vladimirlogachev Oct 2, 2025
8c0a3ce
Simplify TestNetworkClient
vladimirlogachev Oct 2, 2025
815add9
Adjust wording
vladimirlogachev Oct 8, 2025
74ca44b
Add successful case to BlockValidationTestSuite
vladimirlogachev Oct 8, 2025
8c0c802
WIP
vladimirlogachev Oct 13, 2025
bface09
Add log
vladimirlogachev Oct 13, 2025
ccfc9da
WIP
vladimirlogachev Oct 13, 2025
debad71
Fix successful case
vladimirlogachev Oct 14, 2025
da42eb2
SImplify BlockValidationTestSuite
vladimirlogachev Oct 14, 2025
fc03b1e
Speed up test
vladimirlogachev Oct 14, 2025
fbd475c
Extract BaseBlockValidationSuite
vladimirlogachev Oct 14, 2025
7970696
Refactor BlockValidationTestSuite
vladimirlogachev Oct 14, 2025
637fc13
Add more test cases
vladimirlogachev Oct 14, 2025
c1c35db
Remove logs
vladimirlogachev Oct 15, 2025
373f8a8
Add type signatures
vladimirlogachev Oct 15, 2025
05d037b
Add block validation test for asset transfers (WIP)
vladimirlogachev Oct 15, 2025
7709568
Fix amount in deposited transaction
vladimirlogachev Oct 15, 2025
aa43240
Fix successful case
vladimirlogachev Oct 15, 2025
9bd134c
Adjust BaseBlockValidationSuite for asset transfers
vladimirlogachev Oct 15, 2025
95aca82
Add more test cases
vladimirlogachev Oct 15, 2025
c1b0970
Add more test cases
vladimirlogachev Oct 15, 2025
a370d4d
Update src/main/scala/units/ELUpdater.scala
vladimirlogachev Oct 16, 2025
f9708c9
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev Oct 16, 2025
619dabb
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev Oct 16, 2025
45f20ee
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev Oct 16, 2025
501cc78
Update consensus-client-it/src/test/scala/units/docker/WavesNodeConta…
vladimirlogachev Oct 16, 2025
a3d4cec
Reuse NetworkClient
phearnot Oct 16, 2025
b915893
Rename fields
vladimirlogachev Oct 16, 2025
ceccb4b
Update consensus-client-it/src/test/scala/units/TestNetworkClient.scala
vladimirlogachev Oct 16, 2025
42e6c73
Refactor to use WavesNodeContainer in TestNetworkClient
vladimirlogachev Oct 16, 2025
34bde39
Fix a bug in TestNetworkClient
vladimirlogachev Oct 16, 2025
6877173
Extract BaseBlockValidationSuite
vladimirlogachev Oct 16, 2025
8018bf6
Extract test suites to separate files
vladimirlogachev Oct 16, 2025
50e71fd
Fix ELUpdater formatting
vladimirlogachev Oct 16, 2025
da9ac60
Use calculateRandao from ELUpdater
vladimirlogachev Oct 17, 2025
26c6425
Remove `correctedTime` from BaseBlockValidationSuite
vladimirlogachev Oct 17, 2025
ace6fd2
Move getLastWithdrawalIndex to EngineApiClient extension
vladimirlogachev Oct 17, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import com.wavesplatform.api.http.`X-Api-Key`
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.state.DataEntry.Format
import com.wavesplatform.state.{DataEntry, EmptyDataEntry, Height}
import com.wavesplatform.transaction.Asset
import com.wavesplatform.transaction.Asset.IssuedAsset
import com.wavesplatform.transaction.Transaction
import com.wavesplatform.transaction.{Asset, Transaction}
import com.wavesplatform.utils.ScorexLogging
import org.scalatest.matchers.should.Matchers
import play.api.libs.json.*
Expand Down Expand Up @@ -159,12 +158,12 @@ class NodeHttpApi(apiUri: Uri, backend: SttpBackend[Identity, ?], apiKeyValue: S
}
}
}

def balance(address: Address, asset: Asset)(implicit loggingOptions: LoggingOptions = LoggingOptions()): Long = {
if (loggingOptions.logCall) log.debug(s"${loggingOptions.prefix} balance($address, $asset)")
basicRequest
.get(asset match {
case Asset.Waves => uri"$apiUri/addresses/balance/$address"
case Asset.Waves => uri"$apiUri/addresses/balance/$address"
case IssuedAsset(id) => uri"$apiUri/assets/balance/$address/$id"
})
.response(asJson[BalanceResponse])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package units

import com.wavesplatform.state.IntegerDataEntry
import com.wavesplatform.transaction.TxHelpers
import org.web3j.protocol.core.DefaultBlockParameterName
import units.eth.EthAddress

class C2ENativeTokenTransfersViaDepositsTestSuite extends BaseDockerTestSuite {
private val clSender = clRichAccount1
private val elReceiver = elRichAccount1
private val elReceiverAddress = EthAddress.unsafeFrom(elReceiver.getAddress)

private val userAmount = 1
private val clAmount = UnitsConvert.toUnitsInWaves(userAmount)
private val elAmount = UnitsConvert.toWei(userAmount)

"C2E native token transfers via deposited transactions" in {
def getElBalance: BigInt = ec1.web3j.ethGetBalance(elReceiverAddress.hex, DefaultBlockParameterName.PENDING).send().getBalance
val elBalanceBefore = getElBalance
step(s"elBalanceBefore: $elBalanceBefore")

waves1.api.broadcastAndWait(
ChainContract.transfer(
sender = clSender,
destElAddress = elReceiverAddress,
asset = chainContract.nativeTokenId,
amount = clAmount
)
)

withClue("Expected amount: ") {
eventually {
val elBalanceAfter = getElBalance
step(s"elBalanceAfter: $elBalanceAfter")
val expectedBalanceAfter = elBalanceBefore + elAmount
UnitsConvert.toUser(elBalanceAfter, NativeTokenElDecimals) shouldBe UnitsConvert.toUser(expectedBalanceAfter, NativeTokenElDecimals)
}
}
}

override def beforeAll(): Unit = {

super.beforeAll()
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)

step("Prepare: issue tokens on chain contract and transfer to a user")
waves1.api.broadcastAndWait(
TxHelpers.reissue(
asset = chainContract.nativeTokenId,
sender = chainContractAccount,
amount = clAmount
)
)
waves1.api.broadcastAndWait(
TxHelpers.transfer(
from = chainContractAccount,
to = clSender.toAddress,
amount = clAmount,
asset = chainContract.nativeTokenId
)
)
}
}
199 changes: 199 additions & 0 deletions consensus-client-it/src/test/scala/units/ManyTransfersTestSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package units

import com.wavesplatform.common.utils.EitherExt2.explicitGet
import com.wavesplatform.state.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.protocol.core.methods.response.TransactionReceipt
import org.web3j.tx.RawTransactionManager
import org.web3j.tx.gas.DefaultGasProvider
import units.docker.EcContainer
import units.el.{BridgeMerkleTree, E2CTopics, Erc20Client}
import units.eth.EthAddress

import scala.jdk.OptionConverters.RichOptional

class ManyTransfersTestSuite extends BaseDockerTestSuite {
private val clRecipient = clRichAccount1
private val elSender = elRichAccount1
private val elSenderAddress = elRichAddress1

private val issueAssetDecimals = 8.toByte
private lazy val issueAsset = chainContract.getRegisteredAsset(1) // 0 is WAVES

private val userAmount = BigDecimal("1")
private val eachTransferTypeCount = 20

private val gasProvider = new DefaultGasProvider
private lazy val txnManager = new RawTransactionManager(ec1.web3j, elSender, EcContainer.ChainId, 20, 2000)
private lazy val terc20 = new Erc20Client(ec1.web3j, TErc20Address, txnManager, gasProvider)

"Many native + many asset transfers" in {

val currNonce =
AtomicInt(ec1.web3j.ethGetTransactionCount(elSenderAddress.hex, DefaultBlockParameterName.PENDING).send().getTransactionCount.intValueExact())
def nextNonce: Int = currNonce.getAndIncrement()

val nativeE2CAmount = UnitsConvert.toAtomic(userAmount * eachTransferTypeCount, NativeTokenElDecimals)
val issuedE2CAmount = UnitsConvert.toAtomic(userAmount * eachTransferTypeCount, TErc20Decimals)

step("Send allowances")
waitFor(terc20.sendApprove(StandardBridgeAddress, issuedE2CAmount, nextNonce))

step("Initiate E2C transfers")
val e2cNativeTxn = nativeBridge.sendSendNative(elSender, clRecipient.toAddress, nativeE2CAmount, nextNonce)
val e2cIssuedTxn = standardBridge.sendBridgeErc20(elSender, TErc20Address, clRecipient.toAddress, issuedE2CAmount, nextNonce)

chainContract.waitForEpoch(waves1.api.height() + 1) // Bypass rollbacks
val e2cReceipts = List(e2cNativeTxn, e2cIssuedTxn).map { txn =>
eventually {
val hash = txn.getTransactionHash
withClue(s"$hash: ") {
ec1.web3j.ethGetTransactionReceipt(hash).send().getTransactionReceipt.toScala.value
}
}
}

withClue("E2C should be on same height, can't continue the test: ") {
val e2cHeights = e2cReceipts.map(_.getBlockNumber.intValueExact()).toSet
e2cHeights.size shouldBe 1
}

val e2cBlockHash = BlockHash(e2cReceipts.head.getBlockHash)
log.debug(s"Block with e2c transfers: $e2cBlockHash")

val e2cLogsInBlock = ec1.engineApi
.getLogs(e2cBlockHash, List(NativeBridgeAddress, StandardBridgeAddress), Nil)
.explicitGet()
.filter(_.topics.intersect(E2CTopics).nonEmpty)

withClue("We have logs for all transactions: ") {
e2cLogsInBlock.size shouldBe e2cReceipts.size
}

step(s"Wait block $e2cBlockHash with transfers on contract")
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")
val recipientAssetBalanceBefore = clRecipientAssetBalance
val recipientNativeTokenBalanceBefore = clRecipientNativeTokenBalance

def mkE2CWithdrawTxn(transferIndex: Int, asset: Asset, amount: BigDecimal, decimals: Byte): InvokeScriptTransaction = ChainContract.withdrawAsset(
sender = clRecipient,
blockHash = e2cBlockHash,
merkleProof = BridgeMerkleTree.mkTransferProofs(e2cLogsInBlock, transferIndex).explicitGet().reverse,
transferIndexInBlock = transferIndex,
amount = UnitsConvert.toWavesAtomic(amount, decimals),
asset = asset
)

val e2cWithdrawTxns = List(
mkE2CWithdrawTxn(0, chainContract.nativeTokenId, userAmount * eachTransferTypeCount, NativeTokenClDecimals),
mkE2CWithdrawTxn(1, issueAsset, userAmount * eachTransferTypeCount, issueAssetDecimals)
)

e2cWithdrawTxns.foreach(waves1.api.broadcast)
e2cWithdrawTxns.foreach(txn => waves1.api.waitForSucceeded(txn.id()))

withClue("Assets received after E2C: ") {
withClue("Native token: ") {
val balanceAfter = clRecipientNativeTokenBalance
balanceAfter shouldBe (recipientNativeTokenBalanceBefore + UnitsConvert.toWavesAtomic(
userAmount * eachTransferTypeCount,
NativeTokenClDecimals
))
}

withClue("Issued asset: ") {
val balanceAfter = clRecipientAssetBalance
balanceAfter shouldBe (recipientAssetBalanceBefore + UnitsConvert.toWavesAtomic(userAmount * eachTransferTypeCount, issueAssetDecimals))
}
}

step("Initiate C2E transfers")
val c2eRecipientAddress = EthAddress.unsafeFrom("0xAAAA00000000000000000000000000000000AAAA")

def mkC2ETransferTxn(asset: Asset, decimals: Byte): InvokeScriptTransaction = ChainContract.transfer(
clRecipient,
c2eRecipientAddress,
asset,
UnitsConvert.toWavesAtomic(userAmount, decimals)
)

val c2eTransferTxns = for {
_ <- 1 to eachTransferTypeCount
tx <- Seq(
mkC2ETransferTxn(issueAsset, issueAssetDecimals),
mkC2ETransferTxn(chainContract.nativeTokenId, NativeTokenClDecimals)
)
} yield tx

c2eTransferTxns.foreach(waves1.api.broadcast)
val c2eTransferTxnResults = c2eTransferTxns.map(txn => waves1.api.waitForSucceeded(txn.id()))

withClue("C2E should be on same height, can't continue the test: ") {
val c2eHeights = c2eTransferTxnResults.map(_.height).toSet
c2eHeights.size shouldBe 1
}

withClue("Assets received after C2E: ") {
eventually {
withClue("Native token: ") {
val balanceAfter = ec1.web3j.ethGetBalance(c2eRecipientAddress.hex, DefaultBlockParameterName.PENDING).send().getBalance
BigInt(balanceAfter) shouldBe (nativeE2CAmount)
}

withClue("Issued asset: ") {
terc20.getBalance(c2eRecipientAddress) shouldBe issuedE2CAmount
}
}
}
}

private def clRecipientAssetBalance: Long = waves1.api.balance(clRecipient.toAddress, issueAsset)
private def clRecipientNativeTokenBalance: Long = waves1.api.balance(clRecipient.toAddress, chainContract.nativeTokenId)

override def beforeAll(): Unit = {
super.beforeAll()
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)

step("Register asset")
waves1.api.broadcastAndWait(ChainContract.issueAndRegister(TErc20Address, TErc20Decimals, "TERC20", "Test ERC20 token", issueAssetDecimals))
eventually {
standardBridge.isRegistered(TErc20Address, ignoreExceptions = true) shouldBe true
}
}
}
Loading