call_flags.sol 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. contract CallFlags {
  2. uint8 roundtrips;
  3. // See https://github.com/paritytech/substrate/blob/5ea6d95309aaccfa399c5f72e5a14a4b7c6c4ca1/frame/contracts/src/wasm/runtime.rs#L373
  4. enum CallFlag { FORWARD_INPUT, CLONE_INPUT, TAIL_CALL, ALLOW_REENTRY }
  5. function bitflags(CallFlag[] _flags) internal pure returns (uint32 flags) {
  6. for (uint n = 0; n < _flags.length; n++) {
  7. flags |= (2 ** uint32(_flags[n]));
  8. }
  9. }
  10. // Reentrancy is required for reaching the `foo` function for itself.
  11. //
  12. // Cloning and forwarding should have the effect of calling this function again, regardless of what _address was passed.
  13. // Furthermore:
  14. // Cloning the input should work together with reentrancy.
  15. // Forwarding the input should fail due to reading the input more than once in the loop
  16. // Tail call should work with any combination of input forwarding.
  17. function echo(
  18. address _address,
  19. bytes4 _selector,
  20. uint32 _x,
  21. CallFlag[] _flags
  22. ) public payable returns(uint32 ret) {
  23. for (uint n = 0; n < 2; n++) {
  24. if (roundtrips > 1) {
  25. return _x;
  26. }
  27. roundtrips += 1;
  28. bytes input = abi.encode(_selector, _x);
  29. (bool ok, bytes raw) = _address.call{flags: bitflags(_flags)}(input);
  30. require(ok);
  31. ret = abi.decode(raw, (uint32));
  32. roundtrips -= 1;
  33. }
  34. }
  35. @selector([0,0,0,0])
  36. function foo(uint32 x) public pure returns(uint32) {
  37. return x;
  38. }
  39. // Yields different result for tail calls
  40. function tail_call_it(
  41. address _address,
  42. bytes4 _selector,
  43. uint32 _x,
  44. CallFlag[] _flags
  45. ) public returns(uint32 ret) {
  46. bytes input = abi.encode(_selector, _x);
  47. (bool ok, bytes raw) = _address.call{flags: bitflags(_flags)}(input);
  48. require(ok);
  49. ret = abi.decode(raw, (uint32));
  50. ret += 1;
  51. }
  52. // Does this.call() on this instead of address.call()
  53. function call_this(uint32 _x) public view returns (uint32) {
  54. return this.foo{flags: bitflags([CallFlag.ALLOW_REENTRY])}(_x);
  55. }
  56. }