TVM-Solidity-Compiler icon indicating copy to clipboard operation
TVM-Solidity-Compiler copied to clipboard

Proposal: add warning in case of using msg.pubkey() without AbiHeader pubkey

Open unboxedtype opened this issue 3 years ago • 2 comments

Hi!

Today, I spent several hours trying to find out why the message I sign with some key fail to pass the signature check in my contract, until found the reason, stated in the API documentation:

"Returns sender's public key, obtained from the body of the external inbound message. If the message is not signed, msg.pubkey() returns 0. If the message is signed and message header (pragma AbiHeader) does not contain pubkey then msg.pubkey() is equal to tvm.pubkey()."

As for me, this behavior feels a bit counter intuitive. Maybe it would be better to show a warning in case of using the msg.pubkey() without pragma AbiHeader pubkey?

unboxedtype avatar Aug 12 '21 14:08 unboxedtype

Could you explain in detail what the problem was? Or could you show the wrong/correct code?

IgorKoval avatar Oct 12 '21 22:10 IgorKoval

Compiler 0.57.3, Linker 0.14.37, SE 0.29.0

To test this behavior, I use the code src/App.sol:

pragma ton-solidity >= 0.57.3;
pragma AbiHeader time;
pragma AbiHeader pubkey;
pragma AbiHeader expire;
pragma msgValue 42.001 ever;

contract App {
    constructor() onlyOwner messageLog("constructor") public { stash = 42; }
    uint public stash;
    function put() accept internalMsg messageLog("put") external { stash += 42; }
    function make() onlyOwner externalMsg messageLog("make") external { App(address(this)).put(); }
    modifier onlyOwner {
        require(tvm.pubkey() != 0, 101);
        require(msg.pubkey() == tvm.pubkey(), 102);
        tvm.accept();
        _;
    }
    modifier accept {
        tvm.accept();
        _;
    }
    modifier messageLog(string method) {
        tvm.log(format(
            "App::{} SI={} IM={} EM={} TT={} V={} DB={} At={} PK={:x}",
            method,
            msg.hasStateInit ? "Y" : "N",
            msg.isInternal ? "Y" : "N",
            msg.isExternal ? "Y" : "N",
            msg.isTickTock ? "Y" : "N",
            msg.value,
            msg.data.bits(),
            msg.createdAt,
            msg.pubkey()
        ));
        _;
    }
}

run.sh

#!/usr/bin/env bash

set -o errexit

npx everdev se reset
rm -fr build build.log

signer="$(npx everdev signer list | grep Default | cut -d' ' -f1)"
npx everdev signer info "${signer}" | jq .keys.public

# Deploy
npx everdev sol compile --code --output-dir build src/App.sol
npx everdev contract deploy --value 1000000000000 build/App

# Interact
npx everdev contract run build/App make
npx everdev contract run-local build/App stash
docker exec "$(npx everdev se info | tail -n +3 | cut -d' ' -f21)" cat /ton-node/log/tvm.log | grep App

The logs show that the public key is transmitted on the external message and is not transmitted on the internal:

App::constructor SI=Y IM=N EM=Y TT=N V=0 DB=898 At=0 PK=deb9e...
App::make SI=N IM=N EM=Y TT=N V=0 DB=898 At=0 PK=deb9e...
App::put SI=N IM=Y EM=N TT=N V=42000000000 DB=32 At=1644909519 PK=0

If you remove all headers, then the behavior changes only formsg.value

App::constructor SI=Y IM=N EM=Y TT=N V=0 DB=609 At=0 PK=deb9e...
App::make SI=N IM=N EM=Y TT=N V=0 DB=609 At=0 PK=deb9e...
App::put SI=N IM=Y EM=N TT=N V=9000000 DB=32 At=1644909342 PK=0

it turns out that msg.pubkey() is filled only for external message regardless of pragma AbiHeader pubkey

ilyar avatar Feb 15 '22 07:02 ilyar