toncenter / tonweb

JavaScript SDK for TON (The Open Network)
MIT License
432 stars 107 forks source link

Invalid BOC is generated for some transfer transactions #115

Open mcnckc opened 9 months ago

mcnckc commented 9 months ago

With a certain amount of payload, the generated BOC for transfer becomes invalid. For example, for any destination address, for value of 1 TON, seqno equal to 1, it happens when the payload is exactly 72 bytes. I checked for wallets v3R2 and v4R2.

To see this, you need to create such transaction (even for an uninit address), and send it to liteservers directly, bypassing toncenter, because unfortunately it doesn't provide a full error description.

I made a simple HTML page, which allows to create and download a .boc file for such a transaction.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <a id="download-boc-link" download="transaction.boc" href="#">Download transaction file</a>
    </body>
    <script src="tonweb.js"></script> //distribution versions
    <script src="tonweb-mnemonic.js"></script> //distribution versions
    <script>
        const tonweb = new window.TonWeb();
        const tonMnemonic = window.TonWeb.mnemonic;
        const DEFAULT_WALLET_VERSION = 'v3R2';
        const WalletClass = tonweb.wallet.all[DEFAULT_WALLET_VERSION];

        async function main() {
            const mnemonic = await tonMnemonic.generateMnemonic();
            const keyPair = await tonMnemonic.mnemonicToKeyPair(mnemonic);
            const wallet = new WalletClass(undefined, {publicKey: keyPair.publicKey, wc: 0});
            const transfer = wallet.methods.transfer({
                secretKey: keyPair.secretKey,
                toAddress: "EQA5yt9vvjJug_i5Ne4U5uxWnGaZuTd-McmsLuneJaZxwCmV", //random address
                amount: 1000000000, //nanoTON
                seqno: 1,
                payload: "A".repeat(72)
            });
            const query = await transfer.getQuery();
            const boc = await query.toBoc(false);
            document.getElementById('download-boc-link').href = 'data:application/octet-stream;base64,' 
            + tonweb.utils.bytesToBase64(boc);
        }
        main();
    </script>
</html>

After, I'm sending transaction.boc, with liteclient binary, taken from ton site, as follows (windows syntax): lite-client -C global-config.json -c "sendfile transaction.boc"

Of course, transaction is rejected, but note the error message from liteserver: [lite-client.cpp:153][!testnode] liteserver error: [Error : 0 : cannot apply external message to current state : failed to parse external message invalid bag-of-cells last cell #1: end offset 117 is different from total data size 245]

That is not the message we get, when sending the same transaction with 71 byte payload, and it clearly says that .boc sent is invalid. I also tried sending with ton-kotlin liteclient, and the result was the same. Moreover, trying to parse this BOC's bytes with ton-kotlin also fails, because the root cell has only one ref, which is empty.

Increasing payload to 73 and more also gives strange results - sending stops giving invalid BOC error, but trying to parse reveals empty payload.

Note, that tonweb doesn't allow large payloads, for specified amount and seqno, it throws an exception, when payload is larger than around 124 bytes, and this is ok. But with 72 bytes, no exception is thrown, and there is now way for developer to know, that such BOC is actually invalid. Also note that 72 bytes payload means that you need only 36 cyrillic letters, of even 24 for some asian characters, which is a pretty reasonable comment length.

Unfortunately, I couldn't find source of this issue, but it seems to be somewhere in Cell.toBoc()