trufflesuite/truffle-logger-example

[RFC] console.log for Solidity

adrianmcli opened this issue ยท 20 comments

This is a request for your comments and feedback. Let us know if you would like to see this integrated into Truffle

@CruzMolina and I built a prototype to introduce console.log/printf/print functionality to Solidity smart contracts within Truffle projects.

A prototype version of the Console.sol contract is here.

Check out the README for how to try this out. It's very simple and it won't take you more than a couple minutes. Let us know what you think and thanks for taking your time!

Hey all! A few questions:

  1. What is the Console.sol contract? Does logging functionality have side effects (i.e. mess with memory or the stack at all)?

  2. What types are allowed in the log function?

  3. If I have a gas-constrained contract or function, will adding log statements add bytecode or change gas costs for execution?

Hi @wadealexc ! Thanks for taking the time to inquire!

Regarding questions 1 & 2, you can find a prototype version of the Console.sol contract here. To my knowledge, the current setup of the contract in the prerelease only deploys & links the library contract when importing it (README). Events are fired off with each log call so I don't believe there are any unintended side effects.

Regarding Q3, adding a log statement to a contract will definitely add bytecode to your contract and up gas costs when calling a contract method that makes a log call. This console.log functionality is mainly intended to benefit devs during local development.

Keep in mind that this is all just an experimental feature at this point. Before we push this to the main develop branch, we'd probably try to put in safeguards to prevent stray Console.log statements in production.

That can mean one or more of several options (other suggestions are welcome):

  1. Doing a check for importation of Console.sol during migrations/deployment, and asking the user if they really want to proceed.

  2. Requiring a specific flag for linking of the Console.sol library, thereby breaking normal migrations if you have Console.log statements but you don't run with a --show-log-statements flag.

  3. Other options we haven't thought of yet.

For those that are interested in how this works, see the Console.sol file here.

Feedback from Reddit

Does this work with failed transactions?

It is based on events which are only emitted when transactions successed [sic]. Question: does this work even when transactions fail?

Source

No, unfortunately it cannot work with failed transactions (because it is based on the event system). We should probably list out the things that you cannot do with this feature to temper expectations. That being said, we could use the Truffle decoder/debugger (which uses debug_traceTransaction) to do this, but it would take a lot of bandwidth that we do not currently have.

Action items:

  • Make list of limitations to temper expectations
  • Put v2 of this feature on roadmap to support this using the Truffle decoder/debugger

Does this add something non-standard to the language?

Mixed feelings. Adding non-standard junk to the language for outputting debug streams rubs me a little wrong. That said, debugging solidity is a PITA so, maybe it'll help...

Source

No. This is all standard Solidity actually. It makes use of a Solidity library that Truffle will link under the hood. But you actually have to make the import statement at the top of your contract file to use this.

Action items:

  • Clarify that this feature is just plain Solidity when introducing/explaining how it works.

One helpful addition could be a logNamed set of events that takes a string or bytes32 parameter as well as the value to log. Similar to ds-test logs: https://github.com/dapphub/ds-test/blob/a4e40050b809705b15867939f5829540c50cb84f/src/test.sol#L20-L25

Console.logNamed("my value", val)

@nsward thanks for the idea!

I've added the feature, the following should now work as well:

Console.log("This is myBool", myBool);
Console.log("This is myInt", myInt);
Console.log("This is myUint", myUint);
Console.log("This is myString", myString);
Console.log("This is myBytes32", myBytes32);
Console.log("This is myAddress", myAddress);

And you can still just use Console.log thanks to overloaded types!

๐Ÿ‘ I like this!

That can mean one or more of several options (other suggestions are welcome):

  1. Doing a check for importation of Console.sol during migrations/deployment, and asking the user if they really want to proceed.
  2. Requiring a specific flag for linking of the Console.sol library, thereby breaking normal migrations if you have Console.log statements but you don't run with a --show-log-statements flag.
  3. Other options we haven't thought of yet.

If we want to be really strict about it we could use EIP1344(chainID opcode) to make Console.sol throw on the main chain. But that's probably too harsh, I'm sure there will be occasions you want to use this feature also on main chain...

So, to expand on the "solve this with @truffle/debugger" approach that @adrianmcli mentioned:

That being said, we could use the Truffle decoder/debugger (which uses debug_traceTransaction) to do this, but it would take a lot of bandwidth that we do not currently have.

This approach is more costly, both for performance concerns (may be solvable) and for engineering time concerns, but it could categorically address these robustness issues (e.g. scrubbing logs for mainnet). For this reason, it might be worth outlining the approach here:

Essentially, instead of listening to each transaction and the events it emits, Truffle could listen to each transaction and obtain log information by loading the transaction into @truffle/debugger, and stepping through the debugger in the background to identify log statements and argument values.

This approach allows certain benefits:

  • It can work with calls / view methods (not just transactions). Although the debugger library requires a transaction hash to start, the in-test debugging feature solves this limitation currently by replaying calls as transactions.

    There are additional considerations here, and some limitations (in-test debugging currently has bugs with failed transactions), but the general framework is there for overcoming this problem.

  • Once we can properly support failed transactions/calls with in-test debugging, we'd be able to support logging in failed transactions by the same means.

  • It would not require a dedicated library, and there'd be far fewer limitations on what syntax this feature could use. Since the debugger can interpret the code as it steps, it can just look for whatever syntax design is best. Heck, this could even be inside some special kind of comment! For example:

    x = 0;
    y = n / 2;
    for (i = 1; i <= n * n; i++) {
      //- console.debug("x: %i", x)
      //- console.debug("y: %i", y)
      (x, y, i) = square.step(x, y, i);
    }
    //- console.debug("result %o", square)

    (We could even just run those comment statements in the context of a Node VM and actually invoke console.debug for each of those.)

  • Putting log statements in comments would mean no gas costs, since comments are free!

Now, of course, downsides:

  • The debugger takes a long time to start up, mostly because it requires a dedicated truffle compile --all. This is something that's in the process of being remedied, eventually to be fixed completely.

  • Beyond required compilation slowness, the debugger is perhaps not performant enough at all. This is unknown. There might be a way to strip out unnecessary logic and run the debugger in a "console.log-only mode", but that'd require doing.

  • The bugs around failed transactions not working for in-test debugging means that it's currently not very straightforward to take that code and turn it into a complete solution all at once. This may not be a downside, since the current event-based solution doesn't offer support for failure conditions, anyway.

  • If we used the in-test debugging implementation at all, we'd have to expand it to support truffle console, etc., and not just truffle test.

Personally, I love the idea of this approach as a long-term direction for this new feature! I don't know if it's feasible... but hey, wouldn't that be neat? ๐Ÿ˜„ (I'm probably biased tho)

I'm having troubles getting this working on some truffle tests. If I:

  1. import "truffle/Console.sol";
  2. npx truffle@truffleLogger develop
  3. truffle(develop)> test
    Then I get strange compilation errors that I don't get when running truffle test.

When I run truffle test is says it can't find truffle/Console.sol.
There doesn't seem to be anything I can install so that my truffle test command just works with the logging.

In another project I ended up migrating all my test cases over to hardhat just to use the console.log functionality but I can't do that with this project.

Thanks..

Same issue as scotb1978, any solutions?

same

I get:

ParserError: Source "truffle/Console.sol" not found: File import callback not supported
import "truffle/Console.sol";

whether I run tests from develop console or whether I use truffle test.

I'm also getting this error
Source "truffle/Console.sol" not found: File import callback not supported

Bump. I'm seeing the same issue.

There is no more https://github.com/trufflesuite/truffle/blob/truffleLogger/packages/core/lib/logging/Console.sol
in repo so it cant work, not sure why project got deleted or moved and no one was informed. Maybe they just want us to move to hardhat :(

I just completed this video that shows me the easy way to achieve this with Hardhart.
https://youtu.be/9Qpi80dQsGU

Any update about this feature?

Any update about this feature?

Really need this feature.