Skip to content

Commit

Permalink
added integration test to send toncoins to 84 destinations
Browse files Browse the repository at this point in the history
  • Loading branch information
neodix42 committed Nov 24, 2022
1 parent 4149260 commit d2e6c34
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 69 deletions.
7 changes: 5 additions & 2 deletions cell/src/main/java/org/ton/java/cell/Cell.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.nio.file.Paths;
import java.util.*;

import static java.util.Objects.nonNull;

public class Cell {
private static final byte[] reachBocMagicPrefix = Utils.hexToBytes("B5EE9C72");
private static final byte[] leanBocMagicPrefix = Utils.hexToBytes("68ff65f3");
Expand Down Expand Up @@ -463,6 +465,7 @@ void moveToTheEnd(HashMap<String, Integer> indexHashmap, List<TopologicalOrderAr
TopologicalOrderArray data = topologicalOrderArray.remove(targetIndex);

topologicalOrderArray.add(data);

for (Cell subCell : data.cell.refs) {
moveToTheEnd(indexHashmap, topologicalOrderArray, Utils.bytesToHex(subCell.hash()));
}
Expand All @@ -486,8 +489,8 @@ TreeWalkResult treeWalk(Cell cell, List<TopologicalOrderArray> topologicalOrderA
String cellHash = Utils.bytesToHex(cell.hash());
if (indexHashmap.containsKey(cellHash)) {
//if (cellHash in indexHashmap){ // Duplication cell
//it is possible that already seen cell is a children of more deep cell
if (parentHash != null) {
//it is possible that already seen cell is a child of more deep cell
if (nonNull(parentHash)) {
if (indexHashmap.get(parentHash) > indexHashmap.get(cellHash)) {
moveToTheEnd(indexHashmap, topologicalOrderArray, cellHash);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.ton.java.cell.TonHashMap;
import org.ton.java.smartcontract.types.Destination;
import org.ton.java.smartcontract.types.ExternalMessage;
import org.ton.java.smartcontract.types.HighloadConfig;
import org.ton.java.smartcontract.wallet.Contract;
import org.ton.java.smartcontract.wallet.Options;
import org.ton.java.smartcontract.wallet.WalletContract;
Expand All @@ -14,7 +15,6 @@
import org.ton.java.tonlib.types.TvmStackEntryNumber;

import java.math.BigInteger;
import java.time.Instant;

public class HighloadWallet implements WalletContract {

Expand Down Expand Up @@ -70,23 +70,17 @@ public Cell createSigningMessage(long seqno) {
CellBuilder message = CellBuilder.beginCell();
message.storeUint(BigInteger.valueOf(getOptions().walletId), 32);

BigInteger i = BigInteger.valueOf((long) Math.pow(Instant.now().getEpochSecond() + 5 * 60L, 32))
.add(new BigInteger(String.valueOf(Instant.now().getEpochSecond())));
System.out.println("queryId --------------------------------- " + i);
message.storeUint(i, 64);
message.storeUint(getOptions().getHighloadQueryId(), 64);
message.storeBit(false);

return message.endCell();
}

public Cell createSigningMessageInternal() {
public Cell createSigningMessageInternal(HighloadConfig highloadConfig) {
CellBuilder message = CellBuilder.beginCell();
message.storeUint(BigInteger.valueOf(getOptions().walletId), 32);

BigInteger i = BigInteger.valueOf((long) Math.pow(Instant.now().getEpochSecond() + 5 * 60L, 32))
.add(new BigInteger(String.valueOf(Instant.now().getEpochSecond())));
System.out.println("queryId --------------------------------- " + i);
message.storeUint(i, 64);
message.storeUint(highloadConfig.getQueryId(), 64);

message.storeBit(true);

Expand All @@ -106,22 +100,29 @@ public String getPublicKey(Tonlib tonlib) {
return publicKeyNumber.getNumber().toString(16);
}

public void sendTonCoins(Tonlib tonlib, byte[] secretKey) {
/**
* Sends to up to 84 destinations
*
* @param tonlib Tonlib
* @param secretKey byte[]
* @param highloadConfig HighloadConfig
*/
public void sendTonCoins(Tonlib tonlib, byte[] secretKey, HighloadConfig highloadConfig) {

Cell signingMessageAll = createSigningMessageInternal();
signingMessageAll.refs.add(createDict());
Cell signingMessageAll = createSigningMessageInternal(highloadConfig);
signingMessageAll.refs.add(createDict(highloadConfig));

ExternalMessage msg = createExternalMessage(signingMessageAll, secretKey, 1, false);

tonlib.sendRawMessage(msg.message.toBocBase64(false));
}

private Cell createDict() {
private Cell createDict(HighloadConfig highloadConfig) {
int dictKeySize = 16;
TonHashMap dictDestinations = new TonHashMap(dictKeySize);

long i = 0; // key, index 16bit
for (Destination destination : getOptions().getHighloadConfig().getDestinations()) {
for (Destination destination : highloadConfig.getDestinations()) {

Cell orderHeader = Contract.createInternalMessageHeader(destination.getAddress(), destination.getAmount());
Cell order = Contract.createCommonMsgInfo(orderHeader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import lombok.Setter;
import lombok.ToString;

import java.math.BigInteger;
import java.util.List;

@Builder
@Getter
@Setter
@ToString
public class HighloadConfig {
// public List<Long> queryId;
public BigInteger queryId;
public List<Destination> destinations;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class Options {
public Cell code;
public long seqno;
public long queryId;
public BigInteger highloadQueryId;
public Object payload;
public int sendMode;
public Cell stateInit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
import org.ton.java.smartcontract.types.WalletVersion;
import org.ton.java.smartcontract.wallet.Options;
import org.ton.java.smartcontract.wallet.Wallet;
import org.ton.java.smartcontract.wallet.v3.WalletV3ContractR2;
import org.ton.java.tonlib.Tonlib;
import org.ton.java.tonlib.types.VerbosityLevel;
import org.ton.java.utils.Utils;

import java.math.BigInteger;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

Expand All @@ -27,7 +31,7 @@
public class TestWalletV2Highload {

@Test
public void testWalletV2Highload() throws InterruptedException {
public void testWalletV2HighloadSendTo10() throws InterruptedException {

Tonlib tonlib = Tonlib.builder()
.testnet(true)
Expand All @@ -39,56 +43,10 @@ public void testWalletV2Highload() throws InterruptedException {
Options options = Options.builder()
.publicKey(keyPair.getPublicKey())
.walletId(42L)
.highloadQueryId(BigInteger.valueOf((long) Math.pow(Instant.now().getEpochSecond() + 5 * 60L, 32))
.add(new BigInteger(String.valueOf(Instant.now().getEpochSecond()))))
.wc(0L)
.highloadConfig(HighloadConfig.builder()
.destinations(List.of(
Destination.builder()
.address(Address.of("EQAyjRKDnEpTBNfRHqYdnzGEQjdY4KG3gxgqiG3DpDY46u8G"))
.amount(Utils.toNano(1))
.mode((byte) 3)
.build(),
Destination.builder()
.address(Address.of("EQBrpstctZ5gF-VaaPswcWHe3JQijjNbtJVn5USXlZ-bAgO3"))
.amount(Utils.toNano(1))
.mode((byte) 3)
.build()
// Destination.builder()
// .address(Address.of("EQAaGHUHfkpWFGs428ETmym4vbvRNxCA1o4sTkwqigKjgf-_"))
// .amount(Utils.toNano(0.08))
// .build(),
// Destination.builder()
// .address(Address.of("EQAUEeWmzaf2An-MmNi1DRlFAU6ol_qTLP-_LlUnfgF-lA00"))
// .amount(Utils.toNano(0.6))
// .build(),
// Destination.builder()
// .address(Address.of("EQCwqNA5WhNTTQtl-QDlOlwcBDUS377Q4tRW69V82Q3LXvru"))
// .amount(Utils.toNano(0.2))
// .build(),
// Destination.builder()
// .address(Address.of("EQAQ93wokze84Loos4arP5aK7AlQFqbg1HDjEogsMCCbZyNo"))
// .amount(Utils.toNano(0.1))
// .build(),
// Destination.builder()
// .address(Address.of("EQALeq_z73heR4wMQRFKgA_fwkbZgxEf0ya0Kl6UjvpvG-A3"))
// .amount(Utils.toNano(0.15))
// .build(),
// Destination.builder()
// .address(Address.of("EQCP-ejxzoB6KJ6auhnsPrW1pW6gAZ8uHXnUSHuHGNpY1zJf"))
// .amount(Utils.toNano(0.42))
// .build()

//EQCkS2OnOOjeLV-LEEUmIPh-_in4pdFr1cScZG1Inft3qUea
//EQCZlgy61mcgYNXK0yiFHC9CxjoxxAFkwiUtzTONrk6_Qk6W
//EQAt_kmFWgz5BJbPExcmUVdfz_go05xGSGbyfPGNjtsNacHu
//EQDO8yw0Dbs1loUBMtoBtT-V-YMSlZdQ_FwZWmyeoN9KpanF
//EQAsEVrV7tQtgoqzpxqYDdX-KLmPxQkfqRbj8RYtAcWdSWJh
//EQBFSkhPfFmwwherHCWnrqryc9zTu0HkV23Ya5EzD-FZjEz-
//EQC1ZVhIHkxQ-IKGK3htrFV90CRBbEfayJC4bzmPoeernBau
//EQALy9XBBfJZ4rZzSkZM65LYglzJ5ORbGbvN16NjbcigXliy
//EQAGashg_KuPjp1CbmU1ic-YTTKTMsbzpDc9PoqoeSRr-Fdz
//EQDJxUadaFJoa_0K_twQHawyJkkyv9vlui_ZsUQRA1mw0HVO
))
.build())

.build();

Wallet wallet = new Wallet(WalletVersion.highload, options);
Expand All @@ -109,13 +67,138 @@ public void testWalletV2Highload() throws InterruptedException {

Utils.sleep(30, "deploying");

HighloadConfig highloadConfig = HighloadConfig.builder()
.queryId(BigInteger.valueOf((long) Math.pow(Instant.now().getEpochSecond() + 5 * 60L, 32)))
.destinations(List.of(
Destination.builder()
.address(Address.of("EQAyjRKDnEpTBNfRHqYdnzGEQjdY4KG3gxgqiG3DpDY46u8G"))
.amount(Utils.toNano(0.2))
.mode((byte) 3)
.build(),
Destination.builder()
.address(Address.of("EQBrpstctZ5gF-VaaPswcWHe3JQijjNbtJVn5USXlZ-bAgO3"))
.amount(Utils.toNano(0.1))
.mode((byte) 3)
.build(),
Destination.builder()
.address(Address.of("EQAaGHUHfkpWFGs428ETmym4vbvRNxCA1o4sTkwqigKjgf-_"))
.amount(Utils.toNano(0.3))
.build(),
Destination.builder()
.address(Address.of("EQAUEeWmzaf2An-MmNi1DRlFAU6ol_qTLP-_LlUnfgF-lA00"))
.amount(Utils.toNano(0.6))
.build(),
Destination.builder()
.address(Address.of("EQCwqNA5WhNTTQtl-QDlOlwcBDUS377Q4tRW69V82Q3LXvru"))
.amount(Utils.toNano(0.2))
.build(),
Destination.builder()
.address(Address.of("EQAQ93wokze84Loos4arP5aK7AlQFqbg1HDjEogsMCCbZyNo"))
.amount(Utils.toNano(0.1))
.build(),
Destination.builder()
.address(Address.of("EQALeq_z73heR4wMQRFKgA_fwkbZgxEf0ya0Kl6UjvpvG-A3"))
.amount(Utils.toNano(0.15))
.build(),
Destination.builder()
.address(Address.of("EQCP-ejxzoB6KJ6auhnsPrW1pW6gAZ8uHXnUSHuHGNpY1zJf"))
.amount(Utils.toNano(0.42))
.build()
,
Destination.builder()
.address(Address.of("EQCkS2OnOOjeLV-LEEUmIPh-_in4pdFr1cScZG1Inft3qUea"))
.amount(Utils.toNano(0.22))
.build()
,
Destination.builder()
.address(Address.of("EQCZlgy61mcgYNXK0yiFHC9CxjoxxAFkwiUtzTONrk6_Qk6W"))
.amount(Utils.toNano(0.33))
.build()
))
.build();

// transfer coins to multiple destination as specified in options
contract.sendTonCoins(tonlib, keyPair.getSecretKey());
contract.sendTonCoins(tonlib, keyPair.getSecretKey(), highloadConfig);

Utils.sleep(60, "sending to multiple destinations");

balance = new BigInteger(tonlib.getAccountState(Address.of(bounceableAddress)).getBalance());
log.info("new wallet {} balance: {}", contract.getName(), Utils.formatNanoValue(balance));
assertThat(balance.longValue()).isLessThan(Utils.toNano(3).longValue());
}

@Test
public void testWalletV2HighloadSendTo84() throws InterruptedException {

Tonlib tonlib = Tonlib.builder()
.verbosityLevel(VerbosityLevel.DEBUG)
.testnet(true)
.build();

TweetNaclFast.Signature.KeyPair keyPair = Utils.generateSignatureKeyPair();

Options options = Options.builder()
.publicKey(keyPair.getPublicKey())
.walletId(42L)
.highloadQueryId(BigInteger.valueOf((long) Math.pow(Instant.now().getEpochSecond() + 5 * 60L, 32))
.add(new BigInteger(String.valueOf(Instant.now().getEpochSecond()))))
.wc(0L)

.build();

Wallet wallet = new Wallet(WalletVersion.highload, options);
HighloadWallet contract = wallet.create();

String nonBounceableAddress = contract.getAddress().toString(true, true, false);
String bounceableAddress = contract.getAddress().toString(true, true, true);

log.info("non-bounceable address {}", nonBounceableAddress);
log.info(" bounceable address {}", bounceableAddress);

// top up new wallet using test-faucet-wallet
BigInteger balance = TestFaucet.topUpContract(tonlib, Address.of(nonBounceableAddress), Utils.toNano(7));
Utils.sleep(10, "topping up...");
log.info("new wallet {} balance: {}", contract.getName(), Utils.formatNanoValue(balance));

contract.deploy(tonlib, keyPair.getSecretKey());

Utils.sleep(30, "deploying");

// Sends to up to 84 destinations
List<Destination> destinations = generateTargetsWithSameAmountAndSendMode(84, keyPair.getPublicKey());

HighloadConfig highloadConfig = HighloadConfig.builder()
.queryId(BigInteger.valueOf((long) Math.pow(Instant.now().getEpochSecond() + 5 * 60L, 32)))
.destinations(destinations)
.build();

contract.sendTonCoins(tonlib, keyPair.getSecretKey(), highloadConfig);
}

private List<Destination> generateTargetsWithSameAmountAndSendMode(int count, byte[] publicKey) {

List<Destination> result = new ArrayList<>();

for (int i = 0; i < count; i++) {

Options options = Options.builder()
.walletId(new Random().nextLong() & 0xffffffffL)
.publicKey(publicKey)
.build();

Wallet wallet = new Wallet(WalletVersion.v3R2, options);
WalletV3ContractR2 contract = wallet.create();
Address dest = contract.getAddress();
double amount = 0.05;
log.info("send {} to {}", Utils.formatNanoValue(Utils.toNano(amount)), dest.toString(true, true, true));

Destination destination = Destination.builder()
.address(dest)
.amount(Utils.toNano(amount))
.build();

result.add(destination);
}
return result;
}
}

0 comments on commit d2e6c34

Please sign in to comment.