Skip to content

Commit

Permalink
add sendRawMessageWithConfirmation() to Tonlib; (works with tonlibjso…
Browse files Browse the repository at this point in the history
…n.so/dll from testnet)

   * Sends raw message, bag of cells encoded in base64, with deliver confirmation. After the message
   * has been sent to the network this method looks up specified account transactions and returns
   * true if message was found among them. Timeout 60 seconds.
add waitForDeployment() with default timeout 60s
Transaction tl-b populates now hash field with in_msg.hash - does not coincide with rawTransaction hash though (todo);
to all contracts added method sendWithConfirmation();
  • Loading branch information
neodix42 committed Dec 6, 2024
1 parent fe4e074 commit a81244d
Show file tree
Hide file tree
Showing 17 changed files with 559 additions and 314 deletions.
263 changes: 136 additions & 127 deletions cell/src/main/java/org/ton/java/tlb/types/Transaction.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.ton.java.tlb.types;

import static java.util.Objects.nonNull;

import java.math.BigInteger;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -8,12 +11,11 @@
import org.ton.java.cell.CellBuilder;
import org.ton.java.cell.CellSlice;
import org.ton.java.cell.TonHashMapE;

import java.math.BigInteger;

import static java.util.Objects.nonNull;
import org.ton.java.utils.Utils;

/**
*
*
* <pre>
* transaction$0111
* account_addr:bits256
Expand All @@ -37,135 +39,142 @@
@Data
@Slf4j
public class Transaction {
int magic;
BigInteger accountAddr;
BigInteger lt;
BigInteger prevTxHash;
BigInteger prevTxLt;
long now;
long outMsgCount;
AccountStates origStatus;
AccountStates endStatus;
TransactionIO inOut;
CurrencyCollection totalFees;
HashUpdate stateUpdate;
TransactionDescription description;

// not in scheme, but might be filled based on request data for flexibility
byte[] hash;

private String getMagic() {
return Long.toBinaryString(magic);
}

public String getAccountAddrShort() {
if (nonNull(accountAddr)) {
String str64 = StringUtils.leftPad(accountAddr.toString(16), 64, "0");
return str64.substring(0, 5)
+ "..."
+ str64.substring(str64.length() - 5);
} else {
return "N/A";
}
}

private String getPrevTxHash() {
if (nonNull(accountAddr)) {
return prevTxHash.toString(16);
} else {
return "null";
}
int magic;
BigInteger accountAddr;
BigInteger lt;
BigInteger prevTxHash;
BigInteger prevTxLt;
long now;
long outMsgCount;
AccountStates origStatus;
AccountStates endStatus;
TransactionIO inOut;
CurrencyCollection totalFees;
HashUpdate stateUpdate;
TransactionDescription description;

String hash; // not in tl-b scheme. equals to in_msg.hash in base64 - TODO testing

private String getMagic() {
return Long.toBinaryString(magic);
}

public String getAccountAddrShort() {
if (nonNull(accountAddr)) {
String str64 = StringUtils.leftPad(accountAddr.toString(16), 64, "0");
return str64.substring(0, 5) + "..." + str64.substring(str64.length() - 5);
} else {
return "N/A";
}
}

public Cell toCell() {
CellBuilder c = CellBuilder.beginCell();
c.storeUint(0b0111, 4);
c.storeUint(accountAddr, 256);
c.storeUint(lt, 64);
c.storeUint(prevTxHash, 256);
c.storeUint(prevTxLt, 64);
c.storeUint(now, 32);
c.storeUint(outMsgCount, 15);
c.storeCell(serializeAccountState(origStatus));
c.storeCell(serializeAccountState(endStatus));
c.storeCell(totalFees.toCell());

c.storeRef(inOut.toCell());
c.storeRef(stateUpdate.toCell());
c.storeRef(description.toCell());

return c.endCell();
private String getPrevTxHash() {
if (nonNull(accountAddr)) {
return prevTxHash.toString(16);
} else {
return "null";
}

public static Transaction deserialize(CellSlice cs) {
long magic = cs.loadUint(4).intValue();
assert (magic == 0b0111)
: "Transaction: magic not equal to 0b0111, found 0b" + Long.toBinaryString(magic);

Transaction tx =
Transaction.builder()
.magic(0b0111)
.accountAddr(cs.loadUint(256))
.lt(cs.loadUint(64))
.prevTxHash(cs.loadUint(256))
.prevTxLt(cs.loadUint(64))
.now(cs.loadUint(32).longValue())
.outMsgCount(cs.loadUint(15).intValue())
.origStatus(deserializeAccountState(cs.loadUint(2).byteValue()))
.endStatus(deserializeAccountState(cs.loadUint(2).byteValueExact()))
.build();

CellSlice inOutMsgs = CellSlice.beginParse(cs.loadRef());
Message msg =
inOutMsgs.loadBit() ? Message.deserialize(CellSlice.beginParse(inOutMsgs.loadRef())) : null;
TonHashMapE out =
inOutMsgs.loadDictE(
15,
k -> k.readInt(15),
v -> Message.deserialize(CellSlice.beginParse(CellSlice.beginParse(v).loadRef())));

tx.setInOut(TransactionIO.builder().in(msg).out(out).build());

tx.setTotalFees(CurrencyCollection.deserialize(cs));
tx.setStateUpdate(HashUpdate.deserialize(CellSlice.beginParse(cs.loadRef())));
tx.setDescription(TransactionDescription.deserialize(CellSlice.beginParse(cs.loadRef())));

return tx;
}

public Cell toCell() {
CellBuilder c = CellBuilder.beginCell();
c.storeUint(0b0111, 4);
c.storeUint(accountAddr, 256);
c.storeUint(lt, 64);
c.storeUint(prevTxHash, 256);
c.storeUint(prevTxLt, 64);
c.storeUint(now, 32);
c.storeUint(outMsgCount, 15);
c.storeCell(serializeAccountState(origStatus));
c.storeCell(serializeAccountState(endStatus));
c.storeCell(totalFees.toCell());

c.storeRef(inOut.toCell());
c.storeRef(stateUpdate.toCell());
c.storeRef(description.toCell());

return c.endCell();
}

public static Transaction deserialize(CellSlice cs) {
long magic = cs.loadUint(4).intValue();
assert (magic == 0b0111)
: "Transaction: magic not equal to 0b0111, found 0b" + Long.toBinaryString(magic);

Transaction tx =
Transaction.builder()
.magic(0b0111)
.accountAddr(cs.loadUint(256))
.lt(cs.loadUint(64))
.prevTxHash(cs.loadUint(256))
.prevTxLt(cs.loadUint(64))
.now(cs.loadUint(32).longValue())
.outMsgCount(cs.loadUint(15).intValue())
.origStatus(deserializeAccountState(cs.loadUint(2).byteValue()))
.endStatus(deserializeAccountState(cs.loadUint(2).byteValueExact()))
.build();

CellSlice inOutMsgs = CellSlice.beginParse(cs.loadRef());
Message msg =
inOutMsgs.loadBit() ? Message.deserialize(CellSlice.beginParse(inOutMsgs.loadRef())) : null;
TonHashMapE out =
inOutMsgs.loadDictE(
15,
k -> k.readInt(15),
v -> Message.deserialize(CellSlice.beginParse(CellSlice.beginParse(v).loadRef())));

tx.setInOut(TransactionIO.builder().in(msg).out(out).build());

tx.setTotalFees(CurrencyCollection.deserialize(cs));
tx.setStateUpdate(HashUpdate.deserialize(CellSlice.beginParse(cs.loadRef())));
tx.setDescription(TransactionDescription.deserialize(CellSlice.beginParse(cs.loadRef())));
if (nonNull(msg)) {
tx.setHash(Utils.bytesToBase64(msg.toCell().getHash()));
}

public static Cell serializeAccountState(AccountStates state) {
switch (state) {
case UNINIT: {
return CellBuilder.beginCell().storeUint(0, 2).endCell();
}
case FROZEN: {
return CellBuilder.beginCell().storeUint(1, 2).endCell();
}
case ACTIVE: {
return CellBuilder.beginCell().storeUint(2, 2).endCell();
}
case NON_EXIST: {
return CellBuilder.beginCell().storeUint(3, 2).endCell();
}
return tx;
}

public static Cell serializeAccountState(AccountStates state) {
switch (state) {
case UNINIT:
{
return CellBuilder.beginCell().storeUint(0, 2).endCell();
}
case FROZEN:
{
return CellBuilder.beginCell().storeUint(1, 2).endCell();
}
case ACTIVE:
{
return CellBuilder.beginCell().storeUint(2, 2).endCell();
}
case NON_EXIST:
{
return CellBuilder.beginCell().storeUint(3, 2).endCell();
}
return null;
}

public static AccountStates deserializeAccountState(byte state) {
switch (state) {
case 0: {
return AccountStates.UNINIT;
}
case 1: {
return AccountStates.FROZEN;
}
case 2: {
return AccountStates.ACTIVE;
}
case 3: {
return AccountStates.NON_EXIST;
}
return null;
}

public static AccountStates deserializeAccountState(byte state) {
switch (state) {
case 0:
{
return AccountStates.UNINIT;
}
case 1:
{
return AccountStates.FROZEN;
}
case 2:
{
return AccountStates.ACTIVE;
}
case 3:
{
return AccountStates.NON_EXIST;
}
return null;
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@
import org.ton.java.address.Address;
import org.ton.java.cell.Cell;
import org.ton.java.cell.CellBuilder;
import org.ton.java.smartcontract.types.Destination;
import org.ton.java.smartcontract.types.HighloadV3Config;
import org.ton.java.smartcontract.types.HighloadV3InternalMessageBody;
import org.ton.java.smartcontract.types.WalletCodes;
import org.ton.java.smartcontract.types.*;
import org.ton.java.smartcontract.wallet.Contract;
import org.ton.java.tlb.types.*;
import org.ton.java.tonlib.Tonlib;
import org.ton.java.tonlib.types.ExtMessageInfo;
import org.ton.java.tonlib.types.RawTransaction;
import org.ton.java.tonlib.types.RunResult;
import org.ton.java.tonlib.types.TvmStackEntryNumber;
import org.ton.java.utils.Utils;
Expand Down Expand Up @@ -182,6 +180,35 @@ public ExtMessageInfo send(HighloadV3Config highloadConfig) {
return tonlib.sendRawMessage(externalMessage.toCell().toBase64());
}

/**
* Sends amount of nano toncoins to destination address and waits till message found among
* account's transactions
*/
public RawTransaction sendWithConfirmation(HighloadV3Config highloadConfig) {
Address ownAddress = getAddress();

Cell body = createTransferMessage(highloadConfig);

Message externalMessage =
Message.builder()
.info(
ExternalMessageInInfo.builder()
.dstAddr(
MsgAddressIntStd.builder()
.workchainId(ownAddress.wc)
.address(ownAddress.toBigInteger())
.build())
.build())
.body(
CellBuilder.beginCell()
.storeBytes(
Utils.signData(keyPair.getPublicKey(), keyPair.getSecretKey(), body.hash()))
.storeRef(body)
.endCell())
.build();
return tonlib.sendRawMessageWithConfirmation(externalMessage.toCell().toBase64(), getAddress());
}

public ExtMessageInfo deploy(HighloadV3Config highloadConfig) {
Address ownAddress = getAddress();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.ton.java.tlb.types.*;
import org.ton.java.tonlib.Tonlib;
import org.ton.java.tonlib.types.ExtMessageInfo;
import org.ton.java.tonlib.types.RawTransaction;
import org.ton.java.tonlib.types.RunResult;
import org.ton.java.tonlib.types.TvmStackEntryNumber;
import org.ton.java.utils.Utils;
Expand Down Expand Up @@ -305,4 +306,24 @@ public ExtMessageInfo send(LockupWalletV1Config config) {

return tonlib.sendRawMessage(externalMessage.toCell().toBase64());
}

/**
* Sends amount of nano toncoins to destination address and waits till message found among
* account's transactions
*/
public RawTransaction sendWithConfirmation(LockupWalletV1Config config) {
Cell body = createTransferBody(config);

Message externalMessage =
Message.builder()
.info(ExternalMessageInInfo.builder().dstAddr(getAddressIntStd()).build())
.body(
CellBuilder.beginCell()
.storeBytes(
Utils.signData(keyPair.getPublicKey(), keyPair.getSecretKey(), body.hash()))
.storeCell(body)
.endCell())
.build();
return tonlib.sendRawMessageWithConfirmation(externalMessage.toCell().toBase64(), getAddress());
}
}
Loading

0 comments on commit a81244d

Please sign in to comment.